Unverified 提交 c38da2df authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1779 from katzyn/identifier

Improve isSimpleIdentifier() and enquoteIdentifier()
......@@ -8106,7 +8106,7 @@ public class Parser {
if (s == null) {
return "\"\"";
}
if (ParserUtil.isSimpleIdentifier(s)) {
if (ParserUtil.isSimpleIdentifier(s, true, false)) {
return s;
}
return StringUtils.quoteIdentifier(s);
......@@ -8124,7 +8124,7 @@ public class Parser {
if (s == null) {
return builder.append("\"\"");
}
if (ParserUtil.isSimpleIdentifier(s)) {
if (ParserUtil.isSimpleIdentifier(s, true, false)) {
return builder.append(s);
}
return StringUtils.quoteIdentifier(builder, s);
......
......@@ -1383,17 +1383,35 @@ public class JdbcStatement extends TraceObject implements Statement, JdbcStateme
/**
* @param identifier
* identifier to quote if required
* identifier to quote if required, may be quoted or unquoted
* @param alwaysQuote
* if {@code true} identifier will be quoted unconditionally
* @return specified identifier quoted if required or explicitly requested
* @return specified identifier quoted if required, explicitly requested, or
* if it was already quoted
* @throws SQLException
* if identifier is not a valid identifier
*/
@Override
public String enquoteIdentifier(String identifier, boolean alwaysQuote) throws SQLException {
if (alwaysQuote || !isSimpleIdentifier(identifier)) {
return StringUtils.quoteIdentifier(identifier);
if (isSimpleIdentifier(identifier)) {
return alwaysQuote ? '"' + identifier + '"': identifier;
}
return identifier;
int length = identifier.length();
if (length > 0 && identifier.charAt(0) == '"') {
boolean quoted = true;
for (int i = 1; i < length; i++) {
if (identifier.charAt(i) == '"') {
quoted = !quoted;
} else if (!quoted) {
throw new SQLException();
}
}
if (quoted) {
throw new SQLException();
}
return identifier;
}
return StringUtils.quoteIdentifier(identifier);
}
/**
......@@ -1403,7 +1421,8 @@ public class JdbcStatement extends TraceObject implements Statement, JdbcStateme
*/
@Override
public boolean isSimpleIdentifier(String identifier) throws SQLException {
return ParserUtil.isSimpleIdentifier(identifier);
JdbcConnection.Settings settings = conn.getSettings();
return ParserUtil.isSimpleIdentifier(identifier, settings.databaseToUpper, settings.databaseToLower);
}
/**
......
......@@ -284,7 +284,6 @@ public class ParserUtil {
private static final int UPPER_OR_OTHER_LETTER =
1 << Character.UPPERCASE_LETTER
| 1 << Character.TITLECASE_LETTER
| 1 << Character.MODIFIER_LETTER
| 1 << Character.OTHER_LETTER;
......@@ -292,6 +291,26 @@ public class ParserUtil {
UPPER_OR_OTHER_LETTER
| 1 << Character.DECIMAL_DIGIT_NUMBER;
private static final int LOWER_OR_OTHER_LETTER =
1 << Character.LOWERCASE_LETTER
| 1 << Character.MODIFIER_LETTER
| 1 << Character.OTHER_LETTER;
private static final int LOWER_OR_OTHER_LETTER_OR_DIGIT =
LOWER_OR_OTHER_LETTER
| 1 << Character.DECIMAL_DIGIT_NUMBER;
private static final int LETTER =
1 << Character.UPPERCASE_LETTER
| 1 << Character.LOWERCASE_LETTER
| 1 << Character.TITLECASE_LETTER
| 1 << Character.MODIFIER_LETTER
| 1 << Character.OTHER_LETTER;
private static final int LETTER_OR_DIGIT =
LETTER
| 1 << Character.DECIMAL_DIGIT_NUMBER;
private ParserUtil() {
// utility class
}
......@@ -316,26 +335,44 @@ public class ParserUtil {
* Is this a simple identifier (in the JDBC specification sense).
*
* @param s identifier to check
* @param databaseToUpper whether unquoted identifiers are converted to upper case
* @param databaseToLower whether unquoted identifiers are converted to lower case
* @return is specified identifier may be used without quotes
* @throws NullPointerException if s is {@code null}
*/
public static boolean isSimpleIdentifier(String s) {
public static boolean isSimpleIdentifier(String s, boolean databaseToUpper, boolean databaseToLower) {
int length = s.length();
if (length == 0) {
return false;
}
int startFlags, partFlags;
if (databaseToUpper) {
if (databaseToLower) {
throw new IllegalArgumentException("databaseToUpper && databaseToLower");
} else {
startFlags = UPPER_OR_OTHER_LETTER;
partFlags = UPPER_OR_OTHER_LETTER_OR_DIGIT;
}
} else {
if (databaseToLower) {
startFlags = LOWER_OR_OTHER_LETTER;
partFlags = LOWER_OR_OTHER_LETTER_OR_DIGIT;
} else {
startFlags = LETTER;
partFlags = LETTER_OR_DIGIT;
}
}
char c = s.charAt(0);
// lowercase a-z is quoted as well
if ((UPPER_OR_OTHER_LETTER >>> Character.getType(c) & 1) == 0 && c != '_') {
if ((startFlags >>> Character.getType(c) & 1) == 0 && c != '_') {
return false;
}
for (int i = 1; i < length; i++) {
c = s.charAt(i);
if ((UPPER_OR_OTHER_LETTER_OR_DIGIT >>> Character.getType(c) & 1) == 0 && c != '_') {
if ((partFlags >>> Character.getType(c) & 1) == 0 && c != '_') {
return false;
}
}
return getSaveTokenType(s, false, 0, length, true) == IDENTIFIER;
return getSaveTokenType(s, !databaseToUpper, 0, length, true) == IDENTIFIER;
}
/**
......
......@@ -53,6 +53,7 @@ public class TestStatement extends TestDb {
testIdentityMerge();
testIdentity();
conn.close();
testIdentifiers();
deleteDb("statement");
}
......@@ -330,23 +331,6 @@ public class TestStatement extends TestDb {
assertNull(stat.getWarnings());
assertTrue(conn == stat.getConnection());
assertEquals("SOME_ID", statBC.enquoteIdentifier("SOME_ID", false));
assertEquals("\"SOME ID\"", statBC.enquoteIdentifier("SOME ID", false));
assertEquals("\"SOME_ID\"", statBC.enquoteIdentifier("SOME_ID", true));
assertEquals("\"FROM\"", statBC.enquoteIdentifier("FROM", false));
assertEquals("\"Test\"", statBC.enquoteIdentifier("Test", false));
assertEquals("\"TODAY\"", statBC.enquoteIdentifier("TODAY", false));
// Other lower case characters don't have upper case mappings
assertEquals("\u02B0", statBC.enquoteIdentifier("\u02B0", false));
assertTrue(statBC.isSimpleIdentifier("SOME_ID"));
assertFalse(statBC.isSimpleIdentifier("SOME ID"));
assertFalse(statBC.isSimpleIdentifier("FROM"));
assertFalse(statBC.isSimpleIdentifier("Test"));
assertFalse(statBC.isSimpleIdentifier("TODAY"));
// Other lower case characters don't have upper case mappings
assertTrue(statBC.isSimpleIdentifier("\u02B0"));
stat.close();
}
......@@ -489,4 +473,84 @@ public class TestStatement extends TestDb {
stat.execute("drop table test");
}
private void testIdentifiers() throws SQLException {
Connection conn = getConnection("statement");
JdbcStatement stat = (JdbcStatement) conn.createStatement();
assertEquals("SOME_ID", stat.enquoteIdentifier("SOME_ID", false));
assertEquals("\"SOME ID\"", stat.enquoteIdentifier("SOME ID", false));
assertEquals("\"SOME_ID\"", stat.enquoteIdentifier("SOME_ID", true));
assertEquals("\"FROM\"", stat.enquoteIdentifier("FROM", false));
assertEquals("\"Test\"", stat.enquoteIdentifier("Test", false));
assertEquals("\"test\"", stat.enquoteIdentifier("test", false));
assertEquals("\"TODAY\"", stat.enquoteIdentifier("TODAY", false));
assertEquals("\"Test\"", stat.enquoteIdentifier("\"Test\"", false));
assertEquals("\"Test\"", stat.enquoteIdentifier("\"Test\"", true));
assertEquals("\"\"\"Test\"", stat.enquoteIdentifier("\"\"\"Test\"", true));
try {
stat.enquoteIdentifier("\"Test", true);
fail();
} catch (SQLException ex) {
// OK
}
// Other lower case characters don't have upper case mappings
assertEquals("\u02B0", stat.enquoteIdentifier("\u02B0", false));
assertTrue(stat.isSimpleIdentifier("SOME_ID"));
assertFalse(stat.isSimpleIdentifier("SOME ID"));
assertFalse(stat.isSimpleIdentifier("FROM"));
assertFalse(stat.isSimpleIdentifier("Test"));
assertFalse(stat.isSimpleIdentifier("test"));
assertFalse(stat.isSimpleIdentifier("TODAY"));
// Other lower case characters don't have upper case mappings
assertTrue(stat.isSimpleIdentifier("\u02B0"));
conn.close();
conn = getConnection("statement;DATABASE_TO_LOWER=TRUE");
stat = (JdbcStatement) conn.createStatement();
assertEquals("some_id", stat.enquoteIdentifier("some_id", false));
assertEquals("\"some id\"", stat.enquoteIdentifier("some id", false));
assertEquals("\"some_id\"", stat.enquoteIdentifier("some_id", true));
assertEquals("\"from\"", stat.enquoteIdentifier("from", false));
assertEquals("\"Test\"", stat.enquoteIdentifier("Test", false));
assertEquals("\"TEST\"", stat.enquoteIdentifier("TEST", false));
assertEquals("\"today\"", stat.enquoteIdentifier("today", false));
assertTrue(stat.isSimpleIdentifier("some_id"));
assertFalse(stat.isSimpleIdentifier("some id"));
assertFalse(stat.isSimpleIdentifier("from"));
assertFalse(stat.isSimpleIdentifier("Test"));
assertFalse(stat.isSimpleIdentifier("TEST"));
assertFalse(stat.isSimpleIdentifier("today"));
conn.close();
conn = getConnection("statement;DATABASE_TO_UPPER=FALSE");
stat = (JdbcStatement) conn.createStatement();
assertEquals("SOME_ID", stat.enquoteIdentifier("SOME_ID", false));
assertEquals("some_id", stat.enquoteIdentifier("some_id", false));
assertEquals("\"SOME ID\"", stat.enquoteIdentifier("SOME ID", false));
assertEquals("\"some id\"", stat.enquoteIdentifier("some id", false));
assertEquals("\"SOME_ID\"", stat.enquoteIdentifier("SOME_ID", true));
assertEquals("\"some_id\"", stat.enquoteIdentifier("some_id", true));
assertEquals("\"FROM\"", stat.enquoteIdentifier("FROM", false));
assertEquals("\"from\"", stat.enquoteIdentifier("from", false));
assertEquals("Test", stat.enquoteIdentifier("Test", false));
assertEquals("\"TODAY\"", stat.enquoteIdentifier("TODAY", false));
assertEquals("\"today\"", stat.enquoteIdentifier("today", false));
assertTrue(stat.isSimpleIdentifier("SOME_ID"));
assertTrue(stat.isSimpleIdentifier("some_id"));
assertFalse(stat.isSimpleIdentifier("SOME ID"));
assertFalse(stat.isSimpleIdentifier("some id"));
assertFalse(stat.isSimpleIdentifier("FROM"));
assertFalse(stat.isSimpleIdentifier("from"));
assertTrue(stat.isSimpleIdentifier("Test"));
assertFalse(stat.isSimpleIdentifier("TODAY"));
assertFalse(stat.isSimpleIdentifier("today"));
conn.close();
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论