From ae40bd5c276500ab686fa94fe99559492c4598c7 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Mon, 9 May 2016 12:12:32 +0200 Subject: [PATCH] [#5245] Add org.jooq.Allow and org.jooq.Require annotation and a SQLDialectChecker using JSR-308 and the checker framework --- jOOQ-checker/.gitignore | 3 + jOOQ-checker/LICENSE.txt | 22 +++ jOOQ-checker/pom.xml | 45 +++++ .../org/jooq/checker/SQLDialectChecker.java | 156 ++++++++++++++++ .../java/org/jooq/checker/package-info.java | 9 + .../src/main/resources/META-INF/LICENSE.txt | 22 +++ .../src/main/resources/META-INF/README.txt | 2 + .../jOOQ-checker-framework-example/.gitignore | 2 + .../LICENSE.txt | 22 +++ .../jOOQ-checker-framework-example/README.md | 12 ++ .../jOOQ-checker-framework-example/output.txt | 37 ++++ .../jOOQ-checker-framework-example/pom.xml | 71 +++++++ .../jooq/example/checker/CheckerTests.java | 35 ++++ .../jooq/example/checker/ShouldntCompile.java | 47 +++++ .../jooq/example/checker/package-info.java | 11 ++ .../db/migration/V1__initialise_database.sql | 3 + .../db/migration/V2__create_author_table.sql | 12 ++ .../V3__create_book_table_and_add_records.sql | 17 ++ .../src/main/resources/log4j.xml | 14 ++ jOOQ/src/main/java/org/jooq/Allow.java | 175 ++++++++++++++++++ jOOQ/src/main/java/org/jooq/Require.java | 129 +++++++++++++ 21 files changed, 846 insertions(+) create mode 100644 jOOQ-checker/.gitignore create mode 100644 jOOQ-checker/LICENSE.txt create mode 100644 jOOQ-checker/pom.xml create mode 100644 jOOQ-checker/src/main/java/org/jooq/checker/SQLDialectChecker.java create mode 100644 jOOQ-checker/src/main/java/org/jooq/checker/package-info.java create mode 100644 jOOQ-checker/src/main/resources/META-INF/LICENSE.txt create mode 100644 jOOQ-checker/src/main/resources/META-INF/README.txt create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/.gitignore create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/LICENSE.txt create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/README.md create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/output.txt create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/pom.xml create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/CheckerTests.java create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/ShouldntCompile.java create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/package-info.java create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V1__initialise_database.sql create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V2__create_author_table.sql create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V3__create_book_table_and_add_records.sql create mode 100644 jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/log4j.xml create mode 100644 jOOQ/src/main/java/org/jooq/Allow.java create mode 100644 jOOQ/src/main/java/org/jooq/Require.java diff --git a/jOOQ-checker/.gitignore b/jOOQ-checker/.gitignore new file mode 100644 index 0000000000..e8e5bad2d2 --- /dev/null +++ b/jOOQ-checker/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +/*.iml \ No newline at end of file diff --git a/jOOQ-checker/LICENSE.txt b/jOOQ-checker/LICENSE.txt new file mode 100644 index 0000000000..7ab3d18350 --- /dev/null +++ b/jOOQ-checker/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) +All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Other licenses: +----------------------------------------------------------------------------- +Commercial licenses for this work are available. These replace the above +ASL 2.0 and offer limited warranties, support, maintenance, and commercial +database integrations. + +For more information, please visit: http://www.jooq.org/licenses \ No newline at end of file diff --git a/jOOQ-checker/pom.xml b/jOOQ-checker/pom.xml new file mode 100644 index 0000000000..d132b3f452 --- /dev/null +++ b/jOOQ-checker/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + + org.jooq + jooq-parent + 3.9.0-SNAPSHOT + + + org.jooq + jooq-checker + jOOQ Checker + + + + Apache License, Version 2.0 + http://www.jooq.org/inc/LICENSE.txt + repo + + + + + + + + + + + + + org.jooq + jooq + + + + org.checkerframework + checker + 2.0.0 + + + + \ No newline at end of file diff --git a/jOOQ-checker/src/main/java/org/jooq/checker/SQLDialectChecker.java b/jOOQ-checker/src/main/java/org/jooq/checker/SQLDialectChecker.java new file mode 100644 index 0000000000..4581fbfb35 --- /dev/null +++ b/jOOQ-checker/src/main/java/org/jooq/checker/SQLDialectChecker.java @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.checker; + +import static com.sun.source.util.TreePath.getPath; +import static java.util.Arrays.asList; +import static org.checkerframework.javacutil.TreeUtils.elementFromDeclaration; +import static org.checkerframework.javacutil.TreeUtils.elementFromUse; +import static org.checkerframework.javacutil.TreeUtils.enclosingMethod; + +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.EnumSet; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; + +import org.jooq.Allow; +import org.jooq.Require; +import org.jooq.SQLDialect; +import org.jooq.Support; + +import org.checkerframework.framework.source.Result; +import org.checkerframework.framework.source.SourceChecker; +import org.checkerframework.framework.source.SourceVisitor; + +import com.sun.source.tree.MethodInvocationTree; + +/** + * A checker to compare {@link SQLDialect} from a use-site {@link Require} + * annotation with a declaration-site {@link Support} annotation. + * + * @author Lukas Eder + */ +public class SQLDialectChecker extends SourceChecker { + + @Override + protected SourceVisitor createSourceVisitor() { + return new SourceVisitor(this) { + + @Override + public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + try { + ExecutableElement elementFromUse = elementFromUse(node); + Support support = elementFromUse.getAnnotation(Support.class); + + // In the absence of a @Support annotation, or if no SQLDialect is supplied, + // all jOOQ API method calls will type check. + if (support != null && support.value().length > 0) { + Element enclosing = elementFromDeclaration(enclosingMethod(getPath(root, node))); + + EnumSet supported = EnumSet.copyOf(asList(support.value())); + EnumSet allowed = EnumSet.noneOf(SQLDialect.class); + EnumSet required = EnumSet.allOf(SQLDialect.class); + EnumSet x; + + while (enclosing != null) { + Allow allow = enclosing.getAnnotation(Allow.class); + Require require = enclosing.getAnnotation(Require.class); + + if (allow != null) + allowed.addAll(asList(allow.value())); + + if (require != null) + required.retainAll(asList(require.value())); + + enclosing = enclosing.getEnclosingElement(); + } + + if (allowed.isEmpty()) + error(node, "No jOOQ API usage is allowed at current scope. Use @Allow."); + + if (required.isEmpty()) + error(node, "No jOOQ API usage is allowed at current scope due to conflicting @Require specification."); + + x = EnumSet.copyOf(allowed); + x.retainAll(supported); + if (x.isEmpty()) + error(node, "None of the supported dialects (" + supported + ") are allowed in the current scope (" + allowed + ")"); + + if (!supported.containsAll(required)) + error(node, "Not all of the required dialects (" + required + ") from the current scope are supported (" + supported + ")"); + } + } + catch (final Exception e) { + print(new Printer() { + @Override + public void print(PrintWriter t) { + e.printStackTrace(t); + } + }); + } + + return super.visitMethodInvocation(node, p); + } + }; + } + + void error(Object node, String message) { + getChecker().report(Result.failure(message, node), node); + } + + void print(Printer printer) { + try (PrintWriter writer = new PrintWriter(new FileWriter("error.txt"))){ + writer.println("This is probably a bug in jOOQ-checker."); + writer.println("Please report this bug here: https://github.com/jOOQ/jOOQ/issues/new"); + writer.println("---------------------------------------------------------------------"); + + printer.print(writer); + } + catch (IOException ignore) {} + } + + interface Printer { + void print(PrintWriter writer); + } +} diff --git a/jOOQ-checker/src/main/java/org/jooq/checker/package-info.java b/jOOQ-checker/src/main/java/org/jooq/checker/package-info.java new file mode 100644 index 0000000000..67d1a9a0ac --- /dev/null +++ b/jOOQ-checker/src/main/java/org/jooq/checker/package-info.java @@ -0,0 +1,9 @@ +/** + * This package contains useful checkers that work with JSR 308 and the checker + * framework. + * + * @author Lukas Eder + * @see http://types. + * cs.washington.edu/checker-framework + */ +package org.jooq.checker; diff --git a/jOOQ-checker/src/main/resources/META-INF/LICENSE.txt b/jOOQ-checker/src/main/resources/META-INF/LICENSE.txt new file mode 100644 index 0000000000..7ab3d18350 --- /dev/null +++ b/jOOQ-checker/src/main/resources/META-INF/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) +All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Other licenses: +----------------------------------------------------------------------------- +Commercial licenses for this work are available. These replace the above +ASL 2.0 and offer limited warranties, support, maintenance, and commercial +database integrations. + +For more information, please visit: http://www.jooq.org/licenses \ No newline at end of file diff --git a/jOOQ-checker/src/main/resources/META-INF/README.txt b/jOOQ-checker/src/main/resources/META-INF/README.txt new file mode 100644 index 0000000000..3e6e227e05 --- /dev/null +++ b/jOOQ-checker/src/main/resources/META-INF/README.txt @@ -0,0 +1,2 @@ +Thanks for downloading jOOQ. +Please visit http://www.jooq.org for more information. \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/.gitignore b/jOOQ-examples/jOOQ-checker-framework-example/.gitignore new file mode 100644 index 0000000000..0fd7a02083 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/.gitignore @@ -0,0 +1,2 @@ +/target +/jooq-flyway-example.iml diff --git a/jOOQ-examples/jOOQ-checker-framework-example/LICENSE.txt b/jOOQ-examples/jOOQ-checker-framework-example/LICENSE.txt new file mode 100644 index 0000000000..7ab3d18350 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) +All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Other licenses: +----------------------------------------------------------------------------- +Commercial licenses for this work are available. These replace the above +ASL 2.0 and offer limited warranties, support, maintenance, and commercial +database integrations. + +For more information, please visit: http://www.jooq.org/licenses \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/README.md b/jOOQ-examples/jOOQ-checker-framework-example/README.md new file mode 100644 index 0000000000..c97f65e0e9 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/README.md @@ -0,0 +1,12 @@ +Thanks for downloading jOOQ. +Please visit http://www.jooq.org for more information. + +To install and run this example, simply check it out and run the following Maven command + +``` +$ pwd +/path/to/checkout/dir +$ cd jOOQ-examples/jOOQ-flyway-example +... +$ mvn clean install +``` \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/output.txt b/jOOQ-examples/jOOQ-checker-framework-example/output.txt new file mode 100644 index 0000000000..7878b3db8c --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/output.txt @@ -0,0 +1,37 @@ +java.lang.NullPointerException + at org.jooq.checker.SQLDialectChecker$1.visitMethodInvocation(SQLDialectChecker.java:101) + at org.jooq.checker.SQLDialectChecker$1.visitMethodInvocation(SQLDialectChecker.java:79) + at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1477) + at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:68) + at com.sun.source.util.TreeScanner.visitExpressionStatement(TreeScanner.java:243) + at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1302) + at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:68) + at com.sun.source.util.TreeScanner.scan(TreeScanner.java:91) + at com.sun.source.util.TreeScanner.visitBlock(TreeScanner.java:162) + at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:918) + at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:68) + at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:81) + at com.sun.source.util.TreeScanner.visitMethod(TreeScanner.java:144) + at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:800) + at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:68) + at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:81) + at com.sun.source.util.TreeScanner.scan(TreeScanner.java:91) + at com.sun.source.util.TreeScanner.scanAndReduce(TreeScanner.java:99) + at com.sun.source.util.TreeScanner.visitClass(TreeScanner.java:133) + at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:720) + at com.sun.source.util.TreePathScanner.scan(TreePathScanner.java:50) + at org.checkerframework.framework.source.SourceVisitor.visit(SourceVisitor.java:70) + at org.checkerframework.framework.source.SourceChecker.typeProcess(SourceChecker.java:939) + at org.checkerframework.javacutil.AbstractTypeProcessor$AttributionTaskListener.finished(AbstractTypeProcessor.java:209) + at com.sun.tools.javac.api.ClientCodeWrapper$WrappedTaskListener.finished(ClientCodeWrapper.java:681) + at com.sun.tools.javac.api.MultiTaskListener.finished(MultiTaskListener.java:111) + at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1342) + at com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1296) + at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:901) + at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:860) + at com.sun.tools.javac.main.Main.compile(Main.java:523) + at com.sun.tools.javac.main.Main.compile(Main.java:381) + at com.sun.tools.javac.main.Main.compile(Main.java:370) + at com.sun.tools.javac.main.Main.compile(Main.java:361) + at com.sun.tools.javac.Main.compile(Main.java:56) + at com.sun.tools.javac.Main.main(Main.java:42) diff --git a/jOOQ-examples/jOOQ-checker-framework-example/pom.xml b/jOOQ-examples/jOOQ-checker-framework-example/pom.xml new file mode 100644 index 0000000000..fd8f99e620 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.jooq + jooq-checker-framework-example + 3.9.0-SNAPSHOT + jOOQ Checker Framework Example + + + + Apache License, Version 2.0 + http://www.jooq.org/inc/LICENSE.txt + repo + + + + + UTF-8 + 3.9.0-SNAPSHOT + + + + + + + org.jooq + jooq + ${org.jooq.version} + + + org.jooq + jooq-checker + ${org.jooq.version} + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.3 + + + + properties + + + + + + + maven-compiler-plugin + 3.3 + + 1.8 + 1.8 + true + + org.jooq.checker.SQLDialectChecker + + + -Xbootclasspath/p:1.8 + + + + + + \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/CheckerTests.java b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/CheckerTests.java new file mode 100644 index 0000000000..e9cdeae073 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/CheckerTests.java @@ -0,0 +1,35 @@ +package org.jooq.example.checker; + +import static org.jooq.SQLDialect.H2; + +import org.jooq.Allow; +import org.jooq.Require; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; + +// The class requires both H2 and MySQL +// The inherited @Allow annotation from the package allows only MySQL, though. +@Require({ SQLDialect.H2, SQLDialect.MYSQL }) +public class CheckerTests { + + // @Allow = MySQL (inherited from package) + // @Require = { H2, MySQL } (inherited from class) + public static void doesntCompileBecauseH2IsNotAllowedAndMySQLIsNotSupported() { + DSL.array(2); + } + + // @Allow = { H2, MySQL (inherited from package) } + // @Require = { H2, MySQL } (inherited from class) + @Allow(H2) + public static void doesntCompileBecauseMySQLIsNotSupported() { + DSL.array(2); + } + + // @Allow = { H2, MySQL (inherited from package) } + // @Require = { H2, MySQL } (inherited from class) + @Allow(H2) + @Require(H2) + public static void compiles() { + DSL.array(2); + } +} diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/ShouldntCompile.java b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/ShouldntCompile.java new file mode 100644 index 0000000000..599b3a33ac --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/ShouldntCompile.java @@ -0,0 +1,47 @@ +package org.jooq.example.checker; +import static org.jooq.example.checker.db.h2.Tables.AUTHOR; +import static org.jooq.example.checker.db.h2.Tables.BOOK; + +import java.sql.Connection; +import java.sql.DriverManager; + +import org.jooq.Require; +import org.jooq.Result; +import org.jooq.SQLDialect; +import org.jooq.impl.DSL; + +/** + * @author Lukas Eder + */ +@Require( + value = { SQLDialect.H2, SQLDialect.MYSQL } +) +public class ShouldntCompile { + + @Require({SQLDialect.H2, SQLDialect.MYSQL}) + public static void main(String[] args) throws Exception { + try (Connection c = DriverManager.getConnection("jdbc:h2:~/checker-test", "sa", "")) { + Result result = + DSL.using(c) + .select( + AUTHOR.FIRST_NAME, + AUTHOR.LAST_NAME, + BOOK.ID, + BOOK.TITLE, + DSL.array(1) + ) + .from(AUTHOR) + .join(BOOK) + .on(AUTHOR.ID.eq(BOOK.AUTHOR_ID)) + .connectBy("abc") + .orderBy(BOOK.ID.asc()) + .fetch(); + + System.out.println(result); + } + } + + public static void x() { + DSL.array(2); + } +} diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/package-info.java b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/package-info.java new file mode 100644 index 0000000000..24ebfb91ea --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/java/org/jooq/example/checker/package-info.java @@ -0,0 +1,11 @@ +/** + * Enable jOOQ API usage with MySQL for the whole package. + * + * @author Lukas Eder + */ +@Allow(MYSQL) +package org.jooq.example.checker; + +import static org.jooq.SQLDialect.MYSQL; + +import org.jooq.Allow; diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V1__initialise_database.sql b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V1__initialise_database.sql new file mode 100644 index 0000000000..ae39dfc707 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V1__initialise_database.sql @@ -0,0 +1,3 @@ +DROP SCHEMA IF EXISTS checker; + +CREATE SCHEMA checker; \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V2__create_author_table.sql b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V2__create_author_table.sql new file mode 100644 index 0000000000..480c50d2e8 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V2__create_author_table.sql @@ -0,0 +1,12 @@ +CREATE SEQUENCE checker.s_author_id START WITH 1; + +CREATE TABLE checker.author ( + id INT NOT NULL, + first_name VARCHAR(50), + last_name VARCHAR(50) NOT NULL, + date_of_birth DATE, + year_of_birth INT, + address VARCHAR(50), + + CONSTRAINT pk_t_author PRIMARY KEY (ID) +); \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V3__create_book_table_and_add_records.sql b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V3__create_book_table_and_add_records.sql new file mode 100644 index 0000000000..fa0b3c690f --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/db/migration/V3__create_book_table_and_add_records.sql @@ -0,0 +1,17 @@ +CREATE TABLE checker.book ( + id INT NOT NULL, + author_id INT NOT NULL, + title VARCHAR(400) NOT NULL, + + CONSTRAINT pk_t_book PRIMARY KEY (id), + CONSTRAINT fk_t_book_author_id FOREIGN KEY (author_id) REFERENCES author(id) +); + + +INSERT INTO checker.author VALUES (next value for checker.s_author_id, 'George', 'Orwell', '1903-06-25', 1903, null); +INSERT INTO checker.author VALUES (next value for checker.s_author_id, 'Paulo', 'Coelho', '1947-08-24', 1947, null); + +INSERT INTO checker.book VALUES (1, 1, '1984'); +INSERT INTO checker.book VALUES (2, 1, 'Animal Farm'); +INSERT INTO checker.book VALUES (3, 2, 'O Alquimista'); +INSERT INTO checker.book VALUES (4, 2, 'Brida'); \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/log4j.xml b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/log4j.xml new file mode 100644 index 0000000000..617678bd22 --- /dev/null +++ b/jOOQ-examples/jOOQ-checker-framework-example/src/main/resources/log4j.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/Allow.java b/jOOQ/src/main/java/org/jooq/Allow.java new file mode 100644 index 0000000000..8f07cf070d --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Allow.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +// ... +// ... +import static org.jooq.SQLDialect.CUBRID; +// ... +import static org.jooq.SQLDialect.DEFAULT; +import static org.jooq.SQLDialect.DERBY; +import static org.jooq.SQLDialect.FIREBIRD; +import static org.jooq.SQLDialect.H2; +// ... +import static org.jooq.SQLDialect.HSQLDB; +// ... +// ... +import static org.jooq.SQLDialect.MARIADB; +import static org.jooq.SQLDialect.MYSQL; +// ... +import static org.jooq.SQLDialect.POSTGRES; +// ... +import static org.jooq.SQLDialect.SQL99; +import static org.jooq.SQLDialect.SQLITE; +// ... +// ... +// ... + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Allow a set of {@link SQLDialect} to be supported by any jOOQ statement in + * the scope of this annotation. + *

+ * This annotation can be used at the use-site of jOOQ API at any given scope + * {@link ElementType#PACKAGE}, {@link ElementType#TYPE}, + * {@link ElementType#METHOD} in order to specify that the given scope allows + * ANY of the supplied {@link SQLDialect} to be supported by all usage of jOOQ + * API within the scope. For example: + *

+ *

+ * // Allow only MYSQL or ORACLE dialect support to be used within the class scope
+ * @Allow(MYSQL, ORACLE)
+ * public class MySQLAndOracleDAO {
+ *
+ *     // Allow rule from class applies to this method
+ *     public void mysqlAndOracleMethod() {
+ *         DSL.using(configuration)
+ *            .insertInto(TABLE, TABLE.COLUMN)
+ *            .values(1)
+ *            // This type checks as it works on both MySQL and Oracle
+ *            .onDuplicateKeyUpdate()
+ *            .set(TABLE.COLUMN, 2)
+ *            .execute();
+ *     }
+ *
+ *     // Refine class Allow rule with additional requirement
+ *     @Require(ORACLE)
+ *     public void oracleOnlyMethod() {
+ *         DSL.using(configuration)
+ *            .mergeInto(TABLE)
+ *            .using(selectOne())
+ *            .on(TABLE.COLUMN.eq(1))
+ *            .whenMatchedThenUpdate()
+ *            .set(TABLE.COLUMN, 2)
+ *            .whenNotMatchedThenInsert(TABLE.COLUMN)
+ *            .values(1)
+ *            .execute();
+ *     }
+ * }
+ * 
+ *

+ * Type checking for these annotations can be supplied by + * org.jooq.checker.SQLDialectChecker from the jOOQ-checker module. + *

Rules:

+ *
    + *
  • In the absence of any {@link Allow} annotation, no jOOQ API usage is + * allowed.
  • + *
  • The combination of all {@link Allow} and {@link Require} annotations is + * applied for any given scope.
  • + *
  • Nested packages are not creating nested scopes.
  • + *
  • If a versioned {@link SQLDialect} is allowed (rather than a + * {@link SQLDialect#family()}), then the allowed version, all of its + * {@link SQLDialect#predecessor()}, and its {@link SQLDialect#family()} are + * allowed.
  • + *
+ * + * @author Lukas Eder + * @see Require + */ +@Target({ METHOD, CONSTRUCTOR, TYPE, PACKAGE }) +@Retention(RUNTIME) +@Documented +@Inherited +public @interface Allow { + + /** + * A list of jOOQ {@link SQLDialect} which are required on any jOOQ API + * method that is annotated with {@link Support}. + */ + @SuppressWarnings("deprecation") + SQLDialect[] value() default { + + + + + + + + + + + + + + + CUBRID, + DEFAULT, + SQL99, + DERBY, + FIREBIRD, + H2, + HSQLDB, + MARIADB, + MYSQL, + POSTGRES, + SQLITE, + }; +} diff --git a/jOOQ/src/main/java/org/jooq/Require.java b/jOOQ/src/main/java/org/jooq/Require.java new file mode 100644 index 0000000000..f4ac9034f0 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Require.java @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com) + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PACKAGE; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Require a set of {@link SQLDialect} to be supported by any jOOQ statement in + * the scope of this annotation. + *

+ * This annotation can be used at the use-site of jOOQ API at any given scope + * {@link ElementType#PACKAGE}, {@link ElementType#TYPE}, + * {@link ElementType#METHOD} in order to specify that the given scope requires + * ALL of the supplied {@link SQLDialect} to be supported by all usage of jOOQ + * API within the scope. For example: + *

+ *

+ * // Allow only MYSQL or ORACLE dialect support to be used within the class scope
+ * @Allow(MYSQL, ORACLE)
+ * public class MySQLAndOracleDAO {
+ *
+ *     // Allow rule from class applies to this method
+ *     public void mysqlAndOracleMethod() {
+ *         DSL.using(configuration)
+ *            .insertInto(TABLE, TABLE.COLUMN)
+ *            .values(1)
+ *            // This type checks as it works on both MySQL and Oracle
+ *            .onDuplicateKeyUpdate()
+ *            .set(TABLE.COLUMN, 2)
+ *            .execute();
+ *     }
+ *
+ *     // Refine class Allow rule with additional requirement
+ *     @Require(ORACLE)
+ *     public void oracleOnlyMethod() {
+ *         DSL.using(configuration)
+ *            .mergeInto(TABLE)
+ *            .using(selectOne())
+ *            .on(TABLE.COLUMN.eq(1))
+ *            .whenMatchedThenUpdate()
+ *            .set(TABLE.COLUMN, 2)
+ *            .whenNotMatchedThenInsert(TABLE.COLUMN)
+ *            .values(1)
+ *            .execute();
+ *     }
+ * }
+ * 
+ *

+ * Type checking for these annotations can be supplied by + * org.jooq.checker.SQLDialectChecker from the jOOQ-checker module. + *

+ * Type checking for these annotations can be supplied by + * org.jooq.checker.SQLDialectChecker from the jOOQ-checker module. + *

Rules:

+ *
    + *
  • In the absence of any {@link Allow} annotation, no jOOQ API usage is + * allowed.
  • + *
  • The combination of all {@link Allow} and {@link Require} annotations is + * applied for any given scope.
  • + *
  • Nested packages are not creating nested scopes.
  • + *
  • If a versioned {@link SQLDialect} is required (rather than a + * {@link SQLDialect#family()}), then the required version, any of its + * {@link SQLDialect#predecessor()}, or its {@link SQLDialect#family()} are + * required.
  • + *
+ * + * @author Lukas Eder + * @see Allow + */ +@Target({ METHOD, CONSTRUCTOR, TYPE, PACKAGE }) +@Retention(RUNTIME) +@Documented +@Inherited +public @interface Require { + + /** + * A list of jOOQ {@link SQLDialect} which are required on any jOOQ API + * method that is annotated with {@link Support}. + */ + SQLDialect[] value() default {}; +}