]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6857 Fix escaping in a like query
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 2 Oct 2015 12:56:02 +0000 (14:56 +0200)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Fri, 2 Oct 2015 12:56:02 +0000 (14:56 +0200)
12 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/ws/ActivityWsAction.java
sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java
sonar-db/src/main/java/org/sonar/db/WildcardPosition.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/component/ComponentQuery.java
sonar-db/src/main/java/org/sonar/db/dialect/AbstractDialect.java
sonar-db/src/main/java/org/sonar/db/dialect/Dialect.java
sonar-db/src/main/java/org/sonar/db/dialect/WildcardPosition.java [deleted file]
sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml
sonar-db/src/test/java/org/sonar/db/DatabaseUtilsTest.java
sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java
sonar-db/src/test/java/org/sonar/db/dialect/DialectTest.java [deleted file]

index 5078bd381b61e05c166c9ab1aa8de2bb6c32f615..503c99724cf240e853aea073449c583525c55429 100644 (file)
@@ -132,7 +132,6 @@ public class SearchAction implements ComponentsWsAction {
 
   private ComponentQuery buildQuery(Request wsRequest, List<String> qualifiers) {
     return new ComponentQuery(
-      dbClient.getDatabase(),
       wsRequest.param(Param.TEXT_QUERY),
       qualifiers.toArray(new String[qualifiers.size()]));
   }
index f34dfd063d3fa2dd409436e17c987f3f4d0577c4..d2122a371840665af2ec6b9608f911d132bf2a92 100644 (file)
@@ -173,7 +173,7 @@ public class ActivityWsAction implements CeWsAction {
       query.setComponentUuid(componentUuid);
     }
     if (componentQuery != null) {
-      ComponentQuery componentDtoQuery = new ComponentQuery(dbClient.getDatabase(), componentQuery, Qualifiers.PROJECT, Qualifiers.VIEW);
+      ComponentQuery componentDtoQuery = new ComponentQuery(componentQuery, Qualifiers.PROJECT, Qualifiers.VIEW);
       List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, 0, CeActivityQuery.MAX_COMPONENT_UUIDS);
       query.setComponentUuids(Lists.transform(componentDtos, ComponentDtoFunctions.toUuid()));
     }
index 84360b3ff8d703dd182cac22d865d676b0ee4e23..13227b1781d3502ec9aa06176d4e02e8ec1f1557 100644 (file)
@@ -75,6 +75,45 @@ public class DatabaseUtils {
     }
   }
 
+  /**
+   * Returns an escaped value in parameter, with the desired wildcards. Suitable to be used in a like sql query<br />
+   * Escapes the "/", "%" and "_" characters.<br/>
+   * 
+   * You <strong>must</strong> add "ESCAPE '/'" after your like query. It defines '/' as the escape character.
+   */
+  /**
+   * 
+   */
+  public static String buildLikeValue(String value, WildcardPosition wildcardPosition) {
+    String escapedValue = escapePercentAndUnderscore(value);
+    String wildcard = "%";
+    switch (wildcardPosition) {
+      case BEFORE:
+        escapedValue = wildcard + escapedValue;
+        break;
+      case AFTER:
+        escapedValue += wildcard;
+        break;
+      case BEFORE_AND_AFTER:
+        escapedValue = wildcard + escapedValue + wildcard;
+        break;
+      default:
+        throw new UnsupportedOperationException("Unhandled WildcardPosition: " + wildcardPosition);
+    }
+
+    return escapedValue;
+  }
+
+  /**
+   * Replace escape percent and underscore by adding a slash just before
+   */
+  private static String escapePercentAndUnderscore(String value) {
+    return value
+      .replaceAll("/", "//")
+      .replaceAll("%", "/%")
+      .replaceAll("_", "/_");
+  }
+
   /**
    * Partition by 1000 elements a list of input and execute a function on each part.
    *
diff --git a/sonar-db/src/main/java/org/sonar/db/WildcardPosition.java b/sonar-db/src/main/java/org/sonar/db/WildcardPosition.java
new file mode 100644 (file)
index 0000000..9c58d71
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.db;
+
+public enum WildcardPosition {
+  BEFORE, AFTER, BEFORE_AND_AFTER
+}
index 6c4e361ddfc4337c168120c35dcb6ecbb7868b1a..57d4536f968a335666df80a43faa728485a8a9df 100644 (file)
@@ -22,20 +22,18 @@ package org.sonar.db.component;
 
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
-import org.sonar.db.Database;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static org.sonar.db.dialect.WildcardPosition.AFTER;
+import static org.sonar.db.DatabaseUtils.buildLikeValue;
+import static org.sonar.db.WildcardPosition.AFTER;
 
 public class ComponentQuery {
-  private final Database database;
   private final String nameOrKeyQuery;
   private final String[] qualifiers;
 
-  public ComponentQuery(Database database, @Nullable String nameOrKeyQuery, String... qualifiers) {
+  public ComponentQuery(@Nullable String nameOrKeyQuery, String... qualifiers) {
     checkArgument(qualifiers.length > 0, "At least one qualifier must be provided");
 
-    this.database = database;
     this.nameOrKeyQuery = nameOrKeyQuery;
     this.qualifiers = qualifiers;
   }
@@ -51,11 +49,11 @@ public class ComponentQuery {
 
   @CheckForNull
   public String getNameOrKeyQueryToSqlForResourceIndex() {
-    return database.getDialect().buildLikeValue(nameOrKeyQuery, AFTER).toLowerCase();
+    return buildLikeValue(nameOrKeyQuery, AFTER).toLowerCase();
   }
 
   @CheckForNull
   public String getNameOrKeyQueryToSqlForProjectKey() {
-    return database.getDialect().buildLikeValue(nameOrKeyQuery, AFTER);
+    return buildLikeValue(nameOrKeyQuery, AFTER);
   }
 }
index 5aeb289f4954c7d490b5105d35c0c45a22019489..236c90acd1b0253ce775f6252afaab5468413f7f 100644 (file)
@@ -87,44 +87,4 @@ abstract class AbstractDialect implements Dialect {
   public int getScrollSingleRowFetchSize() {
     return 1;
   }
-
-  @Override
-  public String buildLikeValue(String value, WildcardPosition wildcardPosition) {
-    String escapedValue = escapePercentAndUnderscore(value);
-    String wildcard = "%";
-    switch (wildcardPosition) {
-      case BEFORE:
-        escapedValue = wildcard + escapedValue;
-        break;
-      case AFTER:
-        escapedValue += wildcard;
-        break;
-      case BEFORE_AND_AFTER:
-        escapedValue = wildcard + escapedValue + wildcard;
-        break;
-      default:
-        throw new UnsupportedOperationException("Unhandled WildcardPosition: " + wildcardPosition);
-    }
-
-    return appendEscapeBackslachForSomeDb(escapedValue);
-  }
-
-  /**
-   * Replace escape percent and underscore by adding a slash just before
-   */
-  private static String escapePercentAndUnderscore(String value) {
-    return value
-      .replaceAll("%", "\\\\%")
-      .replaceAll("_", "\\\\_");
-  }
-
-  private String appendEscapeBackslachForSomeDb(String value) {
-    return isOracleOrMsSql()
-      ? (value + " ESCAPE '\\'")
-      : value;
-  }
-
-  private boolean isOracleOrMsSql() {
-    return getId().equals(Oracle.ID) || getId().equals(MsSql.ID);
-  }
 }
index a6fba169d91dfb4c7657042a6b8d60857293b173..3d0a0f6e730145c2eface2de092815947a1b6a86 100644 (file)
@@ -89,10 +89,4 @@ public interface Dialect {
    * @return a boolean
    */
   boolean supportsMigration();
-
-  /**
-   * Returns an escaped value in parameter, with the desired wildcards.
-   * Suitable to be used in a like sql query
-   */
-  String buildLikeValue(String value, WildcardPosition wildcardPosition);
 }
diff --git a/sonar-db/src/main/java/org/sonar/db/dialect/WildcardPosition.java b/sonar-db/src/main/java/org/sonar/db/dialect/WildcardPosition.java
deleted file mode 100644 (file)
index 0b8780b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.db.dialect;
-
-public enum WildcardPosition {
-  BEFORE, AFTER, BEFORE_AND_AFTER
-}
index 8d846a61e39cb061bd498b178d5b075197aaf6bb..9d295b933833076ae4194fa3a137776cc58d657c 100644 (file)
         <foreach collection="qualifiers" item="qualifier" open="(" close=")" separator=",">
           #{qualifier}
         </foreach>
-        AND ri.kee like #{nameOrKeyQueryToSqlForResourceIndex})
-        OR p.kee like #{nameOrKeyQueryToSqlForProjectKey})
+        AND ri.kee like #{nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/')
+        OR p.kee like #{nameOrKeyQueryToSqlForProjectKey} ESCAPE '/')
       </if>
     </where>
   </sql>
index fc8506433796556c41088e6b5b1c071417d8ca05..823f425a044c81fc1daebac793f0b66f31e7e00f 100644 (file)
@@ -44,6 +44,10 @@ import static org.assertj.core.api.Assertions.fail;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.sonar.db.DatabaseUtils.buildLikeValue;
+import static org.sonar.db.WildcardPosition.AFTER;
+import static org.sonar.db.WildcardPosition.BEFORE;
+import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
 
 @Category(DbTests.class)
 public class DatabaseUtilsTest {
@@ -218,4 +222,14 @@ public class DatabaseUtilsTest {
 
     assertThat(logTester.logs(LoggerLevel.ERROR)).contains("SQL error: 456. Message: this is next");
   }
+
+  @Test
+  public void buildLikeValue_with_special_characters() {
+    String escapedValue = "like-\\/_/%//-value";
+    String wildcard = "%";
+
+    assertThat(buildLikeValue("like-\\_%/-value", BEFORE)).isEqualTo(wildcard + escapedValue);
+    assertThat(buildLikeValue("like-\\_%/-value", AFTER)).isEqualTo(escapedValue + wildcard);
+    assertThat(buildLikeValue("like-\\_%/-value", BEFORE_AND_AFTER)).isEqualTo(wildcard + escapedValue + wildcard);
+  }
 }
index 0ca8782056fa0f79cef56353cb9ea862a6af16b9..a282e4e25da92b20e3bea512ec0b935a69bf317e 100644 (file)
@@ -649,7 +649,7 @@ public class ComponentDaoTest {
     db.commit();
     componentDb.indexProjects();
 
-    ComponentQuery query = new ComponentQuery(db.database(), "oJect", Qualifiers.PROJECT);
+    ComponentQuery query = new ComponentQuery("oJect", Qualifiers.PROJECT);
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 1, 3);
 
     assertThat(result).hasSize(3);
@@ -659,15 +659,15 @@ public class ComponentDaoTest {
 
   @Test
   public void select_by_query_name_with_special_characters() {
-    componentDb.insertProjectAndSnapshot(dbSession, newProjectDto().setName("project-_%-name"));
+    componentDb.insertProjectAndSnapshot(dbSession, newProjectDto().setName("project-\\_%/-name"));
     db.commit();
     componentDb.indexProjects();
 
-    ComponentQuery query = new ComponentQuery(db.database(), "-_%-", Qualifiers.PROJECT);
+    ComponentQuery query = new ComponentQuery("-\\_%/-", Qualifiers.PROJECT);
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
-    assertThat(result.get(0).name()).isEqualTo("project-_%-name");
+    assertThat(result.get(0).name()).isEqualTo("project-\\_%/-name");
   }
 
   @Test
@@ -677,7 +677,7 @@ public class ComponentDaoTest {
     db.commit();
     componentDb.indexProjects();
 
-    ComponentQuery query = new ComponentQuery(db.database(), "project-_%-", Qualifiers.PROJECT);
+    ComponentQuery query = new ComponentQuery("project-_%-", Qualifiers.PROJECT);
     List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10);
 
     assertThat(result).hasSize(1);
diff --git a/sonar-db/src/test/java/org/sonar/db/dialect/DialectTest.java b/sonar-db/src/test/java/org/sonar/db/dialect/DialectTest.java
deleted file mode 100644 (file)
index c20b8cc..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * SonarQube, open source software quality management tool.
- * Copyright (C) 2008-2014 SonarSource
- * mailto:contact AT sonarsource DOT com
- *
- * SonarQube is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * SonarQube is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-
-package org.sonar.db.dialect;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.dialect.WildcardPosition.AFTER;
-import static org.sonar.db.dialect.WildcardPosition.BEFORE;
-import static org.sonar.db.dialect.WildcardPosition.BEFORE_AND_AFTER;
-
-public class DialectTest {
-
-  Dialect underTest = new H2();
-
-  @Test
-  public void buildLikeValue_with_H2() {
-    String escapedValue = "like-\\_\\%-value";
-    String wildcard = "%";
-
-    assertThat(underTest.buildLikeValue("like-_%-value", BEFORE)).isEqualTo(wildcard + escapedValue);
-    assertThat(underTest.buildLikeValue("like-_%-value", AFTER)).isEqualTo(escapedValue + wildcard);
-    assertThat(underTest.buildLikeValue("like-_%-value", BEFORE_AND_AFTER)).isEqualTo(wildcard + escapedValue + wildcard);
-  }
-
-  @Test
-  public void buildLikeValue_with_Oracle() {
-    underTest = new Oracle();
-    String escapedValue = "like-\\_\\%-value";
-    String wildcard = "%";
-
-    assertThat(underTest.buildLikeValue("like-_%-value", BEFORE_AND_AFTER)).isEqualTo(wildcard + escapedValue + wildcard + " ESCAPE '\\'");
-
-  }
-}