1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.expression; |
7 | |
8 | import java.sql.Connection; |
9 | import java.sql.SQLException; |
10 | import java.util.HashMap; |
11 | import org.h2.api.Aggregate; |
12 | import org.h2.api.ErrorCode; |
13 | import org.h2.command.Parser; |
14 | import org.h2.command.dml.Select; |
15 | import org.h2.engine.Session; |
16 | import org.h2.engine.UserAggregate; |
17 | import org.h2.message.DbException; |
18 | import org.h2.table.ColumnResolver; |
19 | import org.h2.table.TableFilter; |
20 | import org.h2.util.StatementBuilder; |
21 | import org.h2.value.DataType; |
22 | import org.h2.value.Value; |
23 | import org.h2.value.ValueNull; |
24 | |
25 | /** |
26 | * This class wraps a user-defined aggregate. |
27 | */ |
28 | public class JavaAggregate extends Expression { |
29 | |
30 | private final UserAggregate userAggregate; |
31 | private final Select select; |
32 | private final Expression[] args; |
33 | private int[] argTypes; |
34 | private int dataType; |
35 | private Connection userConnection; |
36 | private int lastGroupRowId; |
37 | |
38 | public JavaAggregate(UserAggregate userAggregate, Expression[] args, |
39 | Select select) { |
40 | this.userAggregate = userAggregate; |
41 | this.args = args; |
42 | this.select = select; |
43 | } |
44 | |
45 | @Override |
46 | public int getCost() { |
47 | int cost = 5; |
48 | for (Expression e : args) { |
49 | cost += e.getCost(); |
50 | } |
51 | return cost; |
52 | } |
53 | |
54 | @Override |
55 | public long getPrecision() { |
56 | return Integer.MAX_VALUE; |
57 | } |
58 | |
59 | @Override |
60 | public int getDisplaySize() { |
61 | return Integer.MAX_VALUE; |
62 | } |
63 | |
64 | @Override |
65 | public int getScale() { |
66 | return DataType.getDataType(dataType).defaultScale; |
67 | } |
68 | |
69 | @Override |
70 | public String getSQL() { |
71 | StatementBuilder buff = new StatementBuilder(); |
72 | buff.append(Parser.quoteIdentifier(userAggregate.getName())).append('('); |
73 | for (Expression e : args) { |
74 | buff.appendExceptFirst(", "); |
75 | buff.append(e.getSQL()); |
76 | } |
77 | return buff.append(')').toString(); |
78 | } |
79 | |
80 | @Override |
81 | public int getType() { |
82 | return dataType; |
83 | } |
84 | |
85 | @Override |
86 | public boolean isEverything(ExpressionVisitor visitor) { |
87 | switch(visitor.getType()) { |
88 | case ExpressionVisitor.DETERMINISTIC: |
89 | // TODO optimization: some functions are deterministic, but we don't |
90 | // know (no setting for that) |
91 | case ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL: |
92 | // user defined aggregate functions can not be optimized |
93 | return false; |
94 | case ExpressionVisitor.GET_DEPENDENCIES: |
95 | visitor.addDependency(userAggregate); |
96 | break; |
97 | default: |
98 | } |
99 | for (Expression e : args) { |
100 | if (e != null && !e.isEverything(visitor)) { |
101 | return false; |
102 | } |
103 | } |
104 | return true; |
105 | } |
106 | |
107 | @Override |
108 | public void mapColumns(ColumnResolver resolver, int level) { |
109 | for (Expression arg : args) { |
110 | arg.mapColumns(resolver, level); |
111 | } |
112 | } |
113 | |
114 | @Override |
115 | public Expression optimize(Session session) { |
116 | userConnection = session.createConnection(false); |
117 | int len = args.length; |
118 | argTypes = new int[len]; |
119 | for (int i = 0; i < len; i++) { |
120 | Expression expr = args[i]; |
121 | args[i] = expr.optimize(session); |
122 | int type = expr.getType(); |
123 | argTypes[i] = type; |
124 | } |
125 | try { |
126 | Aggregate aggregate = getInstance(); |
127 | dataType = aggregate.getInternalType(argTypes); |
128 | } catch (SQLException e) { |
129 | throw DbException.convert(e); |
130 | } |
131 | return this; |
132 | } |
133 | |
134 | @Override |
135 | public void setEvaluatable(TableFilter tableFilter, boolean b) { |
136 | for (Expression e : args) { |
137 | e.setEvaluatable(tableFilter, b); |
138 | } |
139 | } |
140 | |
141 | private Aggregate getInstance() throws SQLException { |
142 | Aggregate agg = userAggregate.getInstance(); |
143 | agg.init(userConnection); |
144 | return agg; |
145 | } |
146 | |
147 | @Override |
148 | public Value getValue(Session session) { |
149 | HashMap<Expression, Object> group = select.getCurrentGroup(); |
150 | if (group == null) { |
151 | throw DbException.get(ErrorCode.INVALID_USE_OF_AGGREGATE_FUNCTION_1, getSQL()); |
152 | } |
153 | try { |
154 | Aggregate agg = (Aggregate) group.get(this); |
155 | if (agg == null) { |
156 | agg = getInstance(); |
157 | } |
158 | Object obj = agg.getResult(); |
159 | if (obj == null) { |
160 | return ValueNull.INSTANCE; |
161 | } |
162 | return DataType.convertToValue(session, obj, dataType); |
163 | } catch (SQLException e) { |
164 | throw DbException.convert(e); |
165 | } |
166 | } |
167 | |
168 | @Override |
169 | public void updateAggregate(Session session) { |
170 | HashMap<Expression, Object> group = select.getCurrentGroup(); |
171 | if (group == null) { |
172 | // this is a different level (the enclosing query) |
173 | return; |
174 | } |
175 | |
176 | int groupRowId = select.getCurrentGroupRowId(); |
177 | if (lastGroupRowId == groupRowId) { |
178 | // already visited |
179 | return; |
180 | } |
181 | lastGroupRowId = groupRowId; |
182 | |
183 | Aggregate agg = (Aggregate) group.get(this); |
184 | try { |
185 | if (agg == null) { |
186 | agg = getInstance(); |
187 | group.put(this, agg); |
188 | } |
189 | Object[] argValues = new Object[args.length]; |
190 | Object arg = null; |
191 | for (int i = 0, len = args.length; i < len; i++) { |
192 | Value v = args[i].getValue(session); |
193 | v = v.convertTo(argTypes[i]); |
194 | arg = v.getObject(); |
195 | argValues[i] = arg; |
196 | } |
197 | if (args.length == 1) { |
198 | agg.add(arg); |
199 | } else { |
200 | agg.add(argValues); |
201 | } |
202 | } catch (SQLException e) { |
203 | throw DbException.convert(e); |
204 | } |
205 | } |
206 | |
207 | } |