diff --git a/java/ql/lib/change-notes/2022-03-15-mybatis-providers.md b/java/ql/lib/change-notes/2022-03-15-mybatis-providers.md new file mode 100644 index 000000000000..32ba9c23c127 --- /dev/null +++ b/java/ql/lib/change-notes/2022-03-15-mybatis-providers.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added modeling of MyBatis (`org.apache.ibatis`) Providers, resulting in additional sinks for the queries `java/ognl-injection`, `java/sql-injection`, `java/sql-injection-local` and `java/concatenated-sql-query`. \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/frameworks/MyBatis.qll b/java/ql/lib/semmle/code/java/frameworks/MyBatis.qll index ebeb0c54e692..b6601e6de088 100644 --- a/java/ql/lib/semmle/code/java/frameworks/MyBatis.qll +++ b/java/ql/lib/semmle/code/java/frameworks/MyBatis.qll @@ -3,6 +3,8 @@ */ import java +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.dataflow.TaintTracking private import semmle.code.java.dataflow.ExternalFlow /** The class `org.apache.ibatis.jdbc.SqlRunner`. */ @@ -102,3 +104,116 @@ class MyBatisSqlOperationAnnotationMethod extends Method { class TypeParam extends Interface { TypeParam() { this.hasQualifiedName("org.apache.ibatis.annotations", "Param") } } + +private class MyBatisProvider extends RefType { + MyBatisProvider() { + this.hasQualifiedName("org.apache.ibatis.annotations", + ["Select", "Delete", "Insert", "Update"] + "Provider") + } +} + +/** + * A return statement of a method used in a MyBatis Provider. + * + * See + * - `MyBatisProvider` + * - https://mybatis.org/mybatis-3/apidocs/org/apache/ibatis/annotations/package-summary.html + */ +class MyBatisInjectionSink extends DataFlow::Node { + MyBatisInjectionSink() { + exists(Annotation a, Method m | + a.getType() instanceof MyBatisProvider and + m.getDeclaringType() = a.getValue(["type", "value"]).(TypeLiteral).getTypeName().getType() and + m.hasName(a.getValue("method").(StringLiteral).getValue()) and + exists(ReturnStmt ret | this.asExpr() = ret.getResult() and ret.getEnclosingCallable() = m) + ) + } +} + +private class MyBatisProviderStep extends TaintTracking::AdditionalValueStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(MethodAccess ma, Annotation a, Method providerMethod | + exists(int i | + ma.getArgument(pragma[only_bind_into](i)) = n1.asExpr() and + providerMethod.getParameter(pragma[only_bind_into](i)) = n2.asParameter() + ) + | + a.getType() instanceof MyBatisProvider and + ma.getMethod().getAnAnnotation() = a and + providerMethod.getDeclaringType() = + a.getValue(["type", "value"]).(TypeLiteral).getTypeName().getType() and + providerMethod.hasName(a.getValue("method").(StringLiteral).getValue()) + ) + } +} + +private class MyBatisAbstractSqlToStringStep extends SummaryModelCsv { + override predicate row(string row) { + row = "org.apache.ibatis.jdbc;AbstractSQL;true;toString;;;Argument[-1];ReturnValue;taint" + } +} + +private class MyBatisAbstractSqlMethodsStep extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "org.apache.ibatis.jdbc;AbstractSQL;true;toString;;;Argument[-1];ReturnValue;taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;WHERE;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;WHERE;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;WHERE;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;WHERE;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;VALUES;(String,String);;Argument[0..1];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;UPDATE;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SET;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SET;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SET;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SET;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SELECT_DISTINCT;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SELECT_DISTINCT;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SELECT_DISTINCT;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SELECT_DISTINCT;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;SELECT;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;RIGHT_OUTER_JOIN;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;RIGHT_OUTER_JOIN;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;RIGHT_OUTER_JOIN;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;RIGHT_OUTER_JOIN;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OUTER_JOIN;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OUTER_JOIN;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OUTER_JOIN;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OUTER_JOIN;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;ORDER_BY;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;ORDER_BY;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;ORDER_BY;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;ORDER_BY;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OFFSET_ROWS;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;OFFSET;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;LIMIT;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;LEFT_OUTER_JOIN;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;LEFT_OUTER_JOIN;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;LEFT_OUTER_JOIN;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;LEFT_OUTER_JOIN;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;JOIN;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INTO_VALUES;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INTO_COLUMNS;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INSERT_INTO;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INNER_JOIN;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INNER_JOIN;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INNER_JOIN;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;INNER_JOIN;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;HAVING;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;HAVING;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;HAVING;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;HAVING;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;GROUP_BY;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;GROUP_BY;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;GROUP_BY;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;GROUP_BY;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;FROM;(String[]);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;FROM;(String[]);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;FROM;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;FROM;(String);;Argument[0].ArrayElement;Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;FETCH_FIRST_ROWS_ONLY;(String);;Argument[0];Argument[-1];taint", + "org.apache.ibatis.jdbc;AbstractSQL;true;DELETE_FROM;(String);;Argument[0];Argument[-1];taint" + ] + } +} diff --git a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll index 5f411afa9ae4..222da6d99960 100644 --- a/java/ql/lib/semmle/code/java/security/OgnlInjection.qll +++ b/java/ql/lib/semmle/code/java/security/OgnlInjection.qll @@ -3,6 +3,7 @@ import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.ExternalFlow +private import semmle.code.java.frameworks.MyBatis /** * A data flow sink for unvalidated user input that is used in OGNL EL evaluation. @@ -122,3 +123,5 @@ private class DefaultOgnlInjectionAdditionalTaintStep extends OgnlInjectionAddit setExpressionStep(node1, node2) } } + +private class MyBatisOgnlInjectionSink extends OgnlInjectionSink instanceof MyBatisInjectionSink { } diff --git a/java/ql/lib/semmle/code/java/security/QueryInjection.qll b/java/ql/lib/semmle/code/java/security/QueryInjection.qll index 86bd6dac4ad6..f4fe51969b9c 100644 --- a/java/ql/lib/semmle/code/java/security/QueryInjection.qll +++ b/java/ql/lib/semmle/code/java/security/QueryInjection.qll @@ -3,6 +3,7 @@ import java import semmle.code.java.dataflow.DataFlow import semmle.code.java.frameworks.javaee.Persistence +private import semmle.code.java.frameworks.MyBatis import semmle.code.java.dataflow.ExternalFlow /** A sink for database query language injection vulnerabilities. */ @@ -66,3 +67,5 @@ private class MongoJsonStep extends AdditionalQueryInjectionTaintStep { ) } } + +private class MyBatisSqlInjectionSink extends QueryInjectionSink instanceof MyBatisInjectionSink { } diff --git a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisAnnotationSqlInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisAnnotationSqlInjection.expected index 8a9b7a3437b9..dfc923f9b589 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisAnnotationSqlInjection.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisAnnotationSqlInjection.expected @@ -13,4 +13,4 @@ nodes | MybatisSqlInjectionService.java:51:27:51:33 | hashMap | semmle.label | hashMap | subpaths #select -| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MybatisSqlInjection.java:62:19:62:43 | name : String | MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MyBatis annotation SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:62:19:62:43 | name | this user input | SqlInjectionMapper.java:29:2:29:54 | Select | this SQL operation | +| MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MybatisSqlInjection.java:62:19:62:43 | name : String | MybatisSqlInjectionService.java:51:27:51:33 | hashMap | MyBatis annotation SQL injection might include code from $@ to $@. | MybatisSqlInjection.java:62:19:62:43 | name | this user input | SqlInjectionMapper.java:33:2:33:54 | Select | this SQL operation | diff --git a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisProvider.java b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisProvider.java new file mode 100644 index 000000000000..e1eceae4f71e --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MyBatisProvider.java @@ -0,0 +1,35 @@ +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.jdbc.SQL; + +public class MyBatisProvider { + public String badSelect(@Param("input") final String input) { + String s = (new SQL() { + { + this.SELECT("password"); + this.FROM("users"); + this.WHERE("username = '" + input + "'"); + } + }).toString(); + return s; + } + + public String badDelete(@Param("input") final String input) { + return "DELETE FROM users WHERE username = '" + input + "';"; + } + + public String badUpdate(@Param("input") final String input) { + String s = (new SQL() { + { + this.UPDATE("users"); + this.SET("balance = 0"); + this.WHERE("username = '" + input + "'"); + } + }).toString(); + return s; + } + + public String badInsert(@Param("input") final String input) { + return "INSERT INTO users VALUES (1, '" + input + "', 'hunter2');"; + } +} + diff --git a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjection.java b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjection.java index 9f37e2eaa15d..624f27ad81d8 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjection.java +++ b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjection.java @@ -68,4 +68,25 @@ public List good1(Integer id) { List result = mybatisSqlInjectionService.good1(id); return result; } + + // using providers + @GetMapping(value = "badSelect") + public String badSelect(@RequestParam String name) { + return mybatisSqlInjectionService.badSelect(name); + } + + @GetMapping(value = "badDelete") + public void badDelete(@RequestParam String name) { + mybatisSqlInjectionService.badDelete(name); + } + + @GetMapping(value = "badUpdate") + public void badUpdate(@RequestParam String name) { + mybatisSqlInjectionService.badUpdate(name); + } + + @GetMapping(value = "badInsert") + public void badInsert(@RequestParam String name) { + mybatisSqlInjectionService.badInsert(name); + } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjectionService.java b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjectionService.java index 88ae4581ce07..89dbd599d710 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjectionService.java +++ b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/MybatisSqlInjectionService.java @@ -55,4 +55,21 @@ public List good1(Integer id) { List result = sqlInjectionMapper.good1(id); return result; } + + // using providers + public String badSelect(String input) { + return sqlInjectionMapper.badSelect(input); + } + + public void badDelete(String input) { + sqlInjectionMapper.badDelete(input); + } + + public void badUpdate(String input) { + sqlInjectionMapper.badUpdate(input); + } + + public void badInsert(String input) { + sqlInjectionMapper.badInsert(input); + } } diff --git a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/SqlInjectionMapper.java b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/SqlInjectionMapper.java index 7823bdc78a19..5b1598172972 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-089/src/main/SqlInjectionMapper.java +++ b/java/ql/test/experimental/query-tests/security/CWE-089/src/main/SqlInjectionMapper.java @@ -5,6 +5,10 @@ import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.SelectProvider; +import org.apache.ibatis.annotations.DeleteProvider; +import org.apache.ibatis.annotations.UpdateProvider; +import org.apache.ibatis.annotations.InsertProvider; @Mapper @Repository @@ -30,4 +34,29 @@ public interface SqlInjectionMapper { public Test bad9(HashMap map); List good1(Integer id); + + //using providers + @SelectProvider( + type = MyBatisProvider.class, + method = "badSelect" + ) + String badSelect(String input); + + @DeleteProvider( + type = MyBatisProvider.class, + method = "badDelete" + ) + void badDelete(String input); + + @UpdateProvider( + type = MyBatisProvider.class, + method = "badUpdate" + ) + void badUpdate(String input); + + @InsertProvider( + type = MyBatisProvider.class, + method = "badInsert" + ) + void badInsert(String input); } diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/DeleteProvider.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/DeleteProvider.java new file mode 100644 index 000000000000..1f18d95a5f9f --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/DeleteProvider.java @@ -0,0 +1,29 @@ +package org.apache.ibatis.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Repeatable(DeleteProvider.List.class) +public @interface DeleteProvider { + + Class value() default void.class; + + Class type() default void.class; + + String method() default ""; + + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface List { + DeleteProvider[] value(); + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/InsertProvider.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/InsertProvider.java new file mode 100644 index 000000000000..04646c6538e8 --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/InsertProvider.java @@ -0,0 +1,29 @@ +package org.apache.ibatis.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Repeatable(InsertProvider.List.class) +public @interface InsertProvider { + + Class value() default void.class; + + Class type() default void.class; + + String method() default ""; + + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface List { + InsertProvider[] value(); + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/SelectProvider.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/SelectProvider.java new file mode 100644 index 000000000000..4c3b4e176cb0 --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/SelectProvider.java @@ -0,0 +1,29 @@ +package org.apache.ibatis.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Repeatable(SelectProvider.List.class) +public @interface SelectProvider { + + Class value() default void.class; + + Class type() default void.class; + + String method() default ""; + + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface List { + SelectProvider[] value(); + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/UpdateProvider.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/UpdateProvider.java new file mode 100644 index 000000000000..32d2c21ad973 --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/annotations/UpdateProvider.java @@ -0,0 +1,29 @@ +package org.apache.ibatis.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Repeatable(UpdateProvider.List.class) +public @interface UpdateProvider { + + Class value() default void.class; + + Class type() default void.class; + + String method() default ""; + + @Documented + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + @interface List { + UpdateProvider[] value(); + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/AbstractSQL.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/AbstractSQL.java new file mode 100644 index 000000000000..b5764e0cf0df --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/AbstractSQL.java @@ -0,0 +1,154 @@ +package org.apache.ibatis.jdbc; + +public abstract class AbstractSQL { + public abstract T getSelf(); + + public T UPDATE(String table) { + return getSelf(); + } + + public T SET(String sets) { + return getSelf(); + } + + public T SET(String ... sets) { + return getSelf(); + } + + public T INSERT_INTO(String tableName) { + return getSelf(); + } + + public T VALUES(String columns, String values) { + return getSelf(); + } + + public T INTO_COLUMNS(String ... columns) { + return getSelf(); + } + + public T INTO_VALUES(String ... values) { + return getSelf(); + } + + public T SELECT(String columns) { + return getSelf(); + } + + public T SELECT(String ... columns) { + return getSelf(); + } + + public T SELECT_DISTINCT(String columns) { + return getSelf(); + } + + public T SELECT_DISTINCT(String ... columns) { + return getSelf(); + } + + public T DELETE_FROM(String table) { + return getSelf(); + } + + public T FROM(String table) { + return getSelf(); + } + + public T FROM(String ... tables) { + return getSelf(); + } + + public T JOIN(String join) { + return getSelf(); + } + + public T JOIN(String ... joins) { + return getSelf(); + } + + public T INNER_JOIN(String join) { + return getSelf(); + } + + public T INNER_JOIN(String ... joins) { + return getSelf(); + } + + public T LEFT_OUTER_JOIN(String join) { + return getSelf(); + } + + public T LEFT_OUTER_JOIN(String ... joins) { + return getSelf(); + } + + public T RIGHT_OUTER_JOIN(String join) { + return getSelf(); + } + + public T RIGHT_OUTER_JOIN(String ... joins) { + return getSelf(); + } + + public T OUTER_JOIN(String join) { + return getSelf(); + } + + public T OUTER_JOIN(String ... joins) { + return getSelf(); + } + + public T WHERE(String conditions) { + return getSelf(); + } + + public T WHERE(String ... conditions) { + return getSelf(); + } + + public T GROUP_BY(String columns) { + return getSelf(); + } + + public T GROUP_BY(String ... columns) { + return getSelf(); + } + + public T HAVING(String conditions) { + return getSelf(); + } + + public T HAVING(String ... conditions) { + return getSelf(); + } + + public T ORDER_BY(String columns) { + return getSelf(); + } + + public T ORDER_BY(String ... columns) { + return getSelf(); + } + + public T LIMIT(String variable) { + return getSelf(); + } + + public T OFFSET(String variable) { + return getSelf(); + } + + public T FETCH_FIRST_ROWS_ONLY(String variable) { + return getSelf(); + } + + public T OFFSET_ROWS(String variable) { + return getSelf(); + } + + @Override + public String toString() { + return ""; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/SQL.java b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/SQL.java new file mode 100644 index 000000000000..37969f8be8c5 --- /dev/null +++ b/java/ql/test/stubs/org.mybatis-3.5.4/org/apache/ibatis/jdbc/SQL.java @@ -0,0 +1,10 @@ +package org.apache.ibatis.jdbc; + +public class SQL extends AbstractSQL { + + @Override + public SQL getSelf() { + return this; + } + +} \ No newline at end of file