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.util.ArrayList; |
9 | |
10 | import org.h2.api.ErrorCode; |
11 | import org.h2.engine.Database; |
12 | import org.h2.engine.Session; |
13 | import org.h2.message.DbException; |
14 | import org.h2.result.LocalResult; |
15 | import org.h2.result.ResultInterface; |
16 | import org.h2.table.Column; |
17 | import org.h2.tools.SimpleResultSet; |
18 | import org.h2.util.MathUtils; |
19 | import org.h2.util.StatementBuilder; |
20 | import org.h2.value.DataType; |
21 | import org.h2.value.Value; |
22 | import org.h2.value.ValueArray; |
23 | import org.h2.value.ValueNull; |
24 | import org.h2.value.ValueResultSet; |
25 | |
26 | /** |
27 | * Implementation of the functions TABLE(..) and TABLE_DISTINCT(..). |
28 | */ |
29 | public class TableFunction extends Function { |
30 | private final boolean distinct; |
31 | private final long rowCount; |
32 | private Column[] columnList; |
33 | |
34 | TableFunction(Database database, FunctionInfo info, long rowCount) { |
35 | super(database, info); |
36 | distinct = info.type == Function.TABLE_DISTINCT; |
37 | this.rowCount = rowCount; |
38 | } |
39 | |
40 | @Override |
41 | public Value getValue(Session session) { |
42 | return getTable(session, args, false, distinct); |
43 | } |
44 | |
45 | @Override |
46 | protected void checkParameterCount(int len) { |
47 | if (len < 1) { |
48 | throw DbException.get(ErrorCode.INVALID_PARAMETER_COUNT_2, getName(), ">0"); |
49 | } |
50 | } |
51 | |
52 | @Override |
53 | public String getSQL() { |
54 | StatementBuilder buff = new StatementBuilder(getName()); |
55 | buff.append('('); |
56 | int i = 0; |
57 | for (Expression e : args) { |
58 | buff.appendExceptFirst(", "); |
59 | buff.append(columnList[i++].getCreateSQL()).append('=').append(e.getSQL()); |
60 | } |
61 | return buff.append(')').toString(); |
62 | } |
63 | |
64 | |
65 | @Override |
66 | public String getName() { |
67 | return distinct ? "TABLE_DISTINCT" : "TABLE"; |
68 | } |
69 | |
70 | @Override |
71 | public ValueResultSet getValueForColumnList(Session session, |
72 | Expression[] nullArgs) { |
73 | return getTable(session, args, true, false); |
74 | } |
75 | |
76 | public void setColumns(ArrayList<Column> columns) { |
77 | this.columnList = new Column[columns.size()]; |
78 | columns.toArray(columnList); |
79 | } |
80 | |
81 | private ValueResultSet getTable(Session session, Expression[] argList, |
82 | boolean onlyColumnList, boolean distinctRows) { |
83 | int len = columnList.length; |
84 | Expression[] header = new Expression[len]; |
85 | Database db = session.getDatabase(); |
86 | for (int i = 0; i < len; i++) { |
87 | Column c = columnList[i]; |
88 | ExpressionColumn col = new ExpressionColumn(db, c); |
89 | header[i] = col; |
90 | } |
91 | LocalResult result = new LocalResult(session, header, len); |
92 | if (distinctRows) { |
93 | result.setDistinct(); |
94 | } |
95 | if (!onlyColumnList) { |
96 | Value[][] list = new Value[len][]; |
97 | int rows = 0; |
98 | for (int i = 0; i < len; i++) { |
99 | Value v = argList[i].getValue(session); |
100 | if (v == ValueNull.INSTANCE) { |
101 | list[i] = new Value[0]; |
102 | } else { |
103 | ValueArray array = (ValueArray) v.convertTo(Value.ARRAY); |
104 | Value[] l = array.getList(); |
105 | list[i] = l; |
106 | rows = Math.max(rows, l.length); |
107 | } |
108 | } |
109 | for (int row = 0; row < rows; row++) { |
110 | Value[] r = new Value[len]; |
111 | for (int j = 0; j < len; j++) { |
112 | Value[] l = list[j]; |
113 | Value v; |
114 | if (l.length <= row) { |
115 | v = ValueNull.INSTANCE; |
116 | } else { |
117 | Column c = columnList[j]; |
118 | v = l[row]; |
119 | v = c.convert(v); |
120 | v = v.convertPrecision(c.getPrecision(), false); |
121 | v = v.convertScale(true, c.getScale()); |
122 | } |
123 | r[j] = v; |
124 | } |
125 | result.addRow(r); |
126 | } |
127 | } |
128 | result.done(); |
129 | ValueResultSet vr = ValueResultSet.get(getSimpleResultSet(result, |
130 | Integer.MAX_VALUE)); |
131 | return vr; |
132 | } |
133 | |
134 | private static SimpleResultSet getSimpleResultSet(ResultInterface rs, |
135 | int maxrows) { |
136 | int columnCount = rs.getVisibleColumnCount(); |
137 | SimpleResultSet simple = new SimpleResultSet(); |
138 | simple.setAutoClose(false); |
139 | for (int i = 0; i < columnCount; i++) { |
140 | String name = rs.getColumnName(i); |
141 | int sqlType = DataType.convertTypeToSQLType(rs.getColumnType(i)); |
142 | int precision = MathUtils.convertLongToInt(rs.getColumnPrecision(i)); |
143 | int scale = rs.getColumnScale(i); |
144 | simple.addColumn(name, sqlType, precision, scale); |
145 | } |
146 | rs.reset(); |
147 | for (int i = 0; i < maxrows && rs.next(); i++) { |
148 | Object[] list = new Object[columnCount]; |
149 | for (int j = 0; j < columnCount; j++) { |
150 | list[j] = rs.currentRow()[j].getObject(); |
151 | } |
152 | simple.addRow(list); |
153 | } |
154 | return simple; |
155 | } |
156 | |
157 | public long getRowCount() { |
158 | return rowCount; |
159 | } |
160 | |
161 | @Override |
162 | public Expression[] getExpressionColumns(Session session) { |
163 | return getExpressionColumns(session, |
164 | getTable(session, getArgs(), true, false).getResultSet()); |
165 | } |
166 | |
167 | } |