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