Skip to content

Commit

Permalink
fix(bdm): reserved keywords not checked during validation
Browse files Browse the repository at this point in the history
The reserved keywords list has been completed.
Some reserved keywords are not necessary invalid. Hence we just add a
warning to tell that they are reserved keywords without blocking.

Relates to
[STUDIO-4477](https://bonitasoft.atlassian.net/browse/STUDIO-4477)
  • Loading branch information
vhemery committed Nov 21, 2024
1 parent 2521ecf commit 702f212
Show file tree
Hide file tree
Showing 9 changed files with 881 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Scanner;
import java.util.Set;
Expand All @@ -28,50 +33,102 @@
*/
public class SQLNameValidator {

public static enum Grammar {

SQL, H2, POSTGRES, ORACLE, MYSQL, SQLSERVER;

@Override
public String toString() {
switch (this) {
case SQL:
return "SQL grammar";
case H2:
return "H2";
case POSTGRES:
return "PostgreSQL";
case ORACLE:
return "Oracle";
case MYSQL:
return "MySQL";
case SQLSERVER:
return "Microsoft SQL Server";
default:
return name();
}
}
}

private static final int DEFAULT_MAX_LENGTH = 255;
private static final String SQL_KEYWORDS_RESOURCE = "/sql_keywords";
private static final String SQL_KEYWORDS_RESOURCE = "/blocked_db_keywords";

private final int maxLength;

static Set<String> sqlKeywords = new HashSet<>();
private static class KeywordsHolder {

public SQLNameValidator() {
this(DEFAULT_MAX_LENGTH);
}
static Set<String> blockedDbKeywords = new HashSet<>();;

public SQLNameValidator(final int maxLength) {
this.maxLength = maxLength;
if (sqlKeywords.isEmpty()) {
initializeSQLKeywords();
static Map<String, List<Grammar>> discouragedKeywords = new HashMap<>();

static {
// initialize keywords
try (Scanner scanner = new Scanner(getKeywordsResource(SQL_KEYWORDS_RESOURCE))) {
while (scanner.hasNext()) {
final String word = scanner.nextLine();
blockedDbKeywords.add(word.trim());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Grammar[] grammars = Grammar.values();
for (Grammar gram : grammars) {
var fileName = "/" + gram.name().toLowerCase() + "_permissive_keywords";
try (Scanner scanner = new Scanner(getKeywordsResource(fileName))) {
while (scanner.hasNext()) {
final String word = scanner.nextLine();
discouragedKeywords.putIfAbsent(word, new ArrayList<Grammar>(grammars.length));
discouragedKeywords.get(word).add(gram);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

private void initializeSQLKeywords() {
try (Scanner scanner = new Scanner(getSQLKeywordsResource())) {
while (scanner.hasNext()) {
final String word = scanner.nextLine();
sqlKeywords.add(word.trim());
private static InputStream getKeywordsResource(String resourceName) throws IOException {
var sqlKeywordsResource = Optional.ofNullable(ResourceFinder.findEntry(resourceName))
.orElseGet(() -> SQLNameValidator.class.getResource(resourceName));
if (sqlKeywordsResource == null) {
throw new FileNotFoundException("SQL Keywords resource not found: " + resourceName);
}
} catch (IOException e) {
throw new IllegalStateException(e);
return sqlKeywordsResource.openStream();
}
}

private InputStream getSQLKeywordsResource() throws IOException {
var sqlKeywordsResource = Optional.ofNullable(ResourceFinder.findEntry(SQL_KEYWORDS_RESOURCE))
.orElseGet(() -> SQLNameValidator.class.getResource(SQL_KEYWORDS_RESOURCE));
if (sqlKeywordsResource == null) {
throw new FileNotFoundException("SQL Keywords resource not found");
}
return sqlKeywordsResource.openStream();
public SQLNameValidator() {
this(DEFAULT_MAX_LENGTH);
}

public SQLNameValidator(final int maxLength) {
this.maxLength = maxLength;
}

public boolean isValid(final String name) {
return name.matches("[a-zA-Z][\\d\\w#@]{0," + maxLength + "}$") && !isSQLKeyword(name);
}

public boolean isSQLKeyword(final String name) {
return sqlKeywords.contains(name.toUpperCase());
return KeywordsHolder.blockedDbKeywords.contains(name.toUpperCase());
}

/**
* Check whether this name is a keyword discouraged by SQL or any specific DB vendor.
*
* @param name name to check
* @return the grammars discouraging it (empty when not discouraged)
*/
public List<Grammar> isKeywordDiscouragedBy(final String name) {
return Collections.unmodifiableList(
KeywordsHolder.discouragedKeywords.getOrDefault(name.toUpperCase(), Collections.emptyList()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ LEAVE
LEFT
LEVEL
LIKE
LIMIT
LINKS
LOCAL
LOCALTIME
Expand Down Expand Up @@ -253,6 +254,7 @@ ROLLBACK
ROLLUP
ROUTINE
ROW
ROWNUM
ROWS
SAVEPOINT
SCHEMA
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CURRENT_CATALOG
CURRENT_SCHEMA
GROUPS
ILIKE
MINUS
OFFSET
QUALIFY
REGEXP
UESCAPE
Loading

0 comments on commit 702f212

Please sign in to comment.