From 2c0b2e11af80fb5418f11a4519b651f59590e2cd Mon Sep 17 00:00:00 2001 From: Marcelo Chiaradia Date: Fri, 8 Jan 2021 16:20:10 +0100 Subject: [PATCH 1/3] #1: Migrate MySQL virtual schema --- .github/workflows/dependencies_check.yml | 25 + .github/workflows/github_release.yml | 36 + .gitignore | 28 + .settings/org.eclipse.core.resources.prefs | 6 + .settings/org.eclipse.jdt.core.prefs | 459 ++++++++++++ .settings/org.eclipse.jdt.ui.prefs | 127 ++++ .settings/org.eclipse.m2e.core.prefs | 4 + .travis.yml | 23 + README.md | 108 +++ doc/changes/changelog.md | 3 + doc/changes/changes_1.0.0.md | 7 + doc/dialects/mysql_user_guide.md | 144 ++++ pom.xml | 316 +++++++++ src/assembly/all-dependencies.xml | 22 + .../mysql/MySQLColumnMetadataReader.java | 74 ++ .../dialects/mysql/MySQLMetadataReader.java | 45 ++ .../dialects/mysql/MySQLSqlDialect.java | 138 ++++ .../mysql/MySQLSqlDialectFactory.java | 29 + .../mysql/MySQLSqlGenerationVisitor.java | 31 + ....exasol.adapter.dialects.SqlDialectFactory | 1 + .../mysql/IntegrationTestConstants.java | 20 + .../mysql/MySQLColumnMetadataReaderTest.java | 51 ++ .../mysql/MySQLMetadataReaderTest.java | 54 ++ .../mysql/MySQLSqlDialectFactoryTest.java | 30 + .../dialects/mysql/MySQLSqlDialectIT.java | 667 ++++++++++++++++++ .../dialects/mysql/MySQLSqlDialectTest.java | 155 ++++ .../mysql/MySQLSqlGenerationVisitorTest.java | 42 ++ .../integration/driver/mysql/mysql.properties | 2 + .../integration/driver/mysql/settings.cfg | 7 + src/test/resources/logging.properties | 6 + versionsMavenPluginRules.xml | 18 + 31 files changed, 2678 insertions(+) create mode 100644 .github/workflows/dependencies_check.yml create mode 100644 .github/workflows/github_release.yml create mode 100644 .gitignore create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.jdt.ui.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 .travis.yml create mode 100644 README.md create mode 100644 doc/changes/changelog.md create mode 100644 doc/changes/changes_1.0.0.md create mode 100644 doc/dialects/mysql_user_guide.md create mode 100644 pom.xml create mode 100644 src/assembly/all-dependencies.xml create mode 100644 src/main/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReader.java create mode 100644 src/main/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReader.java create mode 100644 src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialect.java create mode 100644 src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactory.java create mode 100644 src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitor.java create mode 100644 src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/IntegrationTestConstants.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReaderTest.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReaderTest.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactoryTest.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectIT.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectTest.java create mode 100644 src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitorTest.java create mode 100644 src/test/resources/integration/driver/mysql/mysql.properties create mode 100644 src/test/resources/integration/driver/mysql/settings.cfg create mode 100644 src/test/resources/logging.properties create mode 100644 versionsMavenPluginRules.xml diff --git a/.github/workflows/dependencies_check.yml b/.github/workflows/dependencies_check.yml new file mode 100644 index 0000000..2cb0303 --- /dev/null +++ b/.github/workflows/dependencies_check.yml @@ -0,0 +1,25 @@ +name: Dependencies Check + +on: + schedule: + - cron: "0 2 * * *" + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Checking dependencies for vulnerabilities + run: mvn org.sonatype.ossindex.maven:ossindex-maven-plugin:audit -f pom.xml \ No newline at end of file diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml new file mode 100644 index 0000000..3afc904 --- /dev/null +++ b/.github/workflows/github_release.yml @@ -0,0 +1,36 @@ +name: GitHub Release + +on: + workflow_dispatch: + inputs: + upload_url: + description: 'Upload URL' + required: true + asset_name: + description: 'Asset file name' + required: true + asset_path: + description: 'Asset file path' + required: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Build with Maven + run: mvn -B clean package --file pom.xml + + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.inputs.upload_url }} + asset_path: ${{ github.event.inputs.asset_path }} + asset_name: ${{ github.event.inputs.asset_name }} + asset_content_type: application/java-archive \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..299e343 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +/target/ +pom.xml.versionsBackup + +# Eclipse and Maven +.classpath +.project +# .settings : we need Eclipse settings for code formatter and clean-up rules +target +.cache +dependency-reduced-pom.xml + +# Intellij +.idea +# Intellij recommends to share iml files, however, better don't share files which might be outdated +*.iml + +# Integration tests +src/test/resources/integration/driver/mysql/*.jar + +# Others +.DS_Store +*.swp +local +Scripts +.dbeaver* +**/*.log +.directory +venv/ diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..29abf99 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,6 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/main/resources=UTF-8 +encoding//src/test/java=UTF-8 +encoding//src/test/resources=UTF-8 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..e3a8580 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,459 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +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=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=11 +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.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=true +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..e7e7880 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,127 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=true +cleanup.convert_to_enhanced_for_loop=true +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=true +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=true +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=false +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_modifiers=false +cleanup.remove_redundant_semicolons=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=true +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=true +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Exasol +cleanup_settings_version=2 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Exasol +formatter_settings_version=16 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.ondemandthreshold=3 +org.eclipse.jdt.ui.staticondemandthreshold=3 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=true +sp_cleanup.always_use_this_for_non_static_field_access=true +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=true +sp_cleanup.convert_to_enhanced_for_loop=true +sp_cleanup.correct_indentation=true +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=true +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=false +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=true +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_modifiers=false +sp_cleanup.remove_redundant_semicolons=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=true +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=true +sp_cleanup.use_this_for_non_static_field_access=true +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=false +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 0000000..f897a7f --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..57aa0b0 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,23 @@ +language: java + +# Setting sudo to false will cause Travis to use Containers. +# To use Docker's privileged mode, we need to enable sudo. +sudo: required + +matrix: + include: + - jdk: openjdk11 + +addons: + sonarcloud: + organization: exasol + +install: + - travis_retry mvn dependency:resolve + +script: + - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent org.jacoco:jacoco-maven-plugin:prepare-agent-integration verify sonar:sonar -Dsonar.login=${SONAR_TOKEN} + +cache: + directories: + - "$HOME/.m2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..ce55792 --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# MySQL Virtual Schema + +[![Build Status](https://api.travis-ci.com/exasol/mysql-virtual-schema.svg?branch=main)](https://travis-ci.com/exasol/mysql-virtual-schema) +[![Maven Central](https://img.shields.io/maven-central/v/com.exasol/mysql-virtual-schema)](https://search.maven.org/artifact/com.exasol/mysql-virtual-schema) + +SonarCloud results: + +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=alert_status)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) + +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=security_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=sqale_index)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) + +[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=code_smells)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=coverage)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=duplicated_lines_density)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=com.exasol%3Amysql-virtual-schema&metric=ncloc)](https://sonarcloud.io/dashboard?id=com.exasol%3Amysql-virtual-schema) + +# Overview + +The **MySQL Virtual Schema** provides an abstraction layer that makes an external [MySQL](https://www.mysql.com/) database accessible from an Exasol database through regular SQL commands. The contents of the external MySQL database are mapped to virtual tables which look like and can be queried as any regular Exasol table. + +If you want to set up a Virtual Schema for a different database system, please head over to the [Virtual Schemas Repository][virtual-schemas]. + +## Features + +* Access a MySQL data source in read only mode from an Exasol database, using a Virtual Schema. + +## Table of Contents + +### Information for Users + +* [Virtual Schemas User Guide][user-guide] +* [MySQL Dialect User Guide](doc/dialects/mysql_user_guide.md) +* [Changelog](doc/changes/changelog.md) + +Find all the documentation in the [Virtual Schemas project][vs-doc]. + +## Information for Developers + +* [Virtual Schema API Documentation][vs-api] + +### Run Time Dependencies + +Running the Virtual Schema requires a Java Runtime version 11 or later. + +| Dependency | Purpose | License | +|--------------------------------------------------------------------|--------------------------------------------------------|-------------------------------| +| [Exasol Virtual Schema JDBC][virtual-schema-common-jdbc] | Common JDBC functions for Virtual Schemas adapters | MIT License | +| [MySQL JDBC Driver][mysql-jdbc-driver] | JDBC driver for MySQL database | Elastic License | +| [Exasol Error Reporting][exasol-error-reporting] | Creating unified error messages | MIT License | + +### Test Dependencies + +| Dependency | Purpose | License | +|--------------------------------------------------------------------|--------------------------------------------------------|-------------------------------| +| [Java Hamcrest](http://hamcrest.org/JavaHamcrest/) | Checking for conditions in code via matchers | BSD License | +| [JUnit](https://junit.org/junit5) | Unit testing framework | Eclipse Public License 1.0 | +| [Mockito](http://site.mockito.org/) | Mocking framework | MIT License | +| [Testcontainers](https://www.testcontainers.org/) | Container-based integration tests | MIT License | +| [Exasol Testcontainers][exasol-testcontainers] | Exasol extension for the Testcontainers framework | MIT License | +| [Test Database Builder][test-db-builder] | Fluent database interfaces for testing | MIT License | +| [Exasol Hamcrest Result Set Matcher][exasol-hamcrest] | Hamcrest result set matcher for testing | MIT License | +| [Exasol UDF Debugging][udf-debugging-java] | Debugging UDFs in testing | MIT License | + + +### Maven Plug-ins + +| Plug-in | Purpose | License | +|--------------------------------------------------------------------|--------------------------------------------------------|-------------------------------| +| [Maven Jacoco Plugin][maven-jacoco-plugin] | Code coverage metering | Eclipse Public License 2.0 | +| [Maven Surefire Plugin][maven-surefire-plugin] | Unit testing | Apache License 2.0 | +| [Maven Compiler Plugin][maven-compiler-plugin] | Setting required Java version | Apache License 2.0 | +| [Maven Assembly Plugin][maven-assembly-plugin] | Creating JAR | Apache License 2.0 | +| [Maven Failsafe Plugin][maven-failsafe-plugin] | Integration testing | Apache License 2.0 | +| [Versions Maven Plugin][versions-maven-plugin] | Checking if dependencies updates are available | Apache License 2.0 | +| [Maven Enforcer Plugin][maven-enforcer-plugin] | Controlling environment constants | Apache License 2.0 | +| [Maven Dependency Plugin][maven-dependency-plugin] | Accessing to test dependencies | Apache License 2.0 | +| [Artifact Reference Checker Plugin][artifact-ref-checker-plugin] | Check if artifact is referenced with correct version | MIT License | +| [Project Keeper Maven Plugin][project-keeper-maven-plugin] | Checking project structure | MIT License | +| [Sonatype OSS Index Maven Plugin][sonatype-oss-index-maven-plugin] | Checking dependencies vulnerability | ASL2 | + +[virtual-schema-common-jdbc]: https://github.com/exasol/virtual-schema-common-jdbc +[mysql-jdbc-driver]: https://dev.mysql.com/downloads/connector/j/ +[exasol-error-reporting]: https://github.com/exasol/error-reporting-java/ + +[exasol-testcontainers]: https://github.com/exasol/exasol-testcontainers +[test-db-builder]: https://github.com/exasol/test-db-builder/ +[exasol-hamcrest]: https://github.com/exasol/hamcrest-resultset-matcher +[udf-debugging-java]: https://github.com/exasol/udf-debugging-java + +[maven-jacoco-plugin]: https://www.eclemma.org/jacoco/trunk/doc/maven.html +[maven-surefire-plugin]: https://maven.apache.org/surefire/maven-surefire-plugin/ +[maven-compiler-plugin]: https://maven.apache.org/plugins/maven-compiler-plugin/ +[maven-assembly-plugin]: https://maven.apache.org/plugins/maven-assembly-plugin/ +[maven-failsafe-plugin]: https://maven.apache.org/surefire/maven-failsafe-plugin/ +[versions-maven-plugin]: https://www.mojohaus.org/versions-maven-plugin/ +[maven-enforcer-plugin]: http://maven.apache.org/enforcer/maven-enforcer-plugin/ +[artifact-ref-checker-plugin]: https://github.com/exasol/artifact-reference-checker-maven-plugin +[maven-dependency-plugin]: https://maven.apache.org/plugins/maven-dependency-plugin/ +[project-keeper-maven-plugin]: https://github.com/exasol/project-keeper-maven-plugin +[sonatype-oss-index-maven-plugin]: https://sonatype.github.io/ossindex-maven/maven-plugin/ + +[user-guide]: https://docs.exasol.com/database_concepts/virtual_schemas.htm +[virtual-schemas]: https://github.com/exasol/virtual-schemas +[vs-api]: https://github.com/exasol/virtual-schema-common-java/blob/master/doc/development/api/virtual_schema_api.md +[vs-doc]: https://github.com/exasol/virtual-schemas/tree/master/doc diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md new file mode 100644 index 0000000..897f7fb --- /dev/null +++ b/doc/changes/changelog.md @@ -0,0 +1,3 @@ +# Changes + +* [1.0.0](changes_0.1.0.md) diff --git a/doc/changes/changes_1.0.0.md b/doc/changes/changes_1.0.0.md new file mode 100644 index 0000000..50f33ac --- /dev/null +++ b/doc/changes/changes_1.0.0.md @@ -0,0 +1,7 @@ +#Virtual Schema for MySQL 1.0.0, released 2021-??-?? + +Code name: + +## Features / Enhancements + +* ISSUE_NUMBER: description diff --git a/doc/dialects/mysql_user_guide.md b/doc/dialects/mysql_user_guide.md new file mode 100644 index 0000000..d2d00ff --- /dev/null +++ b/doc/dialects/mysql_user_guide.md @@ -0,0 +1,144 @@ +# MySQL Dialect User Guide + +[MySQL](https://www.mysql.com/) is an open-source relational database management system. + +## Registering the JDBC Driver in EXAOperation + +First download the [MySQL JDBC driver](https://dev.mysql.com/downloads/connector/j/). +Select Operating System -> Platform Independent -> Download. + +Now register the driver in EXAOperation: + +1. Click "Software" +1. Switch to tab "JDBC Drivers" +1. Click "Browse..." +1. Select JDBC driver file +1. Click "Upload" +1. Click "Add" +1. In a dialog "Add EXACluster JDBC driver" configure the JDBC driver (see below) + +You need to specify the following settings when adding the JDBC driver via EXAOperation. + +| Parameter | Value | +|-----------|-----------------------------------------------------| +| Name | `MYSQL` | +| Main | `com.mysql.jdbc.Driver` | +| Prefix | `jdbc:mysql:` | +| Files | `mysql-connector-java-.jar` | + +IMPORTANT: Currently you have to **Disable Security Manager** for the driver if you want to connect to MySQL using Virtual Schemas. +It is necessary because JDBC driver requires a JAVA permission which we do not grant by default. + +## Uploading the JDBC Driver to EXAOperation + +1. [Create a bucket in BucketFS](https://docs.exasol.com/administration/on-premise/bucketfs/create_new_bucket_in_bucketfs_service.htm) +1. Upload the driver to BucketFS + +This step is necessary since the UDF container the adapter runs in has no access to the JDBC drivers installed via EXAOperation but it can access BucketFS. + +## Installing the Adapter Script + +Upload the latest available release of [MySQL Virtual Schema](https://github.com/exasol/mysql-virtual-schema/releases) to Bucket FS. + +Then create a schema to hold the adapter script. + +```sql +CREATE SCHEMA SCHEMA_FOR_VS_SCRIPT; +``` + +The SQL statement below creates the adapter script, defines the Java class that serves as entry point and tells the UDF framework where to find the libraries (JAR files) for Virtual Schema and database driver. + +```sql +CREATE OR REPLACE JAVA ADAPTER SCRIPT SCHEMA_FOR_VS_SCRIPT.ADAPTER_SCRIPT_MYSQL AS + %scriptclass com.exasol.adapter.RequestDispatcher; + %jar /buckets///virtual-schema-dist-8.0.0-bundle-4.0.5.jar; + %jar /buckets///mysql-connector-java-.jar; +/ +; +``` + +## Defining a Named Connection + +Define the connection to MySQL as shown below. + +```sql +CREATE OR REPLACE CONNECTION MYSQL_JDBC_CONNECTION +TO 'jdbc:mysql://:/' +USER '' +IDENTIFIED BY ''; +``` + +## Creating a Virtual Schema + +Below you see how a MySQL Virtual Schema is created. Use CATALOG_NAME property to select a database. + +```sql +CREATE VIRTUAL SCHEMA + USING SCHEMA_FOR_VS_SCRIPT.ADAPTER_SCRIPT_MYSQL + WITH + SQL_DIALECT = 'MYSQL' + CONNECTION_NAME = 'MYSQL_JDBC_CONNECTION' + CATALOG_NAME = ''; +``` + +## Data Types Conversion + +MySQL Data Type | Supported | Converted Exasol Data Type| Known limitations +-------------------|-----------|---------------------------|------------------- +BOOLEAN | ✓ | BOOLEAN | +BIGINT | ✓ | DECIMAL | +BINARY | × | | +BIT | ✓ | BOOLEAN | +BLOB | × | | +CHAR | ✓ | CHAR | +DATE | ✓ | DATE | +DATETIME | ✓ | TIMESTAMP | +DECIMAL | ✓ | DECIMAL | +DOUBLE | ✓ | DOUBLE PRECISION | +ENUM | ✓ | CHAR | +FLOAT | ✓ | DOUBLE PRECISION | +INT | ✓ | DECIMAL | +LONGBLOB | × | | +LONGTEXT | ✓ | VARCHAR(2000000) | +MEDIUMBLOB | × | | +MEDIUMINT | ✓ | DECIMAL | +MEDIUMTEXT | ✓ | VARCHAR(2000000) | +SET | ✓ | CHAR | +SMALLINT | ✓ | DECIMAL | +TEXT | ✓ | VARCHAR(65535) | The size of the column is always 65535.* +TINYBLOB | × | | +TINYINT | ✓ | DECIMAL | +TINYTEXT | ✓ | VARCHAR | +TIME | ✓ | TIMESTAMP | Casted to `TIMESTAMP` with a format `1970-01-01 hh:mm:ss`. +TIMESTAMP | ✓ | TIMESTAMP | +VARBINARY | × | | +VARCHAR | ✓ | VARCHAR | +YEAR | ✓ | DATE | + +* The tested versions of MySQL Connector JDBC Driver return the column's size depending on the charset and its collation. +As the real data in a MySQL table can sometimes exceed the size that we get from the JDBC driver, we set the size for all TEXT columns to 65535 characters. + +If you need to use currently unsupported data types or find a way around known limitations, please, create a github issue in the [VS repository](https://github.com/exasol/virtual-schemas/issues). + +## Testing information + +In the following matrix you find combinations of JDBC driver and dialect version that we tested. + +| Virtual Schema Version | MySQL Version | Driver Name | Driver Version | +|------------------------|---------------|-----------------|----------------| +| Latest | MySQL 8.0.20 | MySQL Connector | 8.0.20 | + +## Executing Disabled Integration Test + +The integration tests are disabled by default, but it is possible to execute them locally. +The reason for the tests being disabled is we can only deliver drivers where the license allows redistribution, and it's not the case with MySQL. +Therefore we cannot include the MySQL JDBC driver, so in order to execute the integration tests you need to download them manually. + +### Starting Disabled Integration Test Locally + +1. Download the MySQL JDBC driver: + - [mysql-connector-java-.jar](https://dev.mysql.com/downloads/connector/j/) +2. Temporarily put the files into `src/test/resources/integration/driver/mysql` directory. +3. If the files' names are different (ie, you renamed the file, or it has a different version number, for example) from the mentioned above, edit `src/test/resources/integration/driver/mysql/mysql.properties` and `settings.cfg` files. +4. Run the tests from an IDE or temporarily add the integration test name into the `maven-failsafe-plugin`'s includes a section and execute `mvn verify` command. +5. Remove the driver after the test and **do not upload it to the GitHub repository**. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0221829 --- /dev/null +++ b/pom.xml @@ -0,0 +1,316 @@ + + 4.0.0 + com.exasol + mysql-virtual-schema + 1.0.0 + Virtual Schema for MySQL + + + maven.exasol.com + https://maven.exasol.com/artifactory/exasol-releases + + + maven.exasol.com + https://maven.exasol.com/artifactory/exasol-snapshots + + + + UTF-8 + UTF-8 + 11 + 3.0.0-M3 + 8.0.0 + 1.15.0 + target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml + + + + + maven.exasol.com + https://maven.exasol.com/artifactory/exasol-releases + + false + + + + maven.exasol.com-snapshots + https://maven.exasol.com/artifactory/exasol-snapshots + + true + + + + + + com.exasol + virtual-schema-common-jdbc + ${vscjdbc.version} + + + com.exasol + error-reporting-java + 0.2.0 + + + + com.exasol + virtual-schema-common-jdbc + ${vscjdbc.version} + test-jar + test + + + org.hamcrest + hamcrest + 2.2 + test + + + org.junit.jupiter + junit-jupiter + 5.7.0 + test + + + org.mockito + mockito-junit-jupiter + 3.6.28 + test + + + + junit + junit + 4.13.1 + test + + + + com.exasol + exasol-testcontainers + 3.3.1 + test + + + org.testcontainers + junit-jupiter + ${org.testcontainers.version} + test + + + org.testcontainers + mysql + ${org.testcontainers.version} + test + + + mysql + mysql-connector-java + 8.0.22 + test + + + com.exasol + test-db-builder-java + 2.0.0 + test + + + com.exasol + hamcrest-resultset-matcher + 1.2.2 + test + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire.and.failsafe.plugin.version} + + + -Djava.util.logging.config.file=src/test/resources/logging.properties ${argLine} + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + prepare-agent + + prepare-agent + + + + prepare-agent-integration + + prepare-agent-integration + + + + report + test + + report + + + + report-integration + verify + + report-integration + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + src/assembly/all-dependencies.xml + + virtual-schema-dist-${vscjdbc.version}-mysql-${version} + false + + + + make-assembly + package + + single + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${surefire.and.failsafe.plugin.version} + + + -Djava.util.logging.config.file=src/test/resources/logging.properties ${argLine} + + MySQLSqlDialectIT.java + + + + + + integration-test + verify + + + + + + org.sonatype.ossindex.maven + ossindex-maven-plugin + 3.1.0 + + + package + + audit + + + + + + + 7ea56ad4-8a8b-4e51-8ed9-5aad83d8efb1 + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + + package + + display-plugin-updates + display-dependency-updates + + + + + file:///${project.basedir}/versionsMavenPluginRules.xml + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + enforce-maven + + enforce + + + + + 3.3.9 + + + + + + + + com.exasol + artifact-reference-checker-maven-plugin + 0.3.1 + + + + verify + + + + + + /doc/changes/* + + + + + com.exasol + project-keeper-maven-plugin + 0.4.1 + + + + verify + + + + + + jar_artifact + integration_tests + + + + + + \ No newline at end of file diff --git a/src/assembly/all-dependencies.xml b/src/assembly/all-dependencies.xml new file mode 100644 index 0000000..efe5abc --- /dev/null +++ b/src/assembly/all-dependencies.xml @@ -0,0 +1,22 @@ + + all-dependencies + + jar + + false + + + + metaInf-services + + + + + true + runtime + / + + + diff --git a/src/main/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReader.java new file mode 100644 index 0000000..eeaa07e --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReader.java @@ -0,0 +1,74 @@ +package com.exasol.adapter.dialects.mysql; + +import java.sql.Connection; +import java.sql.Types; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.IdentifierConverter; +import com.exasol.adapter.jdbc.BaseColumnMetadataReader; +import com.exasol.adapter.jdbc.JdbcTypeDescription; +import com.exasol.adapter.metadata.DataType; + +/** + * This class implements MySQL-specific reading of column metadata. + */ +public class MySQLColumnMetadataReader extends BaseColumnMetadataReader { + private static final String TEXT_DATA_TYPE_NAME = "TEXT"; + protected static final int TEXT_DATA_TYPE_SIZE = 65535; + + /** + * Create a new instance of the {@link MySQLColumnMetadataReader}. + * + * @param connection JDBC connection through which the column metadata is read from the remote database + * @param properties user-defined adapter properties + * @param identifierConverter converter between source and Exasol identifiers + */ + public MySQLColumnMetadataReader(final Connection connection, final AdapterProperties properties, + final IdentifierConverter identifierConverter) { + super(connection, properties, identifierConverter); + } + + @Override + public DataType mapJdbcType(final JdbcTypeDescription jdbcTypeDescription) { + switch (jdbcTypeDescription.getJdbcType()) { + case Types.TIME: + return DataType.createTimestamp(false); + case Types.BINARY: + return DataType.createUnsupported(); + case Types.LONGVARCHAR: + return convertVarChar(jdbcTypeDescription); + default: + return super.mapJdbcType(jdbcTypeDescription); + } + } + + private DataType convertVarChar(final JdbcTypeDescription jdbcTypeDescription) { + final int size = getVarcharSize(jdbcTypeDescription); + final int octetLength = jdbcTypeDescription.getByteSize(); + final DataType.ExaCharset charset = (octetLength == size) ? DataType.ExaCharset.ASCII + : DataType.ExaCharset.UTF8; + if (size <= DataType.MAX_EXASOL_VARCHAR_SIZE) { + final int precision = getVarcharPrecision(size); + return DataType.createVarChar(precision, charset); + } else { + return DataType.createVarChar(DataType.MAX_EXASOL_VARCHAR_SIZE, charset); + } + } + + private int getVarcharPrecision(int size) { + if (size == 0) { + return DataType.MAX_EXASOL_VARCHAR_SIZE; + } else { + return size; + } + } + + private int getVarcharSize(final JdbcTypeDescription jdbcTypeDescription) { + final String typeName = jdbcTypeDescription.getTypeName(); + if (typeName.equals(TEXT_DATA_TYPE_NAME)) { + return TEXT_DATA_TYPE_SIZE; + } else { + return jdbcTypeDescription.getPrecisionOrSize(); + } + } +} diff --git a/src/main/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReader.java b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReader.java new file mode 100644 index 0000000..5dae768 --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReader.java @@ -0,0 +1,45 @@ +package com.exasol.adapter.dialects.mysql; + +import java.sql.Connection; +import java.util.Set; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.jdbc.*; + +/** + * Metadata reader that reads MySQL-specific database metadata. + */ +public class MySQLMetadataReader extends AbstractRemoteMetadataReader { + /** + * Create a new instance of the {@link MySQLMetadataReader}. + * + * @param connection JDBC connection to the remote data source + * @param properties user-defined adapter properties + */ + public MySQLMetadataReader(final Connection connection, final AdapterProperties properties) { + super(connection, properties); + } + + @Override + protected ColumnMetadataReader createColumnMetadataReader() { + return new MySQLColumnMetadataReader(this.connection, this.properties, this.identifierConverter); + } + + @Override + protected TableMetadataReader createTableMetadataReader() { + return new BaseTableMetadataReader(this.connection, this.columnMetadataReader, this.properties, + this.identifierConverter); + } + + @Override + protected IdentifierConverter createIdentifierConverter() { + return new BaseIdentifierConverter(IdentifierCaseHandling.INTERPRET_CASE_SENSITIVE, + IdentifierCaseHandling.INTERPRET_CASE_SENSITIVE); + } + + @Override + public Set getSupportedTableTypes() { + return RemoteMetadataReaderConstants.ANY_TABLE_TYPE; + } +} diff --git a/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialect.java b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialect.java new file mode 100644 index 0000000..8ee6f72 --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialect.java @@ -0,0 +1,138 @@ +package com.exasol.adapter.dialects.mysql; + +import static com.exasol.adapter.AdapterProperties.CATALOG_NAME_PROPERTY; +import static com.exasol.adapter.capabilities.AggregateFunctionCapability.*; +import static com.exasol.adapter.capabilities.LiteralCapability.*; +import static com.exasol.adapter.capabilities.MainCapability.*; +import static com.exasol.adapter.capabilities.PredicateCapability.*; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.*; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.ST_INTERSECTION; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.ST_UNION; + +import java.sql.SQLException; +import java.util.Set; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.capabilities.Capabilities; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.jdbc.*; +import com.exasol.adapter.sql.SqlNodeVisitor; + +/** + * This class implements the SQL dialect of MySQL. + * + * @see MySQL developer documentation + */ +public class MySQLSqlDialect extends AbstractSqlDialect { + static final String NAME = "MYSQL"; + private static final Capabilities CAPABILITIES = createCapabilityList(); + + /** + * Create a new instance of the {@link MySQLSqlDialect}. + * + * @param connectionFactory factory for the JDBC connection to the Athena service + * @param properties user-defined adapter properties + */ + public MySQLSqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { + super(connectionFactory, properties, Set.of(CATALOG_NAME_PROPERTY)); + } + + private static Capabilities createCapabilityList() { + return Capabilities // + .builder() // + .addMain(SELECTLIST_PROJECTION, SELECTLIST_EXPRESSIONS, FILTER_EXPRESSIONS, AGGREGATE_SINGLE_GROUP, + AGGREGATE_GROUP_BY_COLUMN, AGGREGATE_GROUP_BY_EXPRESSION, AGGREGATE_GROUP_BY_TUPLE, + AGGREGATE_HAVING, ORDER_BY_COLUMN, ORDER_BY_EXPRESSION, LIMIT, LIMIT_WITH_OFFSET, JOIN, + JOIN_TYPE_INNER, JOIN_TYPE_LEFT_OUTER, JOIN_TYPE_RIGHT_OUTER, JOIN_CONDITION_EQUI) // + .addLiteral(NULL, BOOL, DATE, TIMESTAMP, TIMESTAMP_UTC, DOUBLE, EXACTNUMERIC, STRING, INTERVAL) // + .addPredicate(AND, OR, NOT, EQUAL, NOTEQUAL, LESS, LESSEQUAL, LIKE, BETWEEN, IS_NULL, IS_NOT_NULL) // + .addScalarFunction(ABS, ACOS, ASIN, ATAN, ATAN2, CEIL, COS, COT, DEGREES, DIV, EXP, FLOOR, GREATEST, + LEAST, LN, LOG, MOD, POWER, RADIANS, RAND, ROUND, SIGN, SIN, SQRT, TAN, ASCII, BIT_LENGTH, + CONCAT, INSERT, INSTR, LENGTH, LOCATE, LOWER, LPAD, LTRIM, OCTET_LENGTH, REGEXP_INSTR, + REGEXP_REPLACE, REGEXP_SUBSTR, REPEAT, REPLACE, REVERSE, RIGHT, RPAD, RTRIM, SOUNDEX, SPACE, + SUBSTR, TRIM, UPPER, ADD_DAYS, ADD_HOURS, ADD_MINUTES, ADD_MONTHS, ADD_SECONDS, ADD_WEEKS, + ADD_YEARS, CONVERT_TZ, CURRENT_DATE, CURRENT_TIMESTAMP, EXTRACT, LOCALTIMESTAMP, MINUTE, MONTH, + SECOND, SYSDATE, SYSTIMESTAMP, WEEK, YEAR, ST_X, ST_Y, ST_ENDPOINT, ST_ISCLOSED, ST_LENGTH, + ST_NUMPOINTS, ST_POINTN, ST_STARTPOINT, ST_AREA, ST_EXTERIORRING, ST_INTERIORRINGN, + ST_NUMINTERIORRINGS, ST_GEOMETRYN, ST_NUMGEOMETRIES, ST_BUFFER, ST_CENTROID, ST_CONTAINS, + ST_CONVEXHULL, ST_CROSSES, ST_DIFFERENCE, ST_DIMENSION, ST_DISJOINT, ST_DISTANCE, ST_ENVELOPE, + ST_EQUALS, ST_GEOMETRYTYPE, ST_INTERSECTION, ST_INTERSECTS, ST_ISEMPTY, ST_ISSIMPLE, + ST_OVERLAPS, ST_SYMDIFFERENCE, ST_TOUCHES, ST_TRANSFORM, ST_UNION, ST_WITHIN, CAST, BIT_AND, + BIT_OR, BIT_XOR, CASE, CURRENT_USER) // + .addAggregateFunction(COUNT, SUM, MIN, MAX, AVG, STDDEV, STDDEV_POP, STDDEV_SAMP, VARIANCE, VAR_POP, + VAR_SAMP) // + .build(); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public Capabilities getCapabilities() { + return CAPABILITIES; + } + + @Override + public StructureElementSupport supportsJdbcCatalogs() { + return StructureElementSupport.MULTIPLE; + } + + @Override + public StructureElementSupport supportsJdbcSchemas() { + return StructureElementSupport.NONE; + } + + @Override + // https://dev.mysql.com/doc/refman/8.0/en/identifiers.html + public String applyQuote(final String identifier) { + return "`" + identifier.replace("`", "``") + "`"; + } + + @Override + public boolean requiresCatalogQualifiedTableNames(final SqlGenerationContext context) { + return true; + } + + @Override + public boolean requiresSchemaQualifiedTableNames(final SqlGenerationContext context) { + return false; + } + + @Override + public NullSorting getDefaultNullSorting() { + return NullSorting.NULLS_SORTED_AT_END; + } + + @Override + // https://dev.mysql.com/doc/refman/8.0/en/string-literals.html + public String getStringLiteral(final String value) { + if (value == null) { + return "NULL"; + } else { + // We replace \ with \\ because we expect that the mode NO_BACKSLASH_ESCAPES is not used. + return "'" + value.replace("\\", "\\\\").replace("'", "''") + "'"; + } + } + + @Override + protected RemoteMetadataReader createRemoteMetadataReader() { + try { + return new MySQLMetadataReader(this.connectionFactory.getConnection(), this.properties); + } catch (final SQLException exception) { + throw new RemoteMetadataReaderException( + "Unable to create MySQL remote metadata reader. Caused by: " + exception.getMessage(), exception); + } + } + + @Override + protected QueryRewriter createQueryRewriter() { + return new ImportIntoQueryRewriter(this, createRemoteMetadataReader(), this.connectionFactory); + } + + @Override + public SqlNodeVisitor getSqlGenerationVisitor(final SqlGenerationContext context) { + return new MySQLSqlGenerationVisitor(this, context); + } +} diff --git a/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactory.java b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactory.java new file mode 100644 index 0000000..36b5bd7 --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactory.java @@ -0,0 +1,29 @@ +package com.exasol.adapter.dialects.mysql; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.SqlDialect; +import com.exasol.adapter.dialects.SqlDialectFactory; +import com.exasol.adapter.jdbc.ConnectionFactory; +import com.exasol.logging.VersionCollector; + +/** + * Factory for the MySql SQL dialect. + */ +public class MySQLSqlDialectFactory implements SqlDialectFactory { + @Override + public SqlDialect createSqlDialect(final ConnectionFactory connectionFactory, final AdapterProperties properties) { + return new MySQLSqlDialect(connectionFactory, properties); + } + + @Override + public String getSqlDialectName() { + return MySQLSqlDialect.NAME; + } + + @Override + public String getSqlDialectVersion() { + final VersionCollector versionCollector = new VersionCollector( + "META-INF/maven/com.exasol/mysql-virtual-schema/pom.properties"); + return versionCollector.getVersionNumber(); + } +} \ No newline at end of file diff --git a/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitor.java b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitor.java new file mode 100644 index 0000000..e791d11 --- /dev/null +++ b/src/main/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitor.java @@ -0,0 +1,31 @@ +package com.exasol.adapter.dialects.mysql; + +import java.util.ArrayList; +import java.util.List; + +import com.exasol.adapter.AdapterException; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.sql.*; + +public class MySQLSqlGenerationVisitor extends SqlGenerationVisitor { + public MySQLSqlGenerationVisitor(final SqlDialect dialect, final SqlGenerationContext context) { + super(dialect, context); + } + + @Override + public String visit(final SqlFunctionScalar function) throws AdapterException { + if (function.getFunction() == ScalarFunction.DIV) { + return getChangedDiv(function); + } + return super.visit(function); + } + + private String getChangedDiv(final SqlFunctionScalar function) throws AdapterException { + final List arguments = function.getArguments(); + final List argumentsSql = new ArrayList<>(arguments.size()); + for (final SqlNode node : arguments) { + argumentsSql.add(node.accept(this)); + } + return argumentsSql.get(0) + " DIV " + argumentsSql.get(1); + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory b/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory new file mode 100644 index 0000000..e1920b7 --- /dev/null +++ b/src/main/resources/META-INF/services/com.exasol.adapter.dialects.SqlDialectFactory @@ -0,0 +1 @@ +com.exasol.adapter.dialects.mysql.MySQLSqlDialectFactory \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/IntegrationTestConstants.java b/src/test/java/com/exasol/adapter/dialects/mysql/IntegrationTestConstants.java new file mode 100644 index 0000000..da1b306 --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/IntegrationTestConstants.java @@ -0,0 +1,20 @@ +package com.exasol.adapter.dialects.mysql; + +import java.nio.file.Path; + +public final class IntegrationTestConstants { + public static final String VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION = "virtual-schema-dist-8.0.0-mysql-1.0.0.jar"; + public static final String EXASOL_DOCKER_IMAGE_REFERENCE = "exasol/docker-db:7.0.2"; + public static final String MYSQL_DOCKER_IMAGE_REFERENCE = "mysql:8.0.20"; + public static final Path PATH_TO_VIRTUAL_SCHEMAS_JAR = Path.of("target", VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION); + public static final String SCHEMA_EXASOL = "SCHEMA_EXASOL"; + public static final String ADAPTER_SCRIPT_EXASOL = "ADAPTER_SCRIPT_EXASOL"; + public static final String TABLE_JOIN_1 = "TABLE_JOIN_1"; + public static final String TABLE_JOIN_2 = "TABLE_JOIN_2"; + public static final String DOCKER_IP_ADDRESS = "172.17.0.1"; + public static final String JDBC_DRIVER_CONFIGURATION_FILE_NAME = "settings.cfg"; + + private IntegrationTestConstants() { + // intentionally left empty + } +} diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReaderTest.java new file mode 100644 index 0000000..6b32481 --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLColumnMetadataReaderTest.java @@ -0,0 +1,51 @@ +package com.exasol.adapter.dialects.mysql; + +import static com.exasol.adapter.dialects.mysql.MySQLColumnMetadataReader.TEXT_DATA_TYPE_SIZE; +import static com.exasol.adapter.metadata.DataType.MAX_EXASOL_VARCHAR_SIZE; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.sql.Types; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.BaseIdentifierConverter; +import com.exasol.adapter.jdbc.JdbcTypeDescription; +import com.exasol.adapter.metadata.DataType; + +class MySQLColumnMetadataReaderTest { + private MySQLColumnMetadataReader columnMetadataReader; + + @BeforeEach + void beforeEach() { + this.columnMetadataReader = new MySQLColumnMetadataReader(null, AdapterProperties.emptyProperties(), + BaseIdentifierConverter.createDefault()); + } + + @Test + void mapTime() { + final JdbcTypeDescription typeDescription = new JdbcTypeDescription(Types.TIME, 0, 0, 10, "TIME"); + assertThat(columnMetadataReader.mapJdbcType(typeDescription), equalTo(DataType.createTimestamp(false))); + } + + @Test + void mapBinary() { + final JdbcTypeDescription typeDescription = new JdbcTypeDescription(Types.BINARY, 0, 0, 10, "BINARY"); + assertThat(columnMetadataReader.mapJdbcType(typeDescription), equalTo(DataType.createUnsupported())); + } + + @Test + void mapText() { + final JdbcTypeDescription typeDescription = new JdbcTypeDescription(Types.LONGVARCHAR, 0, 16383, 10, "TEXT"); + assertThat(columnMetadataReader.mapJdbcType(typeDescription).getSize(), equalTo(TEXT_DATA_TYPE_SIZE)); + } + + @Test + void mapMediumText() { + final JdbcTypeDescription typeDescription = new JdbcTypeDescription(Types.LONGVARCHAR, 0, 4194303, 10, + "MEDIUMTEXT"); + assertThat(columnMetadataReader.mapJdbcType(typeDescription).getSize(), equalTo(MAX_EXASOL_VARCHAR_SIZE)); + } +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReaderTest.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReaderTest.java new file mode 100644 index 0000000..2633394 --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLMetadataReaderTest.java @@ -0,0 +1,54 @@ +package com.exasol.adapter.dialects.mysql; + +import static org.hamcrest.Matchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.sql.Connection; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.jdbc.BaseTableMetadataReader; + +@ExtendWith(MockitoExtension.class) +class MySQLMetadataReaderTest { + private MySQLMetadataReader reader; + @Mock + private Connection connectionMock; + + @BeforeEach + void beforeEach() { + this.reader = new MySQLMetadataReader(this.connectionMock, AdapterProperties.emptyProperties()); + } + + @Test + void testGetTableMetadataReader() { + assertThat(this.reader.getTableMetadataReader(), instanceOf(BaseTableMetadataReader.class)); + } + + @Test + void testGetColumnMetadataReader() { + assertThat(this.reader.getColumnMetadataReader(), instanceOf(MySQLColumnMetadataReader.class)); + } + + @Test + void testGetSupportedTableTypes() { + assertThat(this.reader.getSupportedTableTypes(), emptyIterableOf(String.class)); + } + + @Test + void testCreateIdentifierConverter() { + final IdentifierConverter converter = this.reader.getIdentifierConverter(); + assertAll(() -> assertThat(converter, instanceOf(BaseIdentifierConverter.class)), + () -> assertThat(converter.getQuotedIdentifierHandling(), + equalTo(IdentifierCaseHandling.INTERPRET_CASE_SENSITIVE)), + () -> assertThat(converter.getUnquotedIdentifierHandling(), + equalTo(IdentifierCaseHandling.INTERPRET_CASE_SENSITIVE))); + } +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactoryTest.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactoryTest.java new file mode 100644 index 0000000..acffcac --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectFactoryTest.java @@ -0,0 +1,30 @@ +package com.exasol.adapter.dialects.mysql; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.exasol.adapter.AdapterProperties; + +class MySQLSqlDialectFactoryTest { + private MySQLSqlDialectFactory factory; + + @BeforeEach + void beforeEach() { + this.factory = new MySQLSqlDialectFactory(); + } + + @Test + void testGetName() { + assertThat(this.factory.getSqlDialectName(), equalTo("MYSQL")); + } + + @Test + void testCreateDialect() { + assertThat(this.factory.createSqlDialect(null, AdapterProperties.emptyProperties()), + instanceOf(MySQLSqlDialect.class)); + } +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectIT.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectIT.java new file mode 100644 index 0000000..c98af8d --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectIT.java @@ -0,0 +1,667 @@ +package com.exasol.adapter.dialects.mysql; + +import static com.exasol.adapter.dialects.mysql.IntegrationTestConstants.*; +import static com.exasol.dbbuilder.dialects.exasol.AdapterScript.Language.JAVA; +import static com.exasol.matcher.ResultSetMatcher.matchesResultSet; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.*; +import java.nio.file.Path; +import java.sql.*; +import java.util.*; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.*; +import org.testcontainers.containers.MySQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import com.exasol.bucketfs.Bucket; +import com.exasol.bucketfs.BucketAccessException; +import com.exasol.containers.ExasolContainer; +import com.exasol.dbbuilder.dialects.Schema; +import com.exasol.dbbuilder.dialects.Table; +import com.exasol.dbbuilder.dialects.exasol.*; +import com.exasol.dbbuilder.dialects.mysql.MySqlObjectFactory; +import com.exasol.dbbuilder.dialects.mysql.MySqlSchema; + +/** + * How to run `MySqlSqlDialectIT`: See the documentation > exasolContainer = new ExasolContainer<>( + EXASOL_DOCKER_IMAGE_REFERENCE); + @Container + private static final MySQLContainer mySQLContainer = new MySQLContainer<>(MYSQL_DOCKER_IMAGE_REFERENCE) + .withUsername("root").withPassword(""); + + @BeforeAll + static void beforeAll() throws InterruptedException, BucketAccessException, TimeoutException, SQLException { + final String driverName = getPropertyFromFile(RESOURCES_FOLDER_DIALECT_NAME, "driver.name"); + uploadDriverToBucket(driverName, RESOURCES_FOLDER_DIALECT_NAME, exasolContainer.getDefaultBucket()); + uploadVsJarToBucket(exasolContainer.getDefaultBucket()); + final MySqlObjectFactory mySqlFactory = new MySqlObjectFactory(mySQLContainer.createConnection("")); + final MySqlSchema mySqlSchema = mySqlFactory.createSchema(MYSQL_SCHEMA); + createMySqlSimpleTable(mySqlSchema); + createMySqlNumericDateTable(mySqlSchema); + createMySqlStringTable(mySqlSchema); + createTestTablesForJoinTests(mySQLContainer.createConnection(""), mySqlSchema.getName()); + final ExasolObjectFactory exasolFactory = new ExasolObjectFactory(exasolContainer.createConnection("")); + final ExasolSchema exasolSchema = exasolFactory.createSchema(SCHEMA_EXASOL); + final AdapterScript adapterScript = createAdapterScript(driverName, exasolSchema); + final String connectionString = "jdbc:mysql://" + DOCKER_IP_ADDRESS + ":" + + mySQLContainer.getMappedPort(MYSQL_PORT) + "/"; + final ConnectionDefinition connectionDefinition = exasolFactory.createConnectionDefinition(JDBC_CONNECTION_NAME, + connectionString, mySQLContainer.getUsername(), mySQLContainer.getPassword()); + exasolFactory.createVirtualSchemaBuilder(VIRTUAL_SCHEMA_JDBC).adapterScript(adapterScript) + .connectionDefinition(connectionDefinition).dialectName("MYSQL") + .properties(Map.of("CATALOG_NAME", MYSQL_SCHEMA)).build(); + } + + private static void uploadDriverToBucket(final String driverName, final String resourcesDialectName, + final Bucket bucket) throws InterruptedException, BucketAccessException, TimeoutException { + final Path pathToSettingsFile = Path.of("src", "test", "resources", "integration", "driver", + resourcesDialectName, JDBC_DRIVER_CONFIGURATION_FILE_NAME); + bucket.uploadFile(PATH_TO_VIRTUAL_SCHEMAS_JAR, VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION); + bucket.uploadFile(pathToSettingsFile, "drivers/jdbc/" + JDBC_DRIVER_CONFIGURATION_FILE_NAME); + final String driverPath = getPropertyFromFile(resourcesDialectName, "driver.path"); + bucket.uploadFile(Path.of(driverPath, driverName), "drivers/jdbc/" + driverName); + } + + private static String getPathToPropertyFile(final String resourcesDialectName) { + return "src/test/resources/integration/driver/" + resourcesDialectName + "/" + resourcesDialectName + + ".properties"; + } + + private static void uploadVsJarToBucket(final Bucket bucket) + throws InterruptedException, BucketAccessException, TimeoutException { + bucket.uploadFile(PATH_TO_VIRTUAL_SCHEMAS_JAR, VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION); + } + + private static String getPropertyFromFile(final String resourcesDialectName, final String propertyName) { + final String pathToPropertyFile = getPathToPropertyFile(resourcesDialectName); + try (final InputStream inputStream = new FileInputStream(pathToPropertyFile)) { + final Properties properties = new Properties(); + properties.load(inputStream); + return properties.getProperty(propertyName); + } catch (final IOException e) { + throw new IllegalArgumentException( + "Cannot access the properties file or read from it. Check if the path spelling is correct" + + " and if the file exists."); + } + } + + private static void createTestTablesForJoinTests(final Connection connection, final String schemaName) + throws SQLException { + try (final Statement statement = connection.createStatement()) { + statement.execute("CREATE TABLE " + schemaName + "." + TABLE_JOIN_1 + "(x INT, y VARCHAR(100))"); + statement.execute("INSERT INTO " + schemaName + "." + TABLE_JOIN_1 + " VALUES (1,'aaa')"); + statement.execute("INSERT INTO " + schemaName + "." + TABLE_JOIN_1 + " VALUES (2,'bbb')"); + statement.execute("CREATE TABLE " + schemaName + "." + TABLE_JOIN_2 + "(x INT, y VARCHAR(100))"); + statement.execute("INSERT INTO " + schemaName + "." + TABLE_JOIN_2 + " VALUES (2,'bbb')"); + statement.execute("INSERT INTO " + schemaName + "." + TABLE_JOIN_2 + " VALUES (3,'ccc')"); + } + } + + private ResultSet getExpectedResultSet(final List expectedColumns, final List expectedRows) + throws SQLException { + final Connection connection = getExasolConnection(); + try (final Statement statement = connection.createStatement()) { + final String expectedValues = expectedRows.stream().map(row -> "(" + row + ")") + .collect(Collectors.joining(",")); + final String qualifiedExpectedTableName = SCHEMA_EXASOL + ".EXPECTED"; + statement.execute("CREATE OR REPLACE TABLE " + qualifiedExpectedTableName + "(" + + String.join(", ", expectedColumns) + ")"); + statement.execute("INSERT INTO " + qualifiedExpectedTableName + " VALUES" + expectedValues); + return statement.executeQuery("SELECT * FROM " + qualifiedExpectedTableName); + } + } + + private ResultSet getActualResultSet(final String query) throws SQLException { + final Connection connection = getExasolConnection(); + try (final Statement statement = connection.createStatement()) { + return statement.executeQuery(query); + } + } + + private static void createMySqlSimpleTable(final Schema mySqlSchema) { + final Table table = mySqlSchema.createTable(MYSQL_SIMPLE_TABLE, List.of("int_col", "bool_col", "varchar_col"), + List.of("INT", "BOOLEAN", "VARCHAR(20)")); + table.insert(-100, true, "a"); + table.insert(-1, false, "abbb"); + table.insert(0, true, "b"); + table.insert(10, false, "bbbb"); + table.insert(50, true, "abc"); + table.insert(100, false, "a"); + } + + private static void createMySqlNumericDateTable(final Schema mySqlSchema) { + final Table table = mySqlSchema.createTable(MYSQL_NUMERIC_DATE_DATATYPES_TABLE, + List.of("BiT_Col", "tinyint_col", "BOOL_COL", "smallint_col", "mediumint_col", "int_col", "bigint_col", + "decimal_col", "float_col", "double_col", // + "date_col", "datetime_col", "timestamp_col", "time_col", "year_col"), + List.of("BIT(6)", "TINYINT", "BOOLEAN", "SMALLINT", "MEDIUMINT", "INT", "BIGINT", "DECIMAL(5, 2)", + "FLOAT(7, 4)", "DOUBLE", // + "DATE", "DATETIME", "TIMESTAMP", "TIME", "YEAR")); + table.insert("5", 127, 1, 32767, 8388607, 2147483647, 9223372036854775807L, 999.99, 999.00009, 999.00009, // + "1000-01-01", "1000-01-01 00:00:00", "1970-01-01 00:00:01.000000", "16:59:59.000000", 1901); + table.insert("9", -127, 0, -32768, -8388608, -2147483648, -9223372036854775808L, -999.99, -999.9999, -999.9999, // + "9999-12-31", "9999-12-31 22:59:59", "2037-01-19 03:14:07.999999", "05:34:13.000000", 2155); + table.insert(null, 0, true, 0, 0, 0, 0, 0, 0, 0, // + null, null, null, null, "1901"); + table.insert(null, 0, false, 0, 0, 0, 0, 0, 0, 0, // + null, null, null, null, 69); + } + + private static void createMySqlStringTable(final Schema mySqlSchema) { + final Table table = mySqlSchema.createTable(MYSQL_STRING_DATATYPES_TABLE, + List.of("binary_col", "varbinary_col", "tinyblob_col", "tinytext_col", "blob_col", "text_col", + "mediumblob_col", "mediumtext_col", "longblob_col", "longtext_col", "enum_col", "set_col", + "varchar_col", "char_col"), + List.of(" BINARY(20)", "VARBINARY(20)", "TINYBLOB", "TINYTEXT", "BLOB", "TEXT", "MEDIUMBLOB", + "MEDIUMTEXT", "LONGBLOB", "LONGTEXT", "ENUM('1', '2', '3')", "SET('1')", "VARCHAR(16000)", + "CHAR(255)")); + table.insert("a", "a", "a", "a", "blob", "text", "mediumblob", "mediumtext", "longblob", "longtext", "1", "1", + "ab", "asd24"); + table.insert("a\0", "a\0", "aa", "b", "blob", "text2", "mediumblob2", "mediumtext2", "longblob", "longtext2", + "2", "1", "a", "11111"); + table.insert(null, null, "aaa", "aaaaaaaaaaaaa", "bloooooooooooob", "text3", null, null, null, null, "3", null, + "", ""); + table.insert(null, null, "aaaaa", "a", "blob", "text", null, null, null, null, null, null, null, null); + } + + private static AdapterScript createAdapterScript(final String driverName, final ExasolSchema schema) { + final String content = "%scriptclass com.exasol.adapter.RequestDispatcher;\n" // + + "%jar /buckets/bfsdefault/default/" + VIRTUAL_SCHEMAS_JAR_NAME_AND_VERSION + ";\n" // + + "%jar /buckets/bfsdefault/default/drivers/jdbc/" + driverName + ";\n"; + return schema.createAdapterScript(ADAPTER_SCRIPT_EXASOL, JAVA, content); + } + + @Test + void testSelectAll() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE; + final ResultSet actualResultSet = getActualResultSet(query); + final ResultSet expected = getExpectedResultSet(List.of("col1 INT", "col2 BOOLEAN", "col3 VARCHAR(20)"), // + List.of("-100, true, 'a'", // + "-1, false, 'abbb'", // + "0, true, 'b'", // + "10, false, 'bbbb'", // + "50, true, 'abc'", // + "100, false, 'a'")); + assertThat(actualResultSet, matchesResultSet(expected)); + } + + private Connection getExasolConnection() throws SQLException { + return exasolContainer.createConnection(""); + } + + @Nested + @DisplayName("Datatype tests") + class DatatypeTest { + @Test + void testBit() throws SQLException { + final String query = "SELECT \"BiT_Col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 BOOLEAN"), // + List.of("true", "true", "false", "false")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testTinyInt() throws SQLException { + final String query = "SELECT \"tinyint_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(3,0)"), // + List.of("127", "-127", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testBoolean() throws SQLException { + final String query = "SELECT \"BOOL_COL\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 BOOLEAN"), // + List.of("true", "false", "true", "false")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testSmallInt() throws SQLException { + final String query = "SELECT \"smallint_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(5,0)"), // + List.of("32767", "-32768", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testMediumInt() throws SQLException { + final String query = "SELECT \"mediumint_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(7,0)"), // + List.of("8388607", "-8388608", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testInt() throws SQLException { + final String query = "SELECT \"int_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(10,0)"), // + List.of("2147483647", "-2147483648", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testBigInt() throws SQLException { + final String query = "SELECT \"bigint_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(19,0)"), // + List.of("9223372036854775807", "-9223372036854775808", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testDecimal() throws SQLException { + final String query = "SELECT \"decimal_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DECIMAL(5,2)"), // + List.of("999.99", "-999.99", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testFloat() throws SQLException { + final String query = "SELECT \"float_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DOUBLE PRECISION"), // + List.of("999.0001", "-999.9999", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testDouble() throws SQLException { + final String query = "SELECT \"double_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DOUBLE PRECISION"), // + List.of("999.00009", "-999.9999", "0", "0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testDate() throws SQLException { + final String query = "SELECT \"date_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DATE"), // + List.of("'1000-01-01'", "'9999-12-31'", "null", "null")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testDatetime() throws SQLException { + final String query = "SELECT \"datetime_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 TIMESTAMP"), // + List.of("'1000-01-01 01:00:00'", "'9999-12-31 23:59:59'", "null", "null")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testTimestamp() throws SQLException { + final String query = "SELECT \"timestamp_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 TIMESTAMP"), // + List.of("'1970-01-01 01:00:01.000000'", "'2037-01-19 04:14:08.0'", "null", "null")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testTime() throws SQLException { + final String query = "SELECT \"time_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 TIMESTAMP"), // + List.of("'1970-01-01 17:59:59.000000'", "'1970-01-01 06:34:13.000000'", "null", "null")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testYear() throws SQLException { + final String query = "SELECT \"year_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_NUMERIC_DATE_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 DATE"), // + List.of("'1901-01-01'", "'2155-01-01'", "'1901-01-01'", "'2069-01-01'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + // Unsupported data types: BINARY, BLOB, LONGBLOB, MEDIUMBLOB, TINYBLOB, VARBINARY. + void testUnsupported() throws SQLException, InterruptedException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_STRING_DATATYPES_TABLE; + final SQLException exception = assertThrows(SQLException.class, () -> getActualResultSet(query)); + assertThat(exception.getMessage(), + containsString(" Unsupported data type(s) in column(s) 1, 2, 3, 5, 7, 9 in query.")); + } + + @Test + void testTinyText() throws SQLException { + final String query = "SELECT \"tinytext_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 VARCHAR(100)"), // + List.of("'a'", "'b'", "'aaaaaaaaaaaaa'", "'a'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testText() throws SQLException { + final String query = "SELECT \"text_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 VARCHAR(65535)"), // + List.of("'text'", "'text2'", "'text3'", "'text'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testMediumText() throws SQLException { + final String query = "SELECT \"mediumtext_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 VARCHAR(2000000)"), // + List.of("'mediumtext'", "'mediumtext2'", "NULL", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testLongText() throws SQLException { + final String query = "SELECT \"longtext_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 VARCHAR(2000000)"), // + List.of("'longtext'", "'longtext2'", "NULL", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testEnum() throws SQLException { + final String query = "SELECT \"enum_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 CHAR"), // + List.of("'1'", "'2'", "'3'", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testSet() throws SQLException { + final String query = "SELECT \"set_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 CHAR"), // + List.of("'1'", "'1'", "NULL", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testVarchar() throws SQLException { + final String query = "SELECT \"varchar_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 VARCHAR(100)"), // + List.of("'ab'", "'a'", "''", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testChar() throws SQLException { + final String query = "SELECT \"char_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_STRING_DATATYPES_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("col1 CHAR(255)"), // + List.of("'asd24'", "'11111'", "''", "NULL")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + } + + @Nested + @DisplayName("Join test") + class JoinTest { + @Test + void testInnerJoin() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a INNER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + " b ON a.\"x\"=b.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("2,'bbb', 2,'bbb'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testInnerJoinWithProjection() throws SQLException { + final String query = "SELECT b.\"y\" || " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + ".\"y\" FROM " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " INNER JOIN " + VIRTUAL_SCHEMA_JDBC + "." + + TABLE_JOIN_2 + " b ON " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + ".\"x\"=b.\"x\""; + final ResultSet expected = getExpectedResultSet(List.of("y VARCHAR(100)"), // + List.of("'bbbbbb'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testLeftJoin() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a LEFT OUTER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + " b ON a.\"x\"=b.\"x\" ORDER BY a.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("1, 'aaa', null, null", // + "2, 'bbb', 2, 'bbb'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testRightJoin() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a RIGHT OUTER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + " b ON a.\"x\"=b.\"x\" ORDER BY a.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("null, null, 3, 'ccc'", // + "2, 'bbb', 2, 'bbb'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testFullOuterJoin() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a FULL OUTER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + " b ON a.\"x\"=b.\"x\" ORDER BY a.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("1, 'aaa', null, null", // + "2, 'bbb', 2, 'bbb'", // + "null, null, 3, 'ccc'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testRightJoinWithComplexCondition() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a RIGHT OUTER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + + " b ON a.\"x\"||a.\"y\"=b.\"x\"||b.\"y\" ORDER BY a.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("null, null, 3, 'ccc'", // + "2, 'bbb', 2, 'bbb'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testFullOuterJoinWithComplexCondition() throws SQLException { + final String query = "SELECT * FROM " + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_1 + " a FULL OUTER JOIN " + + VIRTUAL_SCHEMA_JDBC + "." + TABLE_JOIN_2 + " b ON a.\"x\"-b.\"x\"=0 ORDER BY a.\"x\""; + final ResultSet expected = getExpectedResultSet( + List.of("x INT", "y VARCHAR(100)", "a INT", "b VARCHAR(100)"), // + List.of("1, 'aaa', null, null", // + "2, 'bbb', 2, 'bbb'", // + "null, null, 3, 'ccc'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + } + + @Test + void testAggregateGroupByColumn() throws SQLException { + final String query = "SELECT \"bool_col\", min(\"int_col\") FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_SIMPLE_TABLE + " GROUP BY \"bool_col\""; + final ResultSet expected = getExpectedResultSet(List.of("bool_col BOOLEAN", "int_col DECIMAL(10,0)"), + List.of("true, -100", // + "false, -1")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testAggregateHaving() throws SQLException { + final String query = "SELECT \"bool_col\", min(\"int_col\") FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_SIMPLE_TABLE + " GROUP BY \"bool_col\" HAVING MIN(\"int_col\") < 0"; + final ResultSet expected = getExpectedResultSet(List.of("bool_col BOOLEAN", "int_col DECIMAL(10,0)"), + List.of("true, -100", // + "false, -1")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + // =, !=, <, <=, >, >= + void testComparisonPredicates() throws SQLException { + final String query = "SELECT \"int_col\", \"int_col\" = 60, \"int_col\" != 60, \"int_col\" < 60, " + + "\"int_col\" <= 60, \"int_col\" > 60, \"int_col\" >= 60 FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_SIMPLE_TABLE + " WHERE \"int_col\" = 0"; + final ResultSet expected = getExpectedResultSet( + List.of("int_col DECIMAL(10,0)", "b1 DECIMAL(19,0)", "b2 DECIMAL(19,0)", "b3 DECIMAL(19,0)", + "b4 DECIMAL(19,0)", "b5 DECIMAL(19,0)", "b6 DECIMAL(19,0)"), // + List.of("0, 0, 1, 1, 1, 0, 0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + // NOT, AND, OR + void testLogicalPredicates() throws SQLException { + final String query = "SELECT \"int_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE + + " WHERE (\"int_col\" < 0 or \"int_col\" > 0) AND NOT (\"int_col\" is null)"; + final ResultSet expected = getExpectedResultSet(List.of("int_col DECIMAL(10,0)"), // + List.of("-100", // + "-1", // + "10", // + "50", // + "100")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + // LIKE, LIKE ESCAPE (not pushed down) + void testLikePredicates() throws SQLException { + final String query = "SELECT \"varchar_col\", \"varchar_col\" LIKE 'a%' ESCAPE 'a' FROM " + VIRTUAL_SCHEMA_JDBC + + "." + MYSQL_SIMPLE_TABLE + " WHERE (\"varchar_col\" LIKE 'a%')"; + final ResultSet expected = getExpectedResultSet(List.of("varchar_col VARCHAR(10)", "bool_col BOOLEAN"), + List.of("'a', false", // + "'abbb', false", // + "'abc', false", // + "'a', false")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + // BETWEEN, IN, IS NULL, !=NULL(rewritten to "IS NOT NULL") + void testMiscPredicates() throws SQLException { + final String query = "SELECT \"int_col\", \"int_col\" in (56, 61), \"int_col\" is null, \"int_col\" != null" + + " FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE + " WHERE \"int_col\" between -10 and 51"; + final ResultSet expected = getExpectedResultSet( + List.of("int_col DECIMAL(10,0)", "b1 BOOLEAN", "b2 BOOLEAN", "b3 BOOLEAN"), // + List.of("-1, false, false, false", // + "0, false, false, false", // + "10, false, false, false", // + "50, false, false, false")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testCountSumAggregateFunction() throws SQLException { + final String query = "SELECT COUNT(\"int_col\"), COUNT(*), COUNT(DISTINCT \"int_col\"), SUM(\"int_col\"), " + + "SUM(DISTINCT \"int_col\") FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE; + final ResultSet expected = getExpectedResultSet( + List.of("a DECIMAL(10,0)", "b DECIMAL(10,0)", "c DECIMAL(10,0)", "d DECIMAL(19,0)", "e DECIMAL(19,0)"), + List.of("6, 6, 6, 59, 59")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testAvgMinMaxAggregateFunction() throws SQLException { + final String query = "SELECT AVG(\"int_col\"), MIN(\"int_col\"), MAX(\"int_col\") FROM " + VIRTUAL_SCHEMA_JDBC + + "." + MYSQL_SIMPLE_TABLE; + final ResultSet expected = getExpectedResultSet( + List.of("a DECIMAL(14,4)", "b DECIMAL(10,0)", "c DECIMAL(10,0)"), // + List.of("9.8333, -100.0000, 100.0000")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testCastedStringFunctions() throws SQLException { + final String query = "SELECT concat(upper(\"varchar_col\"),lower(repeat(\"varchar_col\",2))) FROM " + + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("a VARCHAR(100)"), // + List.of("'Aaa'", // + "'ABBBabbbabbb'", // + "'Bbb'", // + "'BBBBbbbbbbbb'", // + "'ABCabcabc'", // + "'Aaa'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testRewrittenDivAndModFunctions() throws SQLException { + final String query = "SELECT DIV(\"int_col\",\"int_col\"), mod(\"int_col\",\"int_col\") FROM " + + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("a DECIMAL(19,0)", "b DECIMAL(19,0)"), // + List.of("1, 0", // + "1, 0", // + "null, null", // + "1, 0", // + "1, 0", // + "1, 0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testRewrittenSubStringFunction() throws SQLException { + final String query = "SELECT substring(\"varchar_col\" FROM 1 FOR 2) FROM " + VIRTUAL_SCHEMA_JDBC + "." + + MYSQL_SIMPLE_TABLE; + final ResultSet expected = getExpectedResultSet(List.of("a VARCHAR(100)"), // + List.of("'a'", // + "'ab'", // + "'b'", // + "'bb'", // + "'ab'", // + "'a'")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testOrderByLimit() throws SQLException { + final String query = "SELECT \"bool_col\", \"int_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE + + " ORDER BY \"int_col\" LIMIT 3"; + final ResultSet expected = getExpectedResultSet(List.of("a BOOLEAN", "b DECIMAL(10,0)"), // + List.of("true, -100", // + "false, -1", // + "true, 0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } + + @Test + void testOrderByLimitOffset() throws SQLException { + final String query = "SELECT \"bool_col\", \"int_col\" FROM " + VIRTUAL_SCHEMA_JDBC + "." + MYSQL_SIMPLE_TABLE + + " ORDER BY \"int_col\" LIMIT 2 OFFSET 1"; + final ResultSet expected = getExpectedResultSet(List.of("a BOOLEAN", "b DECIMAL(10,0)"), // + List.of("false, -1", // + "true, 0")); + assertThat(getActualResultSet(query), matchesResultSet(expected)); + } +} \ No newline at end of file diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectTest.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectTest.java new file mode 100644 index 0000000..a9d512e --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlDialectTest.java @@ -0,0 +1,155 @@ +package com.exasol.adapter.dialects.mysql; + +import static com.exasol.adapter.AdapterProperties.*; +import static com.exasol.adapter.capabilities.AggregateFunctionCapability.*; +import static com.exasol.adapter.capabilities.LiteralCapability.*; +import static com.exasol.adapter.capabilities.MainCapability.*; +import static com.exasol.adapter.capabilities.PredicateCapability.*; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.*; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.ST_INTERSECTION; +import static com.exasol.adapter.capabilities.ScalarFunctionCapability.ST_UNION; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; + +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.SqlDialect; +import com.exasol.adapter.jdbc.ConnectionFactory; + +@ExtendWith(MockitoExtension.class) +class MySQLSqlDialectTest { + private MySQLSqlDialect dialect; + @Mock + private ConnectionFactory connectionFactoryMock; + + @BeforeEach + void beforeEach() { + this.dialect = new MySQLSqlDialect(this.connectionFactoryMock, AdapterProperties.emptyProperties()); + } + + @Test + void testGetName() { + assertThat(this.dialect.getName(), equalTo("MYSQL")); + } + + @Test + void testGetMainCapabilities() { + assertThat(this.dialect.getCapabilities().getMainCapabilities(), + containsInAnyOrder(SELECTLIST_PROJECTION, SELECTLIST_EXPRESSIONS, FILTER_EXPRESSIONS, + AGGREGATE_SINGLE_GROUP, AGGREGATE_GROUP_BY_COLUMN, AGGREGATE_GROUP_BY_EXPRESSION, + AGGREGATE_GROUP_BY_TUPLE, AGGREGATE_HAVING, ORDER_BY_COLUMN, ORDER_BY_EXPRESSION, LIMIT, + LIMIT_WITH_OFFSET, JOIN, JOIN_TYPE_INNER, JOIN_TYPE_LEFT_OUTER, JOIN_TYPE_RIGHT_OUTER, + JOIN_CONDITION_EQUI)); + } + + @Test + void testGetLiteralCapabilities() { + assertThat(this.dialect.getCapabilities().getLiteralCapabilities(), + containsInAnyOrder(NULL, BOOL, DATE, TIMESTAMP, TIMESTAMP_UTC, DOUBLE, EXACTNUMERIC, STRING, INTERVAL)); + } + + @Test + void testGetPredicateCapabilities() { + assertThat(this.dialect.getCapabilities().getPredicateCapabilities(), containsInAnyOrder(AND, OR, NOT, EQUAL, + NOTEQUAL, LESS, LESSEQUAL, LIKE, BETWEEN, IS_NULL, IS_NOT_NULL)); + } + + @Test + void testGetScalarFunctionCapabilities() { + assertThat(this.dialect.getCapabilities().getScalarFunctionCapabilities(), containsInAnyOrder(ABS, ACOS, ASIN, + ATAN, ATAN2, CEIL, COS, COT, DEGREES, DIV, EXP, FLOOR, GREATEST, LEAST, LN, LOG, MOD, POWER, RADIANS, + RAND, ROUND, SIGN, SIN, SQRT, TAN, ASCII, BIT_LENGTH, CONCAT, INSERT, INSTR, LENGTH, LOCATE, LOWER, + LPAD, LTRIM, OCTET_LENGTH, REGEXP_INSTR, REGEXP_REPLACE, REGEXP_SUBSTR, REPEAT, REPLACE, REVERSE, RIGHT, + RPAD, RTRIM, SOUNDEX, SPACE, SUBSTR, TRIM, UPPER, ADD_DAYS, ADD_HOURS, ADD_MINUTES, ADD_MONTHS, + ADD_SECONDS, ADD_WEEKS, ADD_YEARS, CONVERT_TZ, CURRENT_DATE, CURRENT_TIMESTAMP, EXTRACT, LOCALTIMESTAMP, + MINUTE, MONTH, SECOND, SYSDATE, SYSTIMESTAMP, WEEK, YEAR, ST_X, ST_Y, ST_ENDPOINT, ST_ISCLOSED, + ST_LENGTH, ST_NUMPOINTS, ST_POINTN, ST_STARTPOINT, ST_AREA, ST_EXTERIORRING, ST_INTERIORRINGN, + ST_NUMINTERIORRINGS, ST_GEOMETRYN, ST_NUMGEOMETRIES, ST_BUFFER, ST_CENTROID, ST_CONTAINS, ST_CONVEXHULL, + ST_CROSSES, ST_DIFFERENCE, ST_DIMENSION, ST_DISJOINT, ST_DISTANCE, ST_ENVELOPE, ST_EQUALS, + ST_GEOMETRYTYPE, ST_INTERSECTION, ST_INTERSECTS, ST_ISEMPTY, ST_ISSIMPLE, ST_OVERLAPS, ST_SYMDIFFERENCE, + ST_TOUCHES, ST_TRANSFORM, ST_UNION, ST_WITHIN, CAST, BIT_AND, BIT_OR, BIT_XOR, CASE, CURRENT_USER)); + } + + @Test + void testGetAggregateFunctionCapabilities() { + assertThat(this.dialect.getCapabilities().getAggregateFunctionCapabilities(), containsInAnyOrder(COUNT, SUM, + MIN, MAX, AVG, STDDEV, STDDEV_POP, STDDEV_SAMP, VARIANCE, VAR_POP, VAR_SAMP)); + } + + @Test + void testSupportsJdbcCatalogs() { + assertThat(this.dialect.supportsJdbcCatalogs(), equalTo(SqlDialect.StructureElementSupport.MULTIPLE)); + } + + @Test + void testSupportsJdbcSchemas() { + assertThat(this.dialect.supportsJdbcSchemas(), equalTo(SqlDialect.StructureElementSupport.NONE)); + } + + @Test + void testRequiresCatalogQualifiedTableNames() { + assertThat(this.dialect.requiresCatalogQualifiedTableNames(null), equalTo(true)); + } + + @Test + void testRequiresSchemaQualifiedTableNames() { + assertThat(this.dialect.requiresSchemaQualifiedTableNames(null), equalTo(false)); + } + + @Test + void testGetDefaultNullSorting() { + assertThat(this.dialect.getDefaultNullSorting(), equalTo(SqlDialect.NullSorting.NULLS_SORTED_AT_END)); + } + + @CsvSource({ "tableName, `tableName`", // + "`tableName, ```tableName`", // + "\"tableName, `\"tableName`" // + }) + @ParameterizedTest + void testApplyQuote(final String unquoted, final String quoted) { + assertThat(this.dialect.applyQuote(unquoted), equalTo(quoted)); + } + + @ValueSource(strings = { "ab:'ab'", "a'b:'a''b'", "a''b:'a''''b'", "'ab':'''ab'''", "a\\b:'a\\\\b'", + "a\\\\b:'a\\\\\\\\b'", "a\\'b:'a\\\\''b'" }) + @ParameterizedTest + void testGetLiteralString(final String definition) { + assertThat(this.dialect.getStringLiteral(definition.substring(0, definition.indexOf(':'))), + Matchers.equalTo(definition.substring(definition.indexOf(':') + 1))); + } + + @Test + void testGetLiteralStringNull() { + assertThat(this.dialect.getStringLiteral(null), equalTo("NULL")); + } + + @Test + void testMetadataReaderClass() { + assertThat(this.dialect.createRemoteMetadataReader(), + instanceOf(MySQLMetadataReader.class)); + } + + @Test + void testGetSupportedProperties() { + assertThat(this.dialect.getSupportedProperties(), + containsInAnyOrder(SQL_DIALECT_PROPERTY, CONNECTION_NAME_PROPERTY, TABLE_FILTER_PROPERTY, + CATALOG_NAME_PROPERTY, EXCLUDED_CAPABILITIES_PROPERTY, DEBUG_ADDRESS_PROPERTY, + LOG_LEVEL_PROPERTY)); + } + + @Test + void testGetSqlGenerationVisitor() { + assertThat(this.dialect.getSqlGenerationVisitor(null), instanceOf(MySQLSqlGenerationVisitor.class)); + } +} diff --git a/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitorTest.java b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitorTest.java new file mode 100644 index 0000000..2cd94d4 --- /dev/null +++ b/src/test/java/com/exasol/adapter/dialects/mysql/MySQLSqlGenerationVisitorTest.java @@ -0,0 +1,42 @@ +package com.exasol.adapter.dialects.mysql; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import com.exasol.adapter.AdapterException; +import com.exasol.adapter.AdapterProperties; +import com.exasol.adapter.dialects.*; +import com.exasol.adapter.jdbc.ConnectionFactory; +import com.exasol.adapter.sql.*; + +@ExtendWith(MockitoExtension.class) +class MySQLSqlGenerationVisitorTest { + private SqlNodeVisitor visitor; + + @BeforeEach + void beforeEach(@Mock final ConnectionFactory connectionFactoryMock) { + final SqlDialectFactory dialectFactory = new MySQLSqlDialectFactory(); + final SqlDialect dialect = dialectFactory.createSqlDialect(connectionFactoryMock, + AdapterProperties.emptyProperties()); + final SqlGenerationContext context = new SqlGenerationContext("test_catalog", "test_schema", false); + this.visitor = new MySQLSqlGenerationVisitor(dialect, context); + } + + @Test + void testRewriteDivFunction() throws AdapterException { + final List arguments = new ArrayList<>(); + arguments.add(new SqlLiteralDouble(10.5)); + arguments.add(new SqlLiteralDouble(10.10)); + final SqlFunctionScalar sqlFunctionScalar = new SqlFunctionScalar(ScalarFunction.DIV, arguments); + assertThat(this.visitor.visit(sqlFunctionScalar), equalTo("10.5 DIV 10.1")); + } +} \ No newline at end of file diff --git a/src/test/resources/integration/driver/mysql/mysql.properties b/src/test/resources/integration/driver/mysql/mysql.properties new file mode 100644 index 0000000..06853d2 --- /dev/null +++ b/src/test/resources/integration/driver/mysql/mysql.properties @@ -0,0 +1,2 @@ +driver.name=mysql-connector-java-8.0.20.jar +driver.path=src/test/resources/integration/driver/mysql \ No newline at end of file diff --git a/src/test/resources/integration/driver/mysql/settings.cfg b/src/test/resources/integration/driver/mysql/settings.cfg new file mode 100644 index 0000000..cd7a39e --- /dev/null +++ b/src/test/resources/integration/driver/mysql/settings.cfg @@ -0,0 +1,7 @@ +DRIVERNAME=MYSQL +JAR=mysql-connector-java-8.0.20.jar +DRIVERMAIN=com.mysql.jdbc.Driver +PREFIX=jdbc:mysql: +NOSECURITY=YES +FETCHSIZE=100000 +INSERTSIZE=-1 diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties new file mode 100644 index 0000000..ad3bc9a --- /dev/null +++ b/src/test/resources/logging.properties @@ -0,0 +1,6 @@ +handlers=java.util.logging.ConsoleHandler +.level=INFO +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=%1$tF %1$tT.%1$tL [%4$-7s] %5$s %n +com.exasol=ALL \ No newline at end of file diff --git a/versionsMavenPluginRules.xml b/versionsMavenPluginRules.xml new file mode 100644 index 0000000..35bd03d --- /dev/null +++ b/versionsMavenPluginRules.xml @@ -0,0 +1,18 @@ + + + + + (?i).*Alpha(?:-?[\d.]+)? + (?i).*a(?:-?[\d.]+)? + (?i).*Beta(?:-?[\d.]+)? + (?i).*-B(?:-?[\d.]+)? + (?i).*-b(?:-?[\d.]+)? + (?i).*RC(?:-?[\d.]+)? + (?i).*CR(?:-?[\d.]+)? + (?i).*M(?:-?[\d.]+)? + + + + \ No newline at end of file From a8a3735db10342219a6a302673e384c6e031fafa Mon Sep 17 00:00:00 2001 From: chiaradiamarcelo Date: Mon, 11 Jan 2021 09:21:06 +0100 Subject: [PATCH 2/3] Update doc/changes/changes_1.0.0.md Co-authored-by: jakobbraun --- doc/changes/changes_1.0.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/changes_1.0.0.md b/doc/changes/changes_1.0.0.md index 50f33ac..fd46c5d 100644 --- a/doc/changes/changes_1.0.0.md +++ b/doc/changes/changes_1.0.0.md @@ -1,4 +1,4 @@ -#Virtual Schema for MySQL 1.0.0, released 2021-??-?? +# Virtual Schema for MySQL 1.0.0, released 2021-??-?? Code name: From 6b9521bfc443f2043792daae149f38ca9f4f6a08 Mon Sep 17 00:00:00 2001 From: Marcelo Chiaradia Date: Mon, 11 Jan 2021 09:28:44 +0100 Subject: [PATCH 3/3] Review findings --- README.md | 6 +++--- doc/{dialects => user_guide}/mysql_user_guide.md | 0 pom.xml | 2 +- src/test/resources/logging.properties | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename doc/{dialects => user_guide}/mysql_user_guide.md (100%) diff --git a/README.md b/README.md index ce55792..c6607ba 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ If you want to set up a Virtual Schema for a different database system, please h ### Information for Users -* [Virtual Schemas User Guide][user-guide] -* [MySQL Dialect User Guide](doc/dialects/mysql_user_guide.md) +* [Virtual Schemas User Guide][virtual-schemas-user-guide] +* [MySQL Dialect User Guide](doc/user_guide/mysql_user_guide.md) * [Changelog](doc/changes/changelog.md) Find all the documentation in the [Virtual Schemas project][vs-doc]. @@ -102,7 +102,7 @@ Running the Virtual Schema requires a Java Runtime version 11 or later. [project-keeper-maven-plugin]: https://github.com/exasol/project-keeper-maven-plugin [sonatype-oss-index-maven-plugin]: https://sonatype.github.io/ossindex-maven/maven-plugin/ -[user-guide]: https://docs.exasol.com/database_concepts/virtual_schemas.htm +[virtual-schemas-user-guide]: https://docs.exasol.com/database_concepts/virtual_schemas.htm [virtual-schemas]: https://github.com/exasol/virtual-schemas [vs-api]: https://github.com/exasol/virtual-schema-common-java/blob/master/doc/development/api/virtual_schema_api.md [vs-doc]: https://github.com/exasol/virtual-schemas/tree/master/doc diff --git a/doc/dialects/mysql_user_guide.md b/doc/user_guide/mysql_user_guide.md similarity index 100% rename from doc/dialects/mysql_user_guide.md rename to doc/user_guide/mysql_user_guide.md diff --git a/pom.xml b/pom.xml index 0221829..117cd66 100644 --- a/pom.xml +++ b/pom.xml @@ -296,7 +296,7 @@ com.exasol project-keeper-maven-plugin - 0.4.1 + 0.4.2 diff --git a/src/test/resources/logging.properties b/src/test/resources/logging.properties index ad3bc9a..8c97abe 100644 --- a/src/test/resources/logging.properties +++ b/src/test/resources/logging.properties @@ -3,4 +3,4 @@ handlers=java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.level=ALL java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.SimpleFormatter.format=%1$tF %1$tT.%1$tL [%4$-7s] %5$s %n -com.exasol=ALL \ No newline at end of file +com.exasol.level=ALL