/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.table;

import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.function.FunctionCall;
import org.h2.expression.function.TableFunction;
import org.h2.index.FunctionIndex;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.schema.Schema;
import org.h2.value.Value;
import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;

A table backed by a system or user-defined function that returns a result set.
/** * 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 ResultInterface cachedResult; private Value cachedValue; public FunctionTable(Schema schema, Session session, Expression functionExpr, FunctionCall function) { super(schema, 0, function.getName(), false, true); 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.getValueType(); 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()); } ResultInterface result = template.getResult(); int columnCount = result.getVisibleColumnCount(); Column[] cols = new Column[columnCount]; for (int i = 0; i < columnCount; i++) { cols[i] = new Column(result.getColumnName(i), result.getColumnType(i)); } setColumns(cols); } @Override public boolean lock(Session session, boolean exclusive, boolean forceLockEvenInMvcc) { // nothing to do return false; } @Override public void close(Session session) { // nothing to do } @Override public void unlock(Session s) { // nothing to do } @Override public boolean isLockedExclusively() { return false; } @Override public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType, boolean create, String indexComment) { throw DbException.getUnsupportedException("ALIAS"); } @Override public void removeRow(Session session, Row row) { throw DbException.getUnsupportedException("ALIAS"); } @Override public void truncate(Session session) { throw DbException.getUnsupportedException("ALIAS"); } @Override public boolean canDrop() { throw DbException.throwInternalError(toString()); } @Override public void addRow(Session session, Row row) { throw DbException.getUnsupportedException("ALIAS"); } @Override public void checkSupportAlter() { throw DbException.getUnsupportedException("ALIAS"); } @Override public TableType getTableType() { return null; } @Override public Index getScanIndex(Session session) { return new FunctionIndex(this, IndexColumn.wrap(columns)); } @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 String getCreateSQL() { return null; } @Override public String getDropSQL() { return null; } @Override public void checkRename() { throw DbException.getUnsupportedException("ALIAS"); }
Read the result from the function. This method buffers the result in a temporary file.
Params:
  • session – the session
Returns:the result
/** * 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; } ResultInterface result = v.getResult(); if (function.isDeterministic()) { cachedResult = result; cachedValue = v; } return result; }
Read the result set from the function. This method doesn't cache.
Params:
  • session – the session
Returns:the result
/** * Read the result set from the function. This method doesn't cache. * * @param session the session * @return the result */
public ResultInterface getResultSet(Session session) { ValueResultSet v = getValueResultSet(session); return v == null ? null : v.getResult(); } 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 long getMaxDataModificationId() { // TODO optimization: table-as-a-function currently doesn't know the // last modified date return Long.MAX_VALUE; } @Override public Index getUniqueIndex() { return null; } @Override public String getSQL(boolean alwaysQuote) { return function.getSQL(alwaysQuote); } @Override public StringBuilder getSQL(StringBuilder builder, boolean alwaysQuote) { return builder.append(function.getSQL(alwaysQuote)); } @Override public long getRowCountApproximation() { return rowCount; } @Override public long getDiskSpaceUsed() { return 0; } @Override public boolean isDeterministic() { return function.isDeterministic(); } @Override public boolean canReference() { return false; } }