package com.tesora.dve.sql.infoschema.direct;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.tesora.dve.common.catalog.CatalogEntity;
import com.tesora.dve.resultset.ColumnSet;
import com.tesora.dve.resultset.IntermediateResultSet;
import com.tesora.dve.resultset.ProjectionInfo;
import com.tesora.dve.resultset.ResultRow;
import com.tesora.dve.sql.infoschema.InfoView;
import com.tesora.dve.sql.infoschema.InformationSchemaColumn;
import com.tesora.dve.sql.infoschema.InformationSchemaException;
import com.tesora.dve.sql.infoschema.ShowOptions;
import com.tesora.dve.sql.infoschema.ShowSchemaBehavior;
import com.tesora.dve.sql.infoschema.engine.LogicalSchemaQueryEngine;
import com.tesora.dve.sql.infoschema.engine.ViewQuery;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ExpressionAlias;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.FunctionCall;
import com.tesora.dve.sql.node.expression.LiteralExpression;
import com.tesora.dve.sql.node.expression.NameAlias;
import com.tesora.dve.sql.node.expression.TableInstance;
import com.tesora.dve.sql.node.expression.VariableInstance;
import com.tesora.dve.sql.node.structural.SortingSpecification;
import com.tesora.dve.sql.schema.FunctionName;
import com.tesora.dve.sql.schema.Name;
import com.tesora.dve.sql.schema.PEViewTable;
import com.tesora.dve.sql.schema.QualifiedName;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.UnqualifiedName;
import com.tesora.dve.sql.schema.mt.IPETenant;
import com.tesora.dve.sql.statement.Statement;
import com.tesora.dve.sql.statement.ddl.SchemaQueryStatement;
import com.tesora.dve.sql.statement.dml.AliasInformation;
import com.tesora.dve.sql.statement.dml.SelectStatement;
import com.tesora.dve.sql.transform.ColumnInstanceCollector;
import com.tesora.dve.sql.transform.VariableInstanceCollector;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.Pair;
import com.tesora.dve.sql.util.ResizableArray;
import com.tesora.dve.sql.util.UnaryFunction;
public class ViewShowSchemaTable extends ViewInformationSchemaTable implements ShowSchemaBehavior {
private final TemporaryTableHandler tempTableHandler;
protected boolean[] scopes = null;
private static final String[] scopeNames = new String[] { "dbn", "tn" };
public ViewShowSchemaTable(SchemaContext sc, InfoView view,
PEViewTable viewTab, UnqualifiedName viewName, UnqualifiedName pluralViewName,
boolean privileged, boolean extension,
List<DirectColumnGenerator> columnGenerators,
TemporaryTableHandler tempTableHandler) {
super(sc, view, viewTab, viewName, pluralViewName, privileged, extension, columnGenerators);
this.tempTableHandler = tempTableHandler;
List<VariableInstance> vars = VariableInstanceCollector.getVariables(viewTab.getView(sc).getViewDefinition(sc, viewTab, false));
if (!vars.isEmpty()) {
for(VariableInstance vi : vars) {
String name = vi.getVariableName().getUnquotedName().get();
for(int i = 0; i < scopeNames.length; i++) {
if (name.startsWith(scopeNames[i])) {
if (scopes == null)
scopes = new boolean[] { false, false };
scopes[i] = true;
}
}
}
}
}
@Override
public Statement buildShowPlural(SchemaContext sc, List<Name> scoping,
ExpressionNode likeExpr, ExpressionNode whereExpr,
ShowOptions options) {
if (tempTableHandler != null) {
Statement out = buildTempTableResults(sc,scoping,likeExpr,whereExpr,options);
if (out != null)
return out;
}
assertPrivilege(sc);
TableInstance ti = null;
if (whereExpr != null) {
ListSet<ColumnInstance> cols = ColumnInstanceCollector.getColumnInstances(whereExpr);
ti = cols.get(0).getTableInstance();
} else {
ti = new TableInstance(this,new UnqualifiedName("a"),new UnqualifiedName("a"),sc.getNextTable(),false);
}
HashMap<String,Object> params = new HashMap<String,Object>();
SelectStatement ss = buildSkeleton(sc,ti,options,params);
if (whereExpr != null)
ss.setWhereClause(whereExpr);
else if (likeExpr != null) {
FunctionCall fc = new FunctionCall(FunctionName.makeLike(),new ColumnInstance(getIdentColumn().getName(),getIdentColumn(),ti),
likeExpr);
ss.setWhereClause(fc);
}
ResizableArray<DirectInformationSchemaColumn> obcs = getOrderByColumns();
if (obcs.size() > 0) {
List<SortingSpecification> sorts = new ArrayList<SortingSpecification>();
for(int i = 0; i < obcs.size(); i++) {
DirectInformationSchemaColumn col = obcs.get(i);
if (col == null) continue;
ColumnInstance ci = new ColumnInstance(col.getName(), col, ti);
SortingSpecification sort = new SortingSpecification(ci,true);
sort.setOrdering(true);
sorts.add(sort);
}
ss.setOrderBy(sorts);
}
normalizeScoping(sc,scoping,params);
ProjectionInfo pi = ss.getProjectionMetadata(sc);
ViewQuery vq = new ViewQuery(ss,params,ss.getBaseTables().get(0));
return new DirectInfoSchemaStatement(LogicalSchemaQueryEngine.buildStep(sc, vq, pi));
}
@Override
public Statement buildUniqueStatement(SchemaContext sc, Name objectName, ShowOptions opts) {
assertPrivilege(sc);
TableInstance ti = new TableInstance(this,new UnqualifiedName("a"),new UnqualifiedName("a"),sc.getNextTable(),false);
Map<String,Object> params = new HashMap<String,Object>();
SelectStatement ss = buildSkeleton(sc,ti,null,params);
FunctionCall fc = new FunctionCall(FunctionName.makeEquals(),new ColumnInstance(getIdentColumn().getName(),getIdentColumn(),ti),
LiteralExpression.makeStringLiteral(objectName.getUnquotedName().get()));
ss.setWhereClause(fc);
normalizeScoping(sc,null,params);
ProjectionInfo pi = ss.getProjectionMetadata(sc);
ViewQuery vq = new ViewQuery(ss,params,ss.getBaseTables().get(0));
return new DirectInfoSchemaStatement(LogicalSchemaQueryEngine.buildStep(sc, vq, pi));
}
@Override
public List<CatalogEntity> getLikeSelectEntities(SchemaContext sc,
String likeExpr, List<Name> scoping, ShowOptions options,
Boolean overrideRequiresPrivilegeValue) {
throw new InformationSchemaException("No entities available for direct tables");
}
public void assertPrivilege(SchemaContext sc) {
if (!requiresPriviledge()) return;
if (!sc.getPolicyContext().isRoot())
throw new InformationSchemaException("You do not have permission to show " + getPluralName().get());
}
protected SelectStatement buildSkeleton(SchemaContext sc, TableInstance ti, ShowOptions options, Map<String,Object> params) {
AliasInformation ai = new AliasInformation();
ai.addAlias("a");
List<ExpressionNode> proj = buildProjection(sc, ti, useExtensions(sc), sc.getPolicyContext().isRoot(), ai, options);
SelectStatement ss = new SelectStatement(ai)
.setTables(ti).setProjection(proj);
ss.getDerivedInfo().addLocalTable(ti.getTableKey());
if (useExtensions(sc)) {
params.put(ViewInformationSchemaTable.metadataExtensions, 1L);
}
if (options != null && options.isIfNotExists())
params.put("ine",1L);
return ss;
}
protected List<ExpressionNode> buildProjection(SchemaContext sc, TableInstance ti, boolean useExtensions, boolean hasPriviledge, AliasInformation aliases, ShowOptions opts) {
List<DirectInformationSchemaColumn> projCols = getProjectionColumns(useExtensions,hasPriviledge, (opts != null && opts.isFull()));
ArrayList<ExpressionNode> proj = new ArrayList<ExpressionNode>();
for(DirectInformationSchemaColumn c : projCols) {
ColumnInstance ci = new ColumnInstance(c,ti);
ExpressionAlias ea = new ExpressionAlias(ci, new NameAlias(c.getName().getUnqualified()), true);
aliases.addAlias(ea.getAlias().get());
proj.add(ea);
}
return proj;
}
public interface TemporaryTableHandler {
public List<ResultRow> buildResults(SchemaContext sc, TableInstance matching, ShowOptions opts, String likeExpr);
}
protected Statement buildTempTableResults(SchemaContext sc, List<Name> scoping,
ExpressionNode likeExpr, ExpressionNode whereExpr,
ShowOptions options) {
if (!sc.getTemporaryTableSchema().isEmpty()) {
Pair<String,String> scope = inferScope(sc,scoping);
QualifiedName qn = new QualifiedName(new UnqualifiedName(scope.getFirst()), new UnqualifiedName(scope.getSecond()));
TableInstance matching = sc.getTemporaryTableSchema().buildInstance(sc, qn);
if (matching != null) {
List<DirectInformationSchemaColumn> projCols =
getProjectionColumns(useExtensions(sc), sc.getPolicyContext().isRoot(), (options != null && options.isFull()));
List<List<InformationSchemaColumn>> proj =
Functional.apply(projCols, new UnaryFunction<List<InformationSchemaColumn>,DirectInformationSchemaColumn>() {
@Override
public List<InformationSchemaColumn> evaluate(
DirectInformationSchemaColumn object) {
return Collections.singletonList((InformationSchemaColumn)object);
}
});
ColumnSet cs = LogicalSchemaQueryEngine.buildProjectionMetadata(sc,proj,null,null);
IntermediateResultSet irs = new IntermediateResultSet(cs,
tempTableHandler.buildResults(sc, matching, options, (likeExpr == null ? null : (String)((LiteralExpression)likeExpr).getValue(sc.getValues()))));
return new SchemaQueryStatement(false,getName().get(),irs);
}
}
return null;
}
private Pair<String,String> inferScope(SchemaContext sc, List<Name> scoping) {
if (scoping.size() > 2)
throw new InformationSchemaException("Overly qualified " + getPluralName() + " statement");
if (scoping.isEmpty())
throw new InformationSchemaException("Underly qualified " + getPluralName() + " statement");
return decomposeScope(sc,scoping);
}
protected Pair<String,String> decomposeScope(SchemaContext sc, List<Name> scoping) {
Name tablen = scoping.get(0);
Name dbn = (scoping.size() > 1 ? scoping.get(1) : null);
if (dbn == null && sc != null) dbn = sc.getCurrentDatabase().getName();
String tableName = (tablen == null ? null : tablen.get());
String dbName = (dbn == null ? null : dbn.get());
return new Pair<String,String>(dbName,tableName);
}
private Name getDatabaseName(SchemaContext sc) {
IPETenant ten = sc.getCurrentTenant().get(sc);
if (ten == null)
return sc.getCurrentDatabase().getName();
if (ten.getTenantID() == null || ten.getUniqueIdentifier() == null)
return sc.getCurrentDatabase().getName();
return new UnqualifiedName(ten.getUniqueIdentifier());
}
public void normalizeScoping(SchemaContext sc, List<Name> given, Map<String,Object> params) {
if (scopes == null) return;
Name[] actualScoping = new Name[] { null, null };
if (scopes[0]) {
// we require a database
if (given == null || given.isEmpty()) {
actualScoping[0] = getDatabaseName(sc);
} else if (given.size() < 2) {
if (!scopes[1])
actualScoping[0] = given.get(0);
else
actualScoping[0] = getDatabaseName(sc);
} else {
actualScoping[0] = given.get(0);
}
}
if (scopes[1]) {
if (given == null || given.isEmpty())
throw new InformationSchemaException("Underly qualified " + getName() + " statement");
else if (given.size() == 2) {
actualScoping[1] = given.get(1);
} else {
actualScoping[1] = given.get(0);
}
}
for(int i = 0; i < scopes.length; i++) {
if (scopes[i]) {
params.put(scopeNames[i],actualScoping[i].getUnquotedName().get());
}
}
}
}