/* * Copyright 2014-2015 the original author or authors * * Licensed under the Apache License, Version 2.0 (the “License”); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an “AS IS” BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wplatform.ddal.dbobject.table; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import com.wplatform.ddal.command.expression.Expression; import com.wplatform.ddal.command.expression.FunctionCall; import com.wplatform.ddal.command.expression.TableFunction; import com.wplatform.ddal.dbobject.index.Index; import com.wplatform.ddal.dbobject.index.IndexMate; import com.wplatform.ddal.dbobject.index.IndexType; import com.wplatform.ddal.dbobject.schema.Schema; import com.wplatform.ddal.engine.Session; import com.wplatform.ddal.message.DbException; import com.wplatform.ddal.message.ErrorCode; import com.wplatform.ddal.result.LocalResult; import com.wplatform.ddal.result.ResultInterface; import com.wplatform.ddal.value.DataType; import com.wplatform.ddal.value.Value; import com.wplatform.ddal.value.ValueNull; import com.wplatform.ddal.value.ValueResultSet; /** * A table backed by a system or user-defined function that returns a result * set. */ public class FunctionTable extends Table { private final FunctionCall function; private final long rowCount; private Expression functionExpr; private LocalResult cachedResult; private Value cachedValue; public FunctionTable(Schema schema, Session session, Expression functionExpr, FunctionCall function) { super(schema, 0, function.getName()); this.functionExpr = functionExpr; this.function = function; if (function instanceof TableFunction) { rowCount = ((TableFunction) function).getRowCount(); } else { rowCount = Long.MAX_VALUE; } function.optimize(session); int type = function.getType(); if (type != Value.RESULT_SET) { throw DbException.get( ErrorCode.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName()); } Expression[] args = function.getArgs(); int numParams = args.length; Expression[] columnListArgs = new Expression[numParams]; for (int i = 0; i < numParams; i++) { args[i] = args[i].optimize(session); columnListArgs[i] = args[i]; } ValueResultSet template = function.getValueForColumnList( session, columnListArgs); if (template == null) { throw DbException.get( ErrorCode.FUNCTION_MUST_RETURN_RESULT_SET_1, function.getName()); } ResultSet rs = template.getResultSet(); try { ResultSetMetaData meta = rs.getMetaData(); int columnCount = meta.getColumnCount(); Column[] cols = new Column[columnCount]; for (int i = 0; i < columnCount; i++) { cols[i] = new Column(meta.getColumnName(i + 1), DataType.getValueTypeFromResultSet(meta, i + 1), meta.getPrecision(i + 1), meta.getScale(i + 1), meta.getColumnDisplaySize(i + 1)); } setColumns(cols); } catch (SQLException e) { throw DbException.convert(e); } } @Override public String getTableType() { return null; } @Override public Index getScanIndex(Session session) { return new IndexMate(this, 0, null, IndexColumn.wrap(columns), IndexType.createScan()); } @Override public ArrayList<Index> getIndexes() { return null; } @Override public boolean canGetRowCount() { return rowCount != Long.MAX_VALUE; } @Override public long getRowCount(Session session) { return rowCount; } @Override public void checkRename() { throw DbException.getUnsupportedException("ALIAS"); } /** * Read the result from the function. This method buffers the result in a * temporary file. * * @param session the session * @return the result */ public ResultInterface getResult(Session session) { ValueResultSet v = getValueResultSet(session); if (v == null) { return null; } if (cachedResult != null && cachedValue == v) { cachedResult.reset(); return cachedResult; } LocalResult result = LocalResult.read(session, v.getResultSet(), 0); if (function.isDeterministic()) { cachedResult = result; cachedValue = v; } return result; } /** * Read the result set from the function. This method doesn't cache. * * @param session the session * @return the result set */ public ResultSet getResultSet(Session session) { ValueResultSet v = getValueResultSet(session); return v == null ? null : v.getResultSet(); } private ValueResultSet getValueResultSet(Session session) { functionExpr = functionExpr.optimize(session); Value v = functionExpr.getValue(session); if (v == ValueNull.INSTANCE) { return null; } return (ValueResultSet) v; } public boolean isBufferResultSetToLocalTemp() { return function.isBufferResultSetToLocalTemp(); } @Override public Index getUniqueIndex() { return null; } @Override public String getSQL() { return function.getSQL(); } @Override public long getRowCountApproximation() { return rowCount; } @Override public boolean isDeterministic() { return function.isDeterministic(); } }