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

Merge pull request #1790 from katzyn/trim

Do not convert standard TRIM function to non-standard functions
......@@ -3880,7 +3880,7 @@ ENVELOPE(X)
"
"Functions (Numeric)","ABS","
ABS(numeric|interval)
ABS( { numeric | interval } )
","
Returns the absolute value of a specified value.
The returned value is of the same data type as the parameter.
......@@ -4216,7 +4216,7 @@ CALL SECURE_RAND(16)
"
"Functions (Numeric)","SIGN","
SIGN(numeric|interval)
SIGN( { numeric | interval } )
","
Returns -1 if the value is smaller than 0, 0 if zero, and otherwise 1.
","
......@@ -4509,7 +4509,7 @@ RTRIM(NAME)
"
"Functions (String)","TRIM","
TRIM ( [ { LEADING | TRAILING | BOTH } [ string ] FROM ] string )
TRIM ( [ [ LEADING | TRAILING | BOTH ] [ string ] FROM ] string )
","
Removes all leading spaces, trailing spaces, or spaces at both ends, from a string.
Other characters can be removed as well.
......
......@@ -41,6 +41,8 @@ Advanced
Two Phase Commit</a><br />
<a href="#compatibility">
Compatibility</a><br />
<a href="#keywords">
Keywords / Reserved Words</a><br />
<a href="#standards_compliance">
Standards Compliance</a><br />
<a href="#windows_service">
......@@ -469,7 +471,7 @@ For a query, this means the transaction is committed even before the application
Other database engines may commit the transaction in this case when the result set is closed.
</p>
<h3>Keywords / Reserved Words</h3>
<h2 id="keywords">Keywords / Reserved Words</h2>
<p>
There is a list of keywords that can't be used as identifiers (table names, column names and so on),
unless they are quoted (surrounded with double quotes).
......@@ -498,6 +500,8 @@ The following tokens are keywords in H2:
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>BETWEEN</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>NR</td><td>+</td></tr>
<tr><td>BOTH</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>CASE</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>CHECK</td>
......@@ -536,6 +540,8 @@ The following tokens are keywords in H2:
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>GROUP</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>GROUPS</td>
<td>CS</td><td>+</td><td></td><td></td><td></td><td></td></tr>
<tr><td>HAVING</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>IF</td>
......@@ -556,6 +562,8 @@ The following tokens are keywords in H2:
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>JOIN</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>LEADING</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>LEFT</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>LIKE</td>
......@@ -584,10 +592,14 @@ The following tokens are keywords in H2:
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>OVER</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td></td><td></td></tr>
<tr><td>PARTITION</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td></td><td></td></tr>
<tr><td>PRIMARY</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>QUALIFY</td>
<td>+</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>RANGE</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td></td><td></td></tr>
<tr><td>REGEXP</td>
<td>CS</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>RIGHT</td>
......@@ -598,6 +610,8 @@ The following tokens are keywords in H2:
<td>+</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>ROWNUM</td>
<td>+</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>ROWS</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>SELECT</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>SYSDATE</td>
......@@ -612,6 +626,8 @@ The following tokens are keywords in H2:
<td>CS</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>TOP</td>
<td>CS</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>TRAILING</td>
<td>CS</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>TRUE</td>
<td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td></tr>
<tr><td>UNION</td>
......
......@@ -21,6 +21,14 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>PR #1790: Do not convert standard TRIM function to non-standard functions
</li>
<li>Issue #1787: Non-standard MERGE throws LOCK_TIMEOUT_1 on violation of some constraints
</li>
<li>PR #1784: improve database not found error
</li>
<li>Issue #1740: Enhancement Request: h2 server: do not swallow exceptions
</li>
<li>Issue #1616: Metadata and scripts should be persisted with unconditionally quoted identifiers
</li>
<li>PR #1779: Improve isSimpleIdentifier() and enquoteIdentifier()
......
......@@ -177,6 +177,7 @@
90143=Row {1} not found in primary index {0}
90144=Authenticator not enabled on database {0}
90145=FOR UPDATE is not allowed in DISTINCT or grouped select
90146=Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=General error: {0}
HY004=Unknown data type: {0}
HYC00=Feature not supported: {0}
......
......@@ -2059,7 +2059,7 @@ public class ErrorCode {
* </pre>
*/
public static final int DATABASE_NOT_FOUND_2 = 90146;
// next is 90147
private ErrorCode() {
......
......@@ -3582,31 +3582,39 @@ public class Parser {
break;
}
case Function.TRIM: {
Expression space = null;
int flags;
boolean needFrom = false;
if (readIf("LEADING")) {
function = Function.getFunction(database, "LTRIM");
if (!readIf(FROM)) {
space = readExpression();
read(FROM);
}
flags = Function.TRIM_LEADING;
needFrom = true;
} else if (readIf("TRAILING")) {
function = Function.getFunction(database, "RTRIM");
flags = Function.TRIM_TRAILING;
needFrom = true;
} else {
needFrom = readIf("BOTH");
flags = Function.TRIM_LEADING | Function.TRIM_TRAILING;
}
Expression p0, space = null;
function.setFlags(flags);
if (needFrom) {
if (!readIf(FROM)) {
space = readExpression();
read(FROM);
}
} else if (readIf("BOTH")) {
if (!readIf(FROM)) {
space = readExpression();
read(FROM);
p0 = readExpression();
} else {
if (readIf(FROM)) {
p0 = readExpression();
} else {
p0 = readExpression();
if (readIf(FROM)) {
space = p0;
p0 = readExpression();
}
}
}
Expression p0 = readExpression();
if (readIf(COMMA)) {
if (!needFrom && space == null && readIf(COMMA)) {
space = readExpression();
} else if (readIf(FROM)) {
space = p0;
p0 = readExpression();
}
function.setParameter(0, p0);
if (space != null) {
......
......@@ -153,6 +153,16 @@ public class Function extends Expression implements FunctionCall {
*/
public static final int H2VERSION = 231;
/**
* The flags for TRIM(LEADING ...) function.
*/
public static final int TRIM_LEADING = 1;
/**
* The flags for TRIM(TRAILING ...) function.
*/
public static final int TRIM_TRAILING = 2;
protected static final int VAR_ARGS = -1;
private static final HashMap<String, FunctionInfo> FUNCTIONS = new HashMap<>(256);
......@@ -162,6 +172,7 @@ public class Function extends Expression implements FunctionCall {
protected final FunctionInfo info;
private ArrayList<Expression> varArgs;
private int flags;
protected TypeInfo type;
private final Database database;
......@@ -573,6 +584,24 @@ public class Function extends Expression implements FunctionCall {
}
}
/**
* Set the flags for this function.
*
* @param flags the flags to set
*/
public void setFlags(int flags) {
this.flags = flags;
}
/**
* Returns the flags.
*
* @return the flags
*/
public int getFlags() {
return flags;
}
@Override
public Value getValue(Session session) {
return getValueWithArgs(session, args);
......@@ -1343,7 +1372,7 @@ public class Function extends Expression implements FunctionCall {
break;
case TRIM:
result = ValueString.get(StringUtils.trim(v0.getString(),
true, true, v1 == null ? " " : v1.getString()),
(flags & TRIM_LEADING) != 0, (flags & TRIM_TRAILING) != 0, v1 == null ? " " : v1.getString()),
database.getMode().treatEmptyStringsAsNull);
break;
case RTRIM:
......@@ -2602,6 +2631,21 @@ public class Function extends Expression implements FunctionCall {
builder.append('(');
}
switch (info.type) {
case TRIM: {
switch (flags) {
case TRIM_LEADING:
builder.append("LEADING ");
break;
case TRIM_TRAILING:
builder.append("TRAILING ");
break;
}
if (args.length > 1) {
args[1].getSQL(builder, alwaysQuote).append(" FROM ");
}
args[0].getSQL(builder, alwaysQuote);
break;
}
case CAST: {
args[0].getSQL(builder, alwaysQuote).append(" AS ").append(new Column(null, type).getCreateSQL());
break;
......
......@@ -1541,6 +1541,7 @@ public class JdbcDatabaseMetaData extends TraceObject implements
* table/column/index name, in addition to the SQL:2003 keywords. The list
* returned is:
* <pre>
* GROUPS
* IF,ILIKE,INTERSECTS,
* LIMIT,
* MINUS,
......@@ -1553,24 +1554,24 @@ public class JdbcDatabaseMetaData extends TraceObject implements
* The complete list of keywords (including SQL:2003 keywords) is:
* <pre>
* ALL, AND, ARRAY, AS,
* BETWEEN,
* BETWEEN, BOTH
* CASE, CHECK, CONSTRAINT, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_USER,
* DISTINCT,
* EXCEPT, EXISTS,
* FALSE, FETCH, FILTER, FOR, FOREIGN, FROM, FULL,
* GROUP,
* GROUP, GROUPS
* HAVING,
* IF, ILIKE, IN, INNER, INTERSECT, INTERSECTS, INTERVAL, IS,
* JOIN,
* LEFT, LIKE, LIMIT, LOCALTIME, LOCALTIMESTAMP,
* LEADING, LEFT, LIKE, LIMIT, LOCALTIME, LOCALTIMESTAMP,
* MINUS,
* NATURAL, NOT, NULL,
* OFFSET, ON, OR, ORDER, OVER,
* PRIMARY,
* PARTITION, PRIMARY,
* QUALIFY,
* REGEXP, RIGHT, ROW, _ROWID_, ROWNUM,
* RANGE, REGEXP, RIGHT, ROW, _ROWID_, ROWNUM, ROWS,
* SELECT, SYSDATE, SYSTIME, SYSTIMESTAMP,
* TABLE, TODAY, TOP, TRUE,
* TABLE, TODAY, TOP, TRAILING, TRUE,
* UNION, UNIQUE,
* VALUES,
* WHERE, WINDOW, WITH
......@@ -1581,7 +1582,8 @@ public class JdbcDatabaseMetaData extends TraceObject implements
@Override
public String getSQLKeywords() {
debugCodeCall("getSQLKeywords");
return "IF,ILIKE,INTERSECTS," //
return "GROUPS," //
+ "IF,ILIKE,INTERSECTS," //
+ "LIMIT," //
+ "MINUS," //
+ "OFFSET," //
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Obecná chyba: {0}
HY004=Neznámý datový typ: {0}
HYC00=Vlastnost není podporována: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Allgemeiner Fehler: {0}
HY004=Unbekannter Datentyp: {0}
HYC00=Dieses Feature wird nicht unterstützt: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Error General : {0}
HY004=Tipo de dato desconocido : {0}
HYC00=Caracteristica no soportada: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Erreur générale: {0}
HY004=Type de données inconnu: {0}
HYC00=Fonctionnalité non supportée: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=一般エラー: {0}
HY004=不明なデータ型: {0}
HYC00=機能はサポートされていません: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Błąd ogólny: {0}
HY004=Nieznany typ danych: {0}
HYC00=Cecha nie jest wspierana: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Erro geral: {0}
HY004=Tipo de dados desconhecido: {0}
HYC00=Recurso não suportado: {0}
......
......@@ -177,6 +177,7 @@
90143=Строка {1} не найдена в первичном индексе {0}
90144=Внешняя аутентификация не включена в базе данных {0}
90145=FOR UPDATE не допускается в запросе с DISTINCT или запросе с группировкой
90146=База данных {0} не найдена и её автоматическое создание запрещено флагом IFEXISTS=true
HY000=Внутренняя ошибка: {0}
HY004=Неизвестный тип данных: {0}
HYC00=Данная функция не поддерживается: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=Všeobecná chyba: {0}
HY004=Neznámy dátový typ: {0}
HYC00=Vlastnosť nie je podporovaná: {0}
......
......@@ -177,6 +177,7 @@
90143=#Row {1} not found in primary index {0}
90144=#Authenticator not enabled on database {0}
90145=#FOR UPDATE is not allowed in DISTINCT or grouped select
90146=#Database {0} not found, and IFEXISTS=true, so we can't auto-create it
HY000=常规错误: {0}
HY004=位置数据类型: {0}
HYC00=不支持的特性: {0}
......
......@@ -412,7 +412,7 @@ public class ParserUtil {
return IDENTIFIER;
case 'B':
if (additionalKeywords) {
if (eq("BETWEEN", s, ignoreCase, start, end)) {
if (eq("BETWEEN", s, ignoreCase, start, end) || eq("BOTH", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
......@@ -472,6 +472,11 @@ public class ParserUtil {
if (eq("GROUP", s, ignoreCase, start, end)) {
return GROUP;
}
if (additionalKeywords) {
if (eq("GROUPS", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
return IDENTIFIER;
case 'H':
if (eq("HAVING", s, ignoreCase, start, end)) {
......@@ -514,7 +519,7 @@ public class ParserUtil {
return LOCALTIMESTAMP;
}
if (additionalKeywords) {
if (eq("LEFT", s, ignoreCase, start, end)) {
if (eq("LEADING", s, ignoreCase, start, end) || eq("LEFT", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
......@@ -551,6 +556,11 @@ public class ParserUtil {
if (eq("PRIMARY", s, ignoreCase, start, end)) {
return PRIMARY;
}
if (additionalKeywords) {
if (eq("PARTITION", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
return IDENTIFIER;
case 'Q':
if (eq("QUALIFY", s, ignoreCase, start, end)) {
......@@ -564,7 +574,8 @@ public class ParserUtil {
return ROWNUM;
}
if (additionalKeywords) {
if (eq("REGEXP", s, ignoreCase, start, end) || eq("RIGHT", s, ignoreCase, start, end)) {
if (eq("RANGE", s, ignoreCase, start, end) || eq("REGEXP", s, ignoreCase, start, end)
|| eq("ROWS", s, ignoreCase, start, end) || eq("RIGHT", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
......@@ -587,7 +598,8 @@ public class ParserUtil {
return TRUE;
}
if (additionalKeywords) {
if (eq("TODAY", s, ignoreCase, start, end) || eq("TOP", s, ignoreCase, start, end)) {
if (eq("TODAY", s, ignoreCase, start, end) || eq("TOP", s, ignoreCase, start, end)
|| eq("TRAILING", s, ignoreCase, start, end)) {
return KEYWORD;
}
}
......
......@@ -638,7 +638,8 @@ public class TestTableEngines extends TestDb {
+ "INNER JOIN \"PUBLIC\".\"T\" /* batched:test PUBLIC.T_IDX_A: A = Z.A */ ON 1=1 "
+ "WHERE \"Z\".\"A\" = \"T\".\"A\"");
checkPlan(stat, "SELECT 1 FROM \"PUBLIC\".\"U\" /* PUBLIC.U_IDX_B */ "
+ "INNER JOIN ( (SELECT \"A\", \"B\" FROM \"PUBLIC\".\"T\") UNION (SELECT \"B\", \"A\" FROM \"PUBLIC\".\"U\") ) \"Z\" "
+ "INNER JOIN ( (SELECT \"A\", \"B\" FROM \"PUBLIC\".\"T\") "
+ "UNION (SELECT \"B\", \"A\" FROM \"PUBLIC\".\"U\") ) \"Z\" "
+ "/* batched:view (SELECT A, B FROM PUBLIC.T "
+ "/++ batched:test PUBLIC.T_IDX_B: B IS ?1 ++/ "
+ "WHERE B IS ?1) UNION (SELECT B, A FROM PUBLIC.U "
......
......@@ -463,7 +463,8 @@ public class TestMetaData extends TestDb {
assertEquals("schema", meta.getSchemaTerm());
assertEquals("\\", meta.getSearchStringEscape());
assertEquals("IF,ILIKE,INTERSECTS," //
assertEquals("GROUPS," //
+ "IF,ILIKE,INTERSECTS," //
+ "LIMIT," //
+ "MINUS," //
+ "OFFSET," //
......
......@@ -14,3 +14,6 @@ select rtrim(null) en, '>' || rtrim('a') || '<' ea, '>' || rtrim(' a ') || '<' e
> ---- --- ----
> null >a< > a<
> rows: 1
select rtrim() from dual;
> exception INVALID_PARAMETER_COUNT_2
......@@ -3,14 +3,29 @@
-- Initial Developer: H2 Group
--
create memory table test(id int primary key, name varchar(255));
CREATE TABLE TEST(ID INT PRIMARY KEY, A VARCHAR, B VARCHAR, C VARCHAR) AS VALUES (1, '__A__', ' B ', 'xAx');
> ok
insert into test values(1, 'Hello');
> update count: 1
SELECT TRIM(BOTH '_' FROM A), '|' || TRIM(LEADING FROM B) || '|', TRIM(TRAILING 'x' FROM C) FROM TEST;
> TRIM('_' FROM A) ('|' || TRIM(LEADING B)) || '|' TRIM(TRAILING 'x' FROM C)
> ---------------- ------------------------------- -------------------------
> A |B | xA
> rows: 1
SELECT LENGTH(TRIM(B)), LENGTH(TRIM(FROM B)) FROM TEST;
> LENGTH(TRIM(B)) LENGTH(TRIM(B))
> --------------- ---------------
> 1 1
> rows: 1
SELECT TRIM(BOTH B) FROM TEST;
> exception SYNTAX_ERROR_2
DROP TABLE TEST;
> ok
select TRIM(BOTH '_' FROM '__A__') A, TRIM(LEADING FROM ' B ') BS, TRIM(TRAILING 'x' FROM 'xAx') XA from test;
> A BS XA
> - -- --
> A B xA
select TRIM(' ' FROM ' abc ') from dual;
> 'abc'
> -----
> abc
> rows: 1
......@@ -1259,9 +1259,6 @@ select * from test;
drop table test;
> ok
select rtrim() from dual;
> exception INVALID_PARAMETER_COUNT_2
CREATE TABLE TEST(ID INT PRIMARY KEY, LABEL CHAR(20), LOOKUP CHAR(30));
> ok
......@@ -3271,12 +3268,6 @@ select (select id from test where 1=0) from test;
drop table test;
> ok
select TRIM(' ' FROM ' abc ') from dual;
> 'abc'
> -----
> abc
> rows: 1
create table test(id int primary key, a boolean);
> ok
......
......@@ -9,7 +9,8 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Map.Entry;
import org.h2.command.Parser;
import org.h2.test.TestBase;
......@@ -25,6 +26,14 @@ import org.objectweb.asm.Opcodes;
*/
public class TestKeywords extends TestBase {
private enum TokenType {
IDENTIFIER,
KEYWORD,
CONTEXT_SENSITIVE_KEYWORD;
}
/**
* Run just this test.
*
......@@ -37,13 +46,13 @@ public class TestKeywords extends TestBase {
@Override
public void test() throws Exception {
final HashSet<String> set = new HashSet<>();
final HashMap<String, TokenType> tokens = new HashMap<>();
ClassReader r = new ClassReader(Parser.class.getResourceAsStream("Parser.class"));
r.accept(new ClassVisitor(Opcodes.ASM7) {
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature,
Object value) {
add(set, value);
add(value);
return null;
}
......@@ -53,18 +62,18 @@ public class TestKeywords extends TestBase {
return new MethodVisitor(Opcodes.ASM7) {
@Override
public void visitLdcInsn(Object value) {
add(set, value);
add(value);
}
};
}
void add(HashSet<String> set, Object value) {
void add(Object value) {
if (!(value instanceof String)) {
return;
}
String s = (String) value;
int l = s.length();
if (l == 0 || ParserUtil.getSaveTokenType(s, false, 0, l, true) != ParserUtil.IDENTIFIER) {
if (l == 0) {
return;
}
for (int i = 0; i < l; i++) {
......@@ -73,28 +82,91 @@ public class TestKeywords extends TestBase {
return;
}
}
set.add(s);
final TokenType type;
switch (ParserUtil.getSaveTokenType(s, false, 0, l, true)) {
case ParserUtil.IDENTIFIER:
type = TokenType.IDENTIFIER;
break;
case ParserUtil.KEYWORD:
type = TokenType.CONTEXT_SENSITIVE_KEYWORD;
break;
default:
type = TokenType.KEYWORD;
}
tokens.put(s, type);
}
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
try (Connection conn = DriverManager.getConnection("jdbc:h2:mem:keywords")) {
Statement stat = conn.createStatement();
for (String s : set) {
for (Entry<String, TokenType> entry : tokens.entrySet()) {
String s = entry.getKey();
TokenType type = entry.getValue();
Throwable exception1 = null, exception2 = null;
try {
stat.execute("CREATE TABLE " + s + '(' + s + " INT)");
stat.execute("INSERT INTO " + s + '(' + s + ") VALUES (10)");
try (ResultSet rs = stat.executeQuery("SELECT " + s + " FROM " + s)) {
assertTrue(rs.next());
assertEquals(10, rs.getInt(1));
assertFalse(rs.next());
} catch (Throwable t) {
exception1 = t;
}
if (exception1 == null) {
try {
try (ResultSet rs = stat.executeQuery("SELECT " + s + " FROM " + s)) {
assertTrue(rs.next());
assertEquals(10, rs.getInt(1));
assertFalse(rs.next());
}
try (ResultSet rs = stat.executeQuery("SELECT SUM(" + s + ") " + s + " FROM " + s + ' ' + s)) {
assertTrue(rs.next());
assertEquals(10, rs.getInt(1));
assertFalse(rs.next());
assertEquals(s, rs.getMetaData().getColumnLabel(1));
}
stat.execute("DROP TABLE " + s);
stat.execute("CREATE TABLE TEST(" + s + " VARCHAR) AS VALUES '-'");
String str;
try (ResultSet rs = stat.executeQuery("SELECT TRIM(" + s + " FROM '--a--') FROM TEST")) {
assertTrue(rs.next());
str = rs.getString(1);
}
stat.execute("DROP TABLE TEST");
try (ResultSet rs = stat
.executeQuery("SELECT ROW_NUMBER() OVER(" + s + ") WINDOW " + s + " AS ()")) {
}
if (!"a".equals(str)) {
exception2 = new AssertionError();
}
} catch (Throwable t) {
exception2 = t;
stat.execute("DROP TABLE IF EXISTS TEST");
}
try (ResultSet rs = stat.executeQuery("SELECT SUM(" + s + ") " + s + " FROM " + s + ' ' + s)) {
assertTrue(rs.next());
assertEquals(10, rs.getInt(1));
assertFalse(rs.next());
assertEquals(s, rs.getMetaData().getColumnLabel(1));
}
switch (type) {
case IDENTIFIER:
if (exception1 != null) {
throw new AssertionError(s + " must be a keyword.", exception1);
}
} catch (Throwable t) {
throw new AssertionError(s + " cannot be used as identifier.", t);
if (exception2 != null) {
throw new AssertionError(s + " must be a context-sensitive keyword.", exception2);
}
break;
case KEYWORD:
if (exception1 == null && exception2 == null) {
throw new AssertionError(s + " may be removed from a list of keywords.");
}
if (exception1 == null) {
throw new AssertionError(s + " may be a context-sensitive keyword.");
}
break;
case CONTEXT_SENSITIVE_KEYWORD:
if (exception1 != null) {
throw new AssertionError(s + " must be a keyword.", exception1);
}
if (exception2 == null) {
throw new AssertionError(s + " may be removed from a list of context-sensitive keywords.");
}
break;
default:
fail();
}
}
}
......
......@@ -809,4 +809,4 @@ preserves masking holder unboxing avert iae transformed subtle reevaluate exclus
presorted inclusion contexts aax mwd percentile cont interpolate mwa hypothetical regproc childed listagg foreground
isodow isoyear psql
waiters reliably httpsdocs privileged narrow spending
waiters reliably httpsdocs privileged narrow spending swallow
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论