/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.jena.sdb.core.sqlnode; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.jena.sdb.core.* ; import org.apache.jena.sdb.core.sqlexpr.SqlColumn ; import org.apache.jena.sdb.core.sqlexpr.SqlExpr ; import org.apache.jena.sdb.core.sqlexpr.SqlExprList ; import org.apache.jena.sdb.shared.SDBInternalError ; import org.apache.jena.sparql.core.Var ; /** A unit that generates an SQL SELECT Statement. * The SQL generation process is a pass over the SqlNdoe structure to generate SelectBlocks, * then to generate the SQL strings. */ public class SqlSelectBlock extends SqlNodeBase1 { // Need: code to take an SqlNode and produce a rename map. // Already in SqlRename. Use SqlRename.view. // SqlRename is currently unused. // Is SqlRename > SqlProject? // with Map<Var, String> = List<VarCol> // Mapping of names // projection // Joins (inner and left) // Restriction. // Group // aggregrate // order // limit/offset private List<ColAlias> cols = new ArrayList<ColAlias>() ; private SqlExprList exprs = new SqlExprList() ; private static final int NOT_SET = -9 ; private long start = NOT_SET ; private long length = NOT_SET ; private boolean distinct = false ; private SqlTable vTable ; // Naming base for renamed columns private Scope idScope = null ; // Scopes are as the wrapped SqlNode unless explicitly changed. private Scope nodeScope = null ; static public SqlNode distinct(SDBRequest request, SqlNode sqlNode) { SqlSelectBlock block = blockWithView(request, sqlNode) ; block.setDistinct(true) ; return block ; } static public SqlNode project(SDBRequest request, SqlNode sqlNode) { return project(request, sqlNode, (ColAlias)null) ; } static public SqlNode project(SDBRequest request, SqlNode sqlNode, Collection<ColAlias> cols) { // If already a view, not via a project, - think harder SqlSelectBlock block = blockNoView(request, sqlNode) ; if ( block.idScope != null || block.nodeScope != null ) System.err.println("SqlSelectBlock.project : already a view") ; if ( cols != null ) block.addAll(cols) ; return block ; } static public SqlNode project(SDBRequest request, SqlNode sqlNode, ColAlias col) { SqlSelectBlock block = blockNoView(request, sqlNode) ; if ( col != null ) block.add(col) ; return block ; } static public SqlNode slice(SDBRequest request, SqlNode sqlNode, long start, long length) { SqlSelectBlock block = blockWithView(request, sqlNode) ; if ( start >= 0 ) { if ( block.getStart() > 0 ) start = start + block.getStart() ; block.setStart(start) ; } if ( length >= 0 ) { if ( block.getLength() >= 0 ) length = Math.min(length, block.getLength()) ; block.setLength(length) ; } return block ; } static public SqlNode view(SDBRequest request, SqlNode sqlNode) { SqlSelectBlock block = blockWithView(request, sqlNode) ; return block ; } static public SqlNode restrict(SDBRequest request, SqlNode sqlNode, SqlExprList exprs) { if ( exprs.size() == 0 ) return sqlNode ; // Single table does not need renaming of columns SqlSelectBlock block = (sqlNode.isTable() ? blockPlain(request, sqlNode) : blockWithView(request, sqlNode)) ; block.getConditions().addAll(exprs) ; return block ; } static public SqlNode restrict(SDBRequest request, SqlNode sqlNode, SqlExpr expr) { SqlSelectBlock block = (sqlNode.isTable() ? blockPlain(request, sqlNode) : blockWithView(request, sqlNode)) ; block.getConditions().add(expr) ; return block ; } /** * @param aliasName * @param sqlNode */ private SqlSelectBlock(String aliasName, SqlNode sqlNode) { super(aliasName, sqlNode) ; if ( aliasName != null ) vTable = new SqlTable(aliasName) ; } @Override public boolean isSelectBlock() { return true ; } @Override public SqlSelectBlock asSelectBlock() { return this ; } public void setBlockAlias(String alias) { super.aliasName = alias ; } public List<ColAlias> getCols() { return cols ; } public void add(ColAlias c) { _add(c) ; } public void addAll(Collection<ColAlias> vc) { for ( ColAlias c : vc ) _add(c) ; } private void _add(ColAlias c) { SqlColumn col = c.getColumn() ; SqlColumn aliasCol = c.getAlias() ; c.check(getAliasName()) ; // // if ( aliasCol.getTable() != null && aliasCol.getTable().getAliasName().equals(getAliasName()) ) // throw new SDBInternalError("Attempt to project to a column with different alias: "+col+" -> "+aliasCol) ; cols.add(c) ; } /** Prepare the SelectBlock for use as a top level element - may discard the block */ public SqlNode clearView() { idScope = null ; nodeScope = null ; cols.clear() ; if ( !distinct && ! hasConditions() && ! hasSlice() ) return getSubNode() ; return this ; } public SqlExprList getConditions() { return exprs ; } public boolean hasSlice() { return (start != NOT_SET ) || ( length != NOT_SET ) ; } public boolean hasConditions() { return exprs.size() > 0 ; } public long getStart() { return start ; } private void setStart(long start) { this.start = start ; } public long getLength() { return length ; } private void setLength(long length) { this.length = length ; } @Override public Scope getIdScope() { return idScope != null ? idScope : super.getIdScope() ; } @Override public Scope getNodeScope() { return nodeScope != null ? nodeScope : super.getNodeScope() ; } @Override public SqlNode apply(SqlTransform transform, SqlNode newSubNode) { return transform.transform(this, newSubNode) ; } @Override public SqlNode copy(SqlNode subNode) { return new SqlSelectBlock(this.getAliasName(), subNode) ; } @Override public void visit(SqlNodeVisitor visitor) { visitor.visit(this) ; } // Not "isDistinct" public boolean getDistinct() { return distinct ; } private void setDistinct(boolean isDistinct) { this.distinct = isDistinct ; } private static SqlSelectBlock blockWithView(SDBRequest request,SqlNode sqlNode) { if ( sqlNode instanceof SqlSelectBlock ) { SqlSelectBlock block = (SqlSelectBlock)sqlNode ; if ( block.cols.size() == 0 ) { // Didn't have a column view - force it calcView(block) ; } return (SqlSelectBlock)sqlNode ; } SqlSelectBlock block = _create(request, sqlNode) ; if ( block.getCols().size() != 0 ) throw new SDBInternalError("Can't set a view on Select block which is already had columns set") ; calcView(block) ; return block ; } private static SqlSelectBlock blockPlain(SDBRequest request,SqlNode sqlNode) { if ( sqlNode instanceof SqlSelectBlock ) return (SqlSelectBlock)sqlNode ; // Same alias (typically, sqlNode is a table or view and this is the table name) SqlSelectBlock block = new SqlSelectBlock(sqlNode.getAliasName(), sqlNode) ; //addNotes(block, sqlNode) ; return block ; } private static SqlSelectBlock blockNoView(SDBRequest request, SqlNode sqlNode) { if ( sqlNode instanceof SqlSelectBlock ) return (SqlSelectBlock)sqlNode ; return _create(request, sqlNode) ; } private static SqlSelectBlock _create(SDBRequest request,SqlNode sqlNode) { String alias = sqlNode.getAliasName() ; //if ( ! sqlNode.isTable() ) alias = request.generator(AliasesSql.SelectBlock).next() ; SqlSelectBlock block = new SqlSelectBlock(alias, sqlNode) ; addNotes(block, sqlNode) ; return block ; } private static void addNotes(SqlSelectBlock block, SqlNode sqlNode) { block.addNotes(sqlNode.getNotes()) ; } static private void calcView(SqlSelectBlock block) { SqlNode sqlNode = block.getSubNode() ; ScopeBase idScopeRename = new ScopeBase() ; ScopeBase nodeScopeRename = new ScopeBase() ; Generator gen = Gensym.create("X") ; // Column names. Not global. block.merge(sqlNode.getIdScope(), idScopeRename, gen) ; block.merge(sqlNode.getNodeScope(), nodeScopeRename, gen) ; block.nodeScope = nodeScopeRename ; block.idScope = idScopeRename ; } // Calculate renames // Map all vars in the scope to names in the rename. private void merge(Scope scope, ScopeBase newScope, Generator gen) { String x = "" ; String sep = "" ; for ( ScopeEntry e : scope.findScopes() ) { SqlColumn oldCol = e.getColumn() ; Var v = e.getVar() ; String colName = gen.next() ; SqlColumn newCol = new SqlColumn(vTable, colName) ; this.add(new ColAlias(oldCol, newCol)) ; newScope.setColumnForVar(v, newCol) ; // Annotations x = String.format("%s%s%s:(%s=>%s)", x, sep, v, oldCol, newCol) ; sep = " " ; } if ( x.length() > 0 ) addNote(x) ; } }