From 0a1b714a3402e1fffffd9a160f93d110bf5242d4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 14 Sep 2016 11:06:22 +0200 Subject: [PATCH] SONAR-8025 improve column name checks + factor with table name check --- .../sonar/db/version/BigIntegerColumnDef.java | 2 +- .../org/sonar/db/version/BlobColumnDef.java | 2 +- .../sonar/db/version/BooleanColumnDef.java | 2 +- .../org/sonar/db/version/ClobColumnDef.java | 2 +- .../sonar/db/version/ColumnDefValidation.java | 41 ---------- .../sonar/db/version/CreateTableBuilder.java | 26 +------ .../sonar/db/version/DecimalColumnDef.java | 2 +- .../sonar/db/version/IntegerColumnDef.java | 2 +- .../sonar/db/version/TinyIntColumnDef.java | 2 +- .../org/sonar/db/version/Validations.java | 78 +++++++++++++++++++ .../sonar/db/version/VarcharColumnDef.java | 2 +- .../sonar/db/version/BlobColumnDefTest.java | 2 +- .../db/version/IntegerColumnDefTest.java | 2 +- ...lidationTest.java => ValidationsTest.java} | 8 +- 14 files changed, 95 insertions(+), 78 deletions(-) delete mode 100644 sonar-db/src/main/java/org/sonar/db/version/ColumnDefValidation.java create mode 100644 sonar-db/src/main/java/org/sonar/db/version/Validations.java rename sonar-db/src/test/java/org/sonar/db/version/{ColumnDefValidationTest.java => ValidationsTest.java} (83%) diff --git a/sonar-db/src/main/java/org/sonar/db/version/BigIntegerColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/BigIntegerColumnDef.java index 687ce188304..dc62060e71c 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/BigIntegerColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/BigIntegerColumnDef.java @@ -23,7 +23,7 @@ import javax.annotation.CheckForNull; import org.sonar.db.dialect.Dialect; import org.sonar.db.dialect.Oracle; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; public class BigIntegerColumnDef extends AbstractColumnDef { diff --git a/sonar-db/src/main/java/org/sonar/db/version/BlobColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/BlobColumnDef.java index c049be66cd9..31e51fb9897 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/BlobColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/BlobColumnDef.java @@ -27,7 +27,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; public class BlobColumnDef extends AbstractColumnDef { public BlobColumnDef(Builder builder) { diff --git a/sonar-db/src/main/java/org/sonar/db/version/BooleanColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/BooleanColumnDef.java index 96d9d19a23b..c2cd7d17789 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/BooleanColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/BooleanColumnDef.java @@ -26,7 +26,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; /** * Used to define VARCHAR column diff --git a/sonar-db/src/main/java/org/sonar/db/version/ClobColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/ClobColumnDef.java index 51fa170c405..a57f0043a2e 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/ClobColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/ClobColumnDef.java @@ -27,7 +27,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; /** * Used to define CLOB columns diff --git a/sonar-db/src/main/java/org/sonar/db/version/ColumnDefValidation.java b/sonar-db/src/main/java/org/sonar/db/version/ColumnDefValidation.java deleted file mode 100644 index 49d64ab2ada..00000000000 --- a/sonar-db/src/main/java/org/sonar/db/version/ColumnDefValidation.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program 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. - * - * This program is distributed in the hope that 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.version; - -import com.google.common.base.CharMatcher; -import javax.annotation.Nullable; - -import static com.google.common.base.CharMatcher.JAVA_LOWER_CASE; -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNull; - -public class ColumnDefValidation { - - private ColumnDefValidation() { - // Only static stuff here - } - - public static String validateColumnName(@Nullable String columnName) { - String name = requireNonNull(columnName, "Column name cannot be null"); - checkArgument(JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).or(CharMatcher.DIGIT).matchesAllOf(name), - String.format("Column name should only contains lowercase and _ characters, got '%s'", columnName)); - return name; - } -} diff --git a/sonar-db/src/main/java/org/sonar/db/version/CreateTableBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/CreateTableBuilder.java index 462da8dd3d7..f67b42b1d0c 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/CreateTableBuilder.java +++ b/sonar-db/src/main/java/org/sonar/db/version/CreateTableBuilder.java @@ -19,7 +19,6 @@ */ package org.sonar.db.version; -import com.google.common.base.CharMatcher; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import java.util.ArrayList; @@ -38,20 +37,15 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static com.google.common.base.CharMatcher.anyOf; -import static com.google.common.base.CharMatcher.inRange; import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import static java.util.stream.Stream.of; +import static org.sonar.db.version.Validations.CONSTRAINT_NAME_MAX_SIZE; +import static org.sonar.db.version.Validations.TABLE_NAME_MAX_SIZE; +import static org.sonar.db.version.Validations.checkDbIdentifier; public class CreateTableBuilder { - private static final int TABLE_NAME_MAX_SIZE = 25; - private static final int CONSTRAINT_NAME_MAX_SIZE = 30; - private static final CharMatcher DIGIT_CHAR_MATCHER = inRange('0', '9'); - private static final CharMatcher LOWER_CASE_ASCII_LETTERS_CHAR_MATCHER = inRange('a', 'z'); - private static final CharMatcher UNDERSCORE_CHAR_MATCHER = anyOf("_"); private final Dialect dialect; private final String tableName; @@ -136,20 +130,6 @@ public class CreateTableBuilder { return this; } - private static String checkDbIdentifier(String identifier, String identifierDesc, int maxSize) { - String res = checkNotNull(identifier, "%s can't be null", identifierDesc); - checkArgument( - identifier.length() <= maxSize, - "%s length can't be more than %s", identifierDesc, maxSize); - checkArgument( - LOWER_CASE_ASCII_LETTERS_CHAR_MATCHER.or(DIGIT_CHAR_MATCHER).or(anyOf("_")).matchesAllOf(identifier), - "%s must be lower case and contain only alphanumeric chars or '_', got '%s'", identifierDesc, identifier); - checkArgument( - DIGIT_CHAR_MATCHER.or(UNDERSCORE_CHAR_MATCHER).matchesNoneOf(identifier.subSequence(0, 1)), - "%s must not start by a number or '_', got '%s'", identifierDesc, identifier); - return res; - } - private String createTableStatement() { StringBuilder res = new StringBuilder("CREATE TABLE "); res.append(tableName); diff --git a/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java index 03327d4b6c5..ba4c46358ef 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/DecimalColumnDef.java @@ -27,7 +27,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; public class DecimalColumnDef extends AbstractColumnDef { diff --git a/sonar-db/src/main/java/org/sonar/db/version/IntegerColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/IntegerColumnDef.java index 298bd2e95c2..8b41a8a4ed8 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/IntegerColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/IntegerColumnDef.java @@ -27,7 +27,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; public class IntegerColumnDef extends AbstractColumnDef { diff --git a/sonar-db/src/main/java/org/sonar/db/version/TinyIntColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/TinyIntColumnDef.java index 0f1f8ac544a..81fe6d06eb4 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/TinyIntColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/TinyIntColumnDef.java @@ -27,7 +27,7 @@ import org.sonar.db.dialect.MySql; import org.sonar.db.dialect.Oracle; import org.sonar.db.dialect.PostgreSql; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; /** * Integer that supports at least range [0..128]. Full range depends on database vendor. diff --git a/sonar-db/src/main/java/org/sonar/db/version/Validations.java b/sonar-db/src/main/java/org/sonar/db/version/Validations.java new file mode 100644 index 00000000000..cf75f9ff18d --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/Validations.java @@ -0,0 +1,78 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program 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. + * + * This program is distributed in the hope that 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.version; + +import com.google.common.base.CharMatcher; +import javax.annotation.Nullable; + +import static com.google.common.base.CharMatcher.anyOf; +import static com.google.common.base.CharMatcher.inRange; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; + +public class Validations { + + static final int TABLE_NAME_MAX_SIZE = 25; + static final int CONSTRAINT_NAME_MAX_SIZE = 30; + + private static final CharMatcher DIGIT_CHAR_MATCHER = inRange('0', '9'); + private static final CharMatcher LOWER_CASE_ASCII_LETTERS_CHAR_MATCHER = inRange('a', 'z'); + private static final CharMatcher UNDERSCORE_CHAR_MATCHER = anyOf("_"); + + private Validations() { + // Only static stuff here + } + + static String validateColumnName(@Nullable String columnName) { + String name = requireNonNull(columnName, "Column name cannot be null"); + checkDbIdentifierCharacters(columnName, "Column name"); + return name; + } + + /** + * Ensure {@code identifier} is a valid DB identifier. + * + * @throws NullPointerException if {@code identifier} is {@code null} + * @throws IllegalArgumentException if {@code identifier} is empty + * @throws IllegalArgumentException if {@code identifier} is longer than {@code maxSize} + * @throws IllegalArgumentException if {@code identifier} is not lowercase + * @throws IllegalArgumentException if {@code identifier} contains characters others than ASCII letters, ASCII numbers or {@code _} + * @throws IllegalArgumentException if {@code identifier} starts with {@code _} or a number + */ + static String checkDbIdentifier(@Nullable String identifier, String identifierDesc, int maxSize) { + String res = checkNotNull(identifier, "%s can't be null", identifierDesc); + checkArgument(!res.isEmpty(), "%s, can't be empty", identifierDesc); + checkArgument( + identifier.length() <= maxSize, + "%s length can't be more than %s", identifierDesc, maxSize); + checkDbIdentifierCharacters(identifier, identifierDesc); + return res; + } + + private static void checkDbIdentifierCharacters(String identifier, String identifierDesc) { + checkArgument( + LOWER_CASE_ASCII_LETTERS_CHAR_MATCHER.or(DIGIT_CHAR_MATCHER).or(anyOf("_")).matchesAllOf(identifier), + "%s must be lower case and contain only alphanumeric chars or '_', got '%s'", identifierDesc, identifier); + checkArgument( + DIGIT_CHAR_MATCHER.or(UNDERSCORE_CHAR_MATCHER).matchesNoneOf(identifier.subSequence(0, 1)), + "%s must not start by a number or '_', got '%s'", identifierDesc, identifier); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java index d8da72a4847..cfab9db27e5 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java +++ b/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java @@ -25,7 +25,7 @@ import org.sonar.db.dialect.MsSql; import static java.lang.String.format; import static java.util.Objects.requireNonNull; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; /** * Used to define VARCHAR column diff --git a/sonar-db/src/test/java/org/sonar/db/version/BlobColumnDefTest.java b/sonar-db/src/test/java/org/sonar/db/version/BlobColumnDefTest.java index 94b6419b985..9d06d1109b5 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/BlobColumnDefTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/BlobColumnDefTest.java @@ -45,7 +45,7 @@ public class BlobColumnDefTest { BlobColumnDef.Builder builder = newBlobColumnDefBuilder(); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Column name should only contains lowercase and _ characters, got 'T'"); + expectedException.expectMessage("Column name must be lower case and contain only alphanumeric chars or '_', got 'T'"); builder.setColumnName("T"); } diff --git a/sonar-db/src/test/java/org/sonar/db/version/IntegerColumnDefTest.java b/sonar-db/src/test/java/org/sonar/db/version/IntegerColumnDefTest.java index 1508af300ee..935fb96cfd8 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/IntegerColumnDefTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/IntegerColumnDefTest.java @@ -45,7 +45,7 @@ public class IntegerColumnDefTest { IntegerColumnDef.Builder builder = newIntegerColumnDefBuilder(); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Column name should only contains lowercase and _ characters, got 'T'"); + expectedException.expectMessage("Column name must be lower case and contain only alphanumeric chars or '_', got 'T'"); builder.setColumnName("T"); } diff --git a/sonar-db/src/test/java/org/sonar/db/version/ColumnDefValidationTest.java b/sonar-db/src/test/java/org/sonar/db/version/ValidationsTest.java similarity index 83% rename from sonar-db/src/test/java/org/sonar/db/version/ColumnDefValidationTest.java rename to sonar-db/src/test/java/org/sonar/db/version/ValidationsTest.java index 77ed523d136..bd66ad3f3de 100644 --- a/sonar-db/src/test/java/org/sonar/db/version/ColumnDefValidationTest.java +++ b/sonar-db/src/test/java/org/sonar/db/version/ValidationsTest.java @@ -23,9 +23,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import static org.sonar.db.version.ColumnDefValidation.validateColumnName; +import static org.sonar.db.version.Validations.validateColumnName; -public class ColumnDefValidationTest { +public class ValidationsTest { @Rule public ExpectedException thrown = ExpectedException.none(); @@ -47,7 +47,7 @@ public class ColumnDefValidationTest { @Test public void fail_when_column_name_is_in_upper_case() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Column name should only contains lowercase and _ characters, got 'DATE_IN_MS'"); + thrown.expectMessage("Column name must be lower case and contain only alphanumeric chars or '_', got 'DATE_IN_MS'"); validateColumnName("DATE_IN_MS"); } @@ -55,7 +55,7 @@ public class ColumnDefValidationTest { @Test public void fail_when_column_name_contains_invalid_character() { thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Column name should only contains lowercase and _ characters, got 'date-in/ms'"); + thrown.expectMessage("Column name must be lower case and contain only alphanumeric chars or '_', got 'date-in/ms'"); validateColumnName("date-in/ms"); } -- 2.39.5