提交 49c8f3f4 authored 作者: Thomas Mueller's avatar Thomas Mueller

Column aliases can now be used in GROUP BY and HAVING.

上级 a7fbae73
......@@ -16,7 +16,7 @@ Change Log
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>-
<ul><li>Column aliases can now be used in GROUP BY and HAVING.
</li></ul>
<h2>Version 1.0.77 (2008-08-16)</h2>
......
......@@ -65,6 +65,11 @@ Linux Package Manager</a><br />
<h2>Products and Projects</h2>
<p><a href="http://www.axiomstack.com/">
Axiom Stack</a><br />
A web framework that let's you write dynamic web applications with Zen-like simplicity.
</p>
<p><a href="http://cayenne.apache.org/">
Apache Cayenne</a><br />
Open source persistence framework providing object-relational mapping (ORM) and remoting services.
......@@ -115,10 +120,20 @@ DbVisualizer</a><br />
Database tool.
</p>
<p><a href="http://antilogics.com/epictetus.html">
Epictetus</a><br />
Free cross platform database tool.
</p>
<p><a href="http://executequery.org">
Execute Query</a><br />
Database utility written in Java.
</p>
<p><a href="http://fabric3.codehaus.org/">
Fabric3</a><br />
Fabric3 is a project implementing a federated service network based on the Service Component Architecture specification (http://www.osoa.org).
</p>
<p><a href="http://www.fluxcorp.com">
Flux</a><br />
......@@ -273,6 +288,12 @@ databases, ontology-based domain modeling, and management of the
distributed enterprise.
</p>
<p><a href="http://www.ontoprise.de/">
Ontoprise OntoBroker</a><br />
SemanticWeb-Middleware. It supports all W3C Semantic Web recommendations:
OWL, RDF, RDFS, SPARQL, and F-Logic.
</p>
<p><a href="http://www.orionserver.com/">
Orion</a><br />
J2EE Application Server.
......@@ -293,6 +314,11 @@ Pickle</a><br />
Pickle is a Java library containing classes for persistence, concurrency, and logging.
</p>
<p><a href="http://www.pimansoft.com">
Piman</a><br />
Water treatment projects data management.
</p>
<p><a href="http://www.polepos.org">
PolePosition</a><br />
Open source database benchmark.
......
......@@ -77,6 +77,7 @@ public class Select extends Query {
private boolean isPrepared, checkInit;
private boolean sortUsingIndex;
private SortOrder sort;
private int currentGroupRowId;
public Select(Session session) {
super(session);
......@@ -125,6 +126,10 @@ public class Select extends Query {
public HashMap getCurrentGroup() {
return currentGroup;
}
public int getCurrentGroupRowId() {
return currentGroupRowId;
}
public void setOrder(ObjectArray order) {
orderList = order;
......@@ -168,6 +173,7 @@ public class Select extends Query {
previousKeyValues = keyValues;
currentGroup = new HashMap();
}
currentGroupRowId++;
for (int i = 0; i < columnCount; i++) {
if (groupByExpression == null || !groupByExpression[i]) {
......@@ -194,22 +200,34 @@ public class Select extends Query {
Expression expr = (Expression) expressions.get(j);
row[j] = expr.getValue(session);
}
if (havingIndex > 0) {
if (isHavingNullOrFalse(row)) {
return;
}
row = keepOnlyDistinct(row, columnCount);
result.addRow(row);
}
private Value[] keepOnlyDistinct(Value[] row, int columnCount) {
if (columnCount == distinctColumnCount) {
return row;
}
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
return r2;
}
private boolean isHavingNullOrFalse(Value[] row) throws SQLException {
if (havingIndex >= 0) {
Value v = row[havingIndex];
if (v == ValueNull.INSTANCE) {
return;
return true;
}
if (!Boolean.TRUE.equals(v.getBoolean())) {
return;
return true;
}
}
if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2;
}
result.addRow(row);
return false;
}
private Index getGroupSortedIndex() {
......@@ -295,6 +313,7 @@ public class Select extends Query {
groups.put(key, values);
}
currentGroup = values;
currentGroupRowId++;
int len = columnCount;
for (int i = 0; i < len; i++) {
if (groupByExpression == null || !groupByExpression[i]) {
......@@ -326,21 +345,10 @@ public class Select extends Query {
Expression expr = (Expression) expressions.get(j);
row[j] = expr.getValue(session);
}
if (havingIndex > 0) {
Value v = row[havingIndex];
if (v == ValueNull.INSTANCE) {
continue;
}
if (!Boolean.TRUE.equals(v.getBoolean())) {
continue;
}
}
if (columnCount != distinctColumnCount) {
// remove columns so that 'distinct' can filter duplicate rows
Value[] r2 = new Value[distinctColumnCount];
ObjectUtils.arrayCopy(row, r2, distinctColumnCount);
row = r2;
if (isHavingNullOrFalse(row)) {
continue;
}
row = keepOnlyDistinct(row, columnCount);
result.addRow(row);
}
}
......@@ -604,7 +612,7 @@ public class Select extends Query {
ObjectArray expressionSQL;
if (orderList != null || group != null) {
expressionSQL = new ObjectArray();
for (int i = 0; i < expressions.size(); i++) {
for (int i = 0; i < visibleColumnCount; i++) {
Expression expr = (Expression) expressions.get(i);
expr = expr.getNonAliasExpression();
String sql = expr.getSQL();
......@@ -625,8 +633,10 @@ public class Select extends Query {
havingIndex = -1;
}
// first visible columns, then order by, then having,
// and group by at the end
// first the select list (visible columns),
// then 'ORDER BY' expressions,
// then 'HAVING' expressions,
// and 'GROUP BY' expressions at the end
if (group != null) {
groupIndex = new int[group.size()];
for (int i = 0; i < group.size(); i++) {
......@@ -640,6 +650,16 @@ public class Select extends Query {
break;
}
}
if (found < 0) {
// special case: GROUP BY a column alias
for (int j = 0; j < expressionSQL.size(); j++) {
Expression e = (Expression) expressions.get(j);
if (sql.equals(e.getAlias())) {
found = j;
break;
}
}
}
if (found < 0) {
int index = expressions.size();
groupIndex[i] = index;
......@@ -665,6 +685,11 @@ public class Select extends Query {
condition.mapColumns(f, 0);
}
}
if (havingIndex >= 0) {
Expression expr = (Expression) expressions.get(havingIndex);
SelectListColumnResolver res = new SelectListColumnResolver(this);
expr.mapColumns(res, 0);
}
checkInit = true;
}
......
/*
* Copyright 2004-2008 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.command.dml;
import java.sql.SQLException;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
/**
* This class represents a column resolver for the column list of a SELECT
* statement. It is used to resolve select column aliases in the HAVING clause.
* Example:
* <p>
* SELECT X/3 AS A, COUNT(*) FROM SYSTEM_RANGE(1, 10) GROUP BY A HAVING A>2;
* </p>
*
* @author Thomas Mueller
*/
public class SelectListColumnResolver implements ColumnResolver {
private Select select;
private Expression[] expressions;
private Column[] columns;
SelectListColumnResolver(Select select) {
this.select = select;
int columnCount = select.getColumnCount();
columns = new Column[columnCount];
expressions = new Expression[columnCount];
ObjectArray columnList = select.getExpressions();
for (int i = 0; i < columnCount; i++) {
Expression expr = (Expression) columnList.get(i);
Column column = new Column(expr.getAlias(), Value.NULL);
column.setTable(null, i);
columns[i] = column;
expressions[i] = expr.getNonAliasExpression();
}
}
public Column[] getColumns() {
return columns;
}
public String getSchemaName() {
return null;
}
public Select getSelect() {
return select;
}
public Column[] getSystemColumns() {
return null;
}
public String getTableAlias() {
return null;
}
public TableFilter getTableFilter() {
return null;
}
public Value getValue(Column column) throws SQLException {
return null;
}
public Expression optimize(ExpressionColumn expressionColumn, Column column) {
return expressions[column.getColumnId()];
}
}
......@@ -125,6 +125,7 @@ public class Aggregate extends Expression {
private int dataType, scale;
private long precision;
private int displaySize;
private int lastGroupRowId;
/**
* Create a new aggregate object.
......@@ -222,6 +223,14 @@ public class Aggregate extends Expression {
// this is a different level (the enclosing query)
return;
}
int groupRowId = select.getCurrentGroupRowId();
if (lastGroupRowId == groupRowId) {
// already visited
return;
}
lastGroupRowId = groupRowId;
AggregateData data = (AggregateData) group.get(this);
if (data == null) {
data = new AggregateData(type);
......
......@@ -96,7 +96,11 @@ public abstract class Expression {
/**
* Update an aggregate value.
* This method is called at statement execution time once for each row.
* This method is called at statement execution time.
* It is usually called once for each row, but if the expression is used multiple
* times (for example in the column list, and as part of the HAVING expression)
* it is called multiple times - the row counter needs to be used to make sure
* the internal state is only updated once.
*
* @param session the session
*/
......
......@@ -124,7 +124,7 @@ public class ExpressionColumn extends Expression {
}
throw Message.getSQLException(ErrorCode.COLUMN_NOT_FOUND_1, name);
}
return this;
return resolver.optimize(this, column);
}
public void updateAggregate(Session session) throws SQLException {
......@@ -218,7 +218,7 @@ public class ExpressionColumn extends Expression {
}
public String getAlias() {
return column.getName();
return column == null ? null : column.getName();
}
public boolean isAutoIncrement() {
......
......@@ -35,6 +35,7 @@ public class JavaAggregate extends Expression {
private int[] argTypes;
private int dataType;
private Connection userConnection;
private int lastGroupRowId;
public JavaAggregate(UserAggregate userAggregate, Expression[] args, Select select) {
this.userAggregate = userAggregate;
......@@ -154,6 +155,14 @@ public class JavaAggregate extends Expression {
// this is a different level (the enclosing query)
return;
}
int groupRowId = select.getCurrentGroupRowId();
if (lastGroupRowId == groupRowId) {
// already visited
return;
}
lastGroupRowId = groupRowId;
AggregateFunction agg = (AggregateFunction) group.get(this);
if (agg == null) {
agg = getInstance();
......
......@@ -172,7 +172,7 @@ public class Column {
* @param table the table
* @param columnId the column index
*/
void setTable(Table table, int columnId) {
public void setTable(Table table, int columnId) {
this.table = table;
this.columnId = columnId;
}
......
......@@ -9,6 +9,8 @@ package org.h2.table;
import java.sql.SQLException;
import org.h2.command.dml.Select;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.value.Value;
/**
......@@ -67,4 +69,13 @@ public interface ColumnResolver {
*/
Select getSelect();
/**
* Get the expression that represents this column.
*
* @param expressionColumn the expression column
* @param column the column
* @return the optimized expression
*/
Expression optimize(ExpressionColumn expressionColumn, Column column);
}
......@@ -7,6 +7,8 @@
package org.h2.table;
import org.h2.command.dml.Select;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.value.Value;
/**
......@@ -54,4 +56,8 @@ public class SingleColumnResolver implements ColumnResolver {
return null;
}
public Expression optimize(ExpressionColumn expressionColumn, Column column) {
return expressionColumn;
}
}
......@@ -15,6 +15,7 @@ import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexCondition;
......@@ -696,4 +697,8 @@ public class TableFilter implements ColumnResolver {
this.alias = alias;
}
public Expression optimize(ExpressionColumn expressionColumn, Column column) {
return expressionColumn;
}
}
......@@ -272,19 +272,6 @@ java org.h2.test.TestAll timer
/*
drop table test;
create table test(id int);
insert into test values(null);
insert into test values(1);
select * from test where id not in (select id from test where 1=0);
select * from test where null not in (select id from test where 1=0);
select * from test where not (id in (select id from test where 1=0));
select * from test where not (null in (select id from test where 1=0));
HSQLDB, MySQL, H2: empty
PostgreSQL, Derby: 1; null
Support large updates (use the transaction log to undo).
H2 Console: support single file upload and directory download (optional)
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
select x/3 as a, count(*) c from system_range(1, 10) group by a having c>2;
> A C
> - -
> 1 3
> 2 3
> rows: 2
create table test(id int);
> ok
insert into test values(1), (2);
> update count: 2
select id+1 as x, count(*) from test group by x;
> X COUNT(*)
> - --------
> 2 1
> 3 1
> rows: 2
select 1 as id, id as b, count(*) from test group by id;
> ID B COUNT(*)
> -- - --------
> 1 1 1
> 1 2 1
> rows: 2
select id+1 as x, count(*) from test group by -x;
> exception
select id+1 as x, count(*) from test group by x having x>2;
> exception
select id+1 as x, count(*) from test group by 1;
> exception
drop table test;
> ok
create table test(t0 timestamp(0), t1 timestamp(1), t4 timestamp(4));
> ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论