提交 e9c61128 authored 作者: Thomas Mueller's avatar Thomas Mueller

Improved Oracle compatibility for CASE WHEN and DECODE.

上级 65d5f5e7
...@@ -18,7 +18,8 @@ Change Log ...@@ -18,7 +18,8 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The statement "drop all objects" did not work if a table depends on a view via a constraint. <ul><li>Improved Oracle compatibility for CASE WHEN and DECODE.
</li><li>The statement "drop all objects" did not work if a table depends on a view via a constraint.
</li><li>Subqueries or views with "order by" an alias expression could not be executed </li><li>Subqueries or views with "order by" an alias expression could not be executed
due to a regression introduced in version 1.3.174. due to a regression introduced in version 1.3.174.
</li><li>Issue 73: MySQL compatibility: support REPLACE, patch by Cemo Koc. </li><li>Issue 73: MySQL compatibility: support REPLACE, patch by Cemo Koc.
......
...@@ -1752,7 +1752,7 @@ public class Parser { ...@@ -1752,7 +1752,7 @@ public class Parser {
} }
} }
if (database.getMode().isolationLevelInSelectStatement) { if (database.getMode().isolationLevelInSelectStatement) {
parseIsolationClause(); parseIsolationClause();
} }
} }
...@@ -1767,10 +1767,12 @@ public class Parser { ...@@ -1767,10 +1767,12 @@ public class Parser {
read("AND"); read("AND");
read("KEEP"); read("KEEP");
if (readIf("SHARE") || readIf("UPDATE") || readIf("EXCLUSIVE")) { if (readIf("SHARE") || readIf("UPDATE") || readIf("EXCLUSIVE")) {
// ignore
} }
read("LOCKS"); read("LOCKS");
} }
} else if (readIf("CS") || readIf("UR")) { } else if (readIf("CS") || readIf("UR")) {
// ignore
} }
} }
} }
...@@ -2597,12 +2599,7 @@ public class Parser { ...@@ -2597,12 +2599,7 @@ public class Parser {
// CASE must be processed before (, // CASE must be processed before (,
// otherwise CASE(3) would be a function call, which it is // otherwise CASE(3) would be a function call, which it is
// not // not
if (isToken("WHEN")) { r = readCase();
r = readWhen(null);
} else {
Expression left = readExpression();
r = readWhen(left);
}
} else if (readIf("(")) { } else if (readIf("(")) {
r = readFunction(null, name); r = readFunction(null, name);
} else if (equalsToken("CURRENT_USER", name)) { } else if (equalsToken("CURRENT_USER", name)) {
...@@ -2787,29 +2784,55 @@ public class Parser { ...@@ -2787,29 +2784,55 @@ public class Parser {
return r; return r;
} }
private Expression readWhen(Expression left) { private Expression readCase() {
if (readIf("END")) { if (readIf("END")) {
readIf("CASE"); readIf("CASE");
return ValueExpression.getNull(); return ValueExpression.getNull();
} }
if (readIf("ELSE")) { if (readIf("ELSE")) {
Expression elsePart = readExpression(); Expression elsePart = readExpression().optimize(session);
read("END"); read("END");
readIf("CASE"); readIf("CASE");
return elsePart; return elsePart;
} }
readIf("WHEN"); int i;
Expression when = readExpression(); Function function;
if (left != null) { if (readIf("WHEN")) {
when = new Comparison(session, Comparison.EQUAL, left, when); function = Function.getFunction(database, "CASE");
} function.setParameter(0, null);
read("THEN"); i = 1;
Expression then = readExpression(); do {
Expression elsePart = readWhen(left); function.setParameter(i++, readExpression());
Function function = Function.getFunction(session.getDatabase(), "CASEWHEN"); read("THEN");
function.setParameter(0, when); function.setParameter(i++, readExpression());
function.setParameter(1, then); } while (readIf("WHEN"));
function.setParameter(2, elsePart); } else {
Expression expr = readExpression();
if (readIf("END")) {
readIf("CASE");
return ValueExpression.getNull();
}
if (readIf("ELSE")) {
Expression elsePart = readExpression().optimize(session);
read("END");
readIf("CASE");
return elsePart;
}
function = Function.getFunction(database, "CASE");
function.setParameter(0, expr);
i = 1;
read("WHEN");
do {
function.setParameter(i++, readExpression());
read("THEN");
function.setParameter(i++, readExpression());
} while (readIf("WHEN"));
}
if (readIf("ELSE")) {
function.setParameter(i, readExpression());
}
read("END");
readIf("CASE");
function.doneWithParameters(); function.doneWithParameters();
return function; return function;
} }
......
...@@ -821,9 +821,10 @@ public class Function extends Expression implements FunctionCall { ...@@ -821,9 +821,10 @@ public class Function extends Expression implements FunctionCall {
} }
case DECODE: { case DECODE: {
int index = -1; int index = -1;
for (int i = 1; i < args.length - 1; i += 2) { for (int i = 1, len = args.length - 1; i < len; i += 2) {
if (database.areEqual(v0, getNullOrValue(session, args, values, i))) { if (database.areEqual(v0, getNullOrValue(session, args, values, i))) {
index = i + 1; index = i + 1;
break;
} }
} }
if (index < 0 && args.length % 2 == 0) { if (index < 0 && args.length % 2 == 0) {
...@@ -876,18 +877,42 @@ public class Function extends Expression implements FunctionCall { ...@@ -876,18 +877,42 @@ public class Function extends Expression implements FunctionCall {
break; break;
} }
case CASE: { case CASE: {
result = null; Expression then = null;
int i = 0; if (v0 == null) {
for (; i < args.length; i++) { // Searched CASE expression
Value when = getNullOrValue(session, args, values, i++); // (null, when, then)
if (Boolean.TRUE.equals(when)) { // (null, when, then, else)
result = getNullOrValue(session, args, values, i); // (null, when, then, when, then)
break; // (null, when, then, when, then, else)
for (int i = 1, len = args.length - 1; i < len; i += 2) {
Value when = args[i].getValue(session);
if (!(when == ValueNull.INSTANCE) && when.getBoolean().booleanValue()) {
then = args[i + 1];
break;
}
}
} else {
// Simple CASE expression
// (expr, when, then)
// (expr, when, then, else)
// (expr, when, then, when, then)
// (expr, when, then, when, then, else)
if (!(v0 == ValueNull.INSTANCE)) {
for (int i = 1, len = args.length - 1; i < len; i += 2) {
Value when = args[i].getValue(session);
if (database.areEqual(v0, when)) {
then = args[i + 1];
break;
}
}
} }
} }
if (result == null) { if (then == null && args.length % 2 == 0) {
result = i < args.length ? getNullOrValue(session, args, values, i) : ValueNull.INSTANCE; // then = elsePart
then = args[args.length - 1];
} }
Value v = then == null ? ValueNull.INSTANCE : then.getValue(session);
result = v.convertTo(dataType);
break; break;
} }
case ARRAY_GET: { case ARRAY_GET: {
...@@ -971,7 +996,11 @@ public class Function extends Expression implements FunctionCall { ...@@ -971,7 +996,11 @@ public class Function extends Expression implements FunctionCall {
} }
Value v = values[i]; Value v = values[i];
if (v == null) { if (v == null) {
v = values[i] = args[i].getValue(session); Expression e = args[i];
if (e == null) {
return null;
}
v = values[i] = e.getValue(session);
} }
return v; return v;
} }
...@@ -1700,7 +1729,9 @@ public class Function extends Expression implements FunctionCall { ...@@ -1700,7 +1729,9 @@ public class Function extends Expression implements FunctionCall {
@Override @Override
public void mapColumns(ColumnResolver resolver, int level) { public void mapColumns(ColumnResolver resolver, int level) {
for (Expression e : args) { for (Expression e : args) {
e.mapColumns(resolver, level); if (e != null) {
e.mapColumns(resolver, level);
}
} }
} }
...@@ -1745,7 +1776,6 @@ public class Function extends Expression implements FunctionCall { ...@@ -1745,7 +1776,6 @@ public class Function extends Expression implements FunctionCall {
min = 2; min = 2;
max = 3; max = 3;
break; break;
case CASE:
case CONCAT: case CONCAT:
case CONCAT_WS: case CONCAT_WS:
case CSVWRITE: case CSVWRITE:
...@@ -1766,6 +1796,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1766,6 +1796,7 @@ public class Function extends Expression implements FunctionCall {
max = 2; max = 2;
break; break;
case DECODE: case DECODE:
case CASE:
min = 3; min = 3;
break; break;
default: default:
...@@ -1810,7 +1841,11 @@ public class Function extends Expression implements FunctionCall { ...@@ -1810,7 +1841,11 @@ public class Function extends Expression implements FunctionCall {
public Expression optimize(Session session) { public Expression optimize(Session session) {
boolean allConst = info.deterministic; boolean allConst = info.deterministic;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Expression e = args[i].optimize(session); Expression e = args[i];
if (e == null) {
continue;
}
e = e.optimize(session);
args[i] = e; args[i] = e;
if (!e.isConstant()) { if (!e.isConstant()) {
allConst = false; allConst = false;
...@@ -1824,24 +1859,12 @@ public class Function extends Expression implements FunctionCall { ...@@ -1824,24 +1859,12 @@ public class Function extends Expression implements FunctionCall {
case NULLIF: case NULLIF:
case COALESCE: case COALESCE:
case LEAST: case LEAST:
case GREATEST: case GREATEST: {
case DECODE: {
t = Value.UNKNOWN; t = Value.UNKNOWN;
s = 0; s = 0;
p = 0; p = 0;
d = 0; d = 0;
int i = 0;
for (Expression e : args) { for (Expression e : args) {
if (info.type == DECODE) {
if (i < 2 || ((i % 2 == 1) && (i != args.length - 1))) {
// decode(a, b, whenB)
// decode(a, b, whenB, else)
// decode(a, b, whenB, c, whenC)
// decode(a, b, whenB, c, whenC, end)
i++;
continue;
}
}
if (e != ValueExpression.getNull()) { if (e != ValueExpression.getNull()) {
int type = e.getType(); int type = e.getType();
if (type != Value.UNKNOWN && type != Value.NULL) { if (type != Value.UNKNOWN && type != Value.NULL) {
...@@ -1851,7 +1874,48 @@ public class Function extends Expression implements FunctionCall { ...@@ -1851,7 +1874,48 @@ public class Function extends Expression implements FunctionCall {
d = Math.max(d, e.getDisplaySize()); d = Math.max(d, e.getDisplaySize());
} }
} }
i++; }
if (t == Value.UNKNOWN) {
t = Value.STRING;
s = 0;
p = Integer.MAX_VALUE;
d = Integer.MAX_VALUE;
}
break;
}
case CASE:
case DECODE: {
t = Value.UNKNOWN;
s = 0;
p = 0;
d = 0;
// (expr, when, then)
// (expr, when, then, else)
// (expr, when, then, when, then)
// (expr, when, then, when, then, else)
for (int i = 2, len = args.length; i < len; i += 2) {
Expression then = args[i];
if (then != ValueExpression.getNull()) {
int type = then.getType();
if (type != Value.UNKNOWN && type != Value.NULL) {
t = Value.getHigherOrder(t, type);
s = Math.max(s, then.getScale());
p = Math.max(p, then.getPrecision());
d = Math.max(d, then.getDisplaySize());
}
}
}
if (args.length % 2 == 0) {
Expression elsePart = args[args.length - 1];
if (elsePart != ValueExpression.getNull()) {
int type = elsePart.getType();
if (type != Value.UNKNOWN && type != Value.NULL) {
t = Value.getHigherOrder(t, type);
s = Math.max(s, elsePart.getScale());
p = Math.max(p, elsePart.getPrecision());
d = Math.max(d, elsePart.getDisplaySize());
}
}
} }
if (t == Value.UNKNOWN) { if (t == Value.UNKNOWN) {
t = Value.STRING; t = Value.STRING;
...@@ -2086,6 +2150,19 @@ public class Function extends Expression implements FunctionCall { ...@@ -2086,6 +2150,19 @@ public class Function extends Expression implements FunctionCall {
@Override @Override
public String getSQL() { public String getSQL() {
StatementBuilder buff = new StatementBuilder(info.name); StatementBuilder buff = new StatementBuilder(info.name);
if (info.type == CASE) {
if (args[0] != null) {
buff.append(" ").append(args[0].getSQL());
}
for (int i = 1, len = args.length - 1; i < len; i += 2) {
buff.append(" WHEN ").append(args[i].getSQL());
buff.append(" THEN ").append(args[i + 1].getSQL());
}
if (args.length % 2 == 0) {
buff.append(" ELSE ").append(args[args.length - 1].getSQL());
}
return buff.append(" END").toString();
}
buff.append('('); buff.append('(');
switch (info.type) { switch (info.type) {
case CAST: { case CAST: {
......
...@@ -4,6 +4,24 @@ ...@@ -4,6 +4,24 @@
-- Initial Developer: H2 Group -- Initial Developer: H2 Group
-- --
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
create sequence seq;
> ok
select case seq.nextval when 2 then 'two' when 3 then 'three' when 1 then 'one' else 'other' end result from dual;
> RESULT
> ------
> one
> rows: 1
drop sequence seq;
> ok
select decode(1, 1, '1', 1, '11') r from dual;
> R
> -
> 1
> rows: 1
create table test(x int); create table test(x int);
> ok > ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论