org.apache.jena.sdb.compiler.TransformSDB Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jena-sdb Show documentation
Show all versions of jena-sdb Show documentation
SDB is a persistence layer for use with Apache Jena that uses an SQL database to store triples/quads.
/*
* 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.compiler;
import static org.apache.jena.atlas.iterator.Iter.filter ;
import static org.apache.jena.atlas.iterator.Iter.map ;
import static org.apache.jena.atlas.iterator.Iter.toSet ;
import static org.apache.jena.atlas.lib.SetUtils.intersection ;
import java.util.List ;
import java.util.Set ;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.graph.Node ;
import org.apache.jena.sdb.core.AliasesSql ;
import org.apache.jena.sdb.core.SDBRequest ;
import org.apache.jena.sdb.core.ScopeEntry ;
import org.apache.jena.sdb.core.sqlexpr.SqlColumn ;
import org.apache.jena.sdb.core.sqlnode.SqlNode ;
import org.apache.jena.sdb.core.sqlnode.SqlSelectBlock ;
import org.apache.jena.sdb.core.sqlnode.SqlTable ;
import org.apache.jena.sdb.layout2.TableDescQuads ;
import org.apache.jena.sdb.shared.SDBInternalError ;
import org.apache.jena.sdb.store.SQLBridge ;
import org.apache.jena.sdb.store.SQLBridgeFactory ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.TransformCopy ;
import org.apache.jena.sparql.algebra.op.* ;
import org.apache.jena.sparql.core.Var ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
public class TransformSDB extends TransformCopy
{
private static Logger log = LoggerFactory.getLogger(TransformSDB.class) ;
private SDBRequest request ;
private QuadBlockCompiler quadBlockCompiler ;
public TransformSDB(SDBRequest request, QuadBlockCompiler quadBlockCompiler)
{
this.request = request ;
this.quadBlockCompiler = quadBlockCompiler ;
}
@Override
public Op transform(OpBGP opBGP)
{ return opBGP ; }
@Override
public Op transform(OpQuadPattern quadPattern)
{
QuadBlock qBlk = new QuadBlock(quadPattern) ;
SqlNode node = quadBlockCompiler.compile(qBlk) ;
return new OpSQL(node, quadPattern, request) ;
}
@Override
public Op transform(OpJoin opJoin, Op left, Op right)
{
// TEMPORARY FIX
// Scoping problems.
if ( true )
return super.transform(opJoin, left, right) ;
if ( ! SDB_QC.isOpSQL(left) || ! SDB_QC.isOpSQL(right) )
return super.transform(opJoin, left, right) ;
SqlNode sqlLeft = ((OpSQL)left).getSqlNode() ;
SqlNode sqlRight = ((OpSQL)right).getSqlNode() ;
// This is wrong. If right is more than single triple pattern,
// the generated SQL wil attempt to use NodeTable lookups from the
// LHS but they are out of scope.
return new OpSQL(SqlBuilder.innerJoin(request, sqlLeft, sqlRight), opJoin, request) ;
}
@Override
public Op transform(OpLeftJoin opJoin, Op left, Op right)
{
if ( ! request.LeftJoinTranslation )
return super.transform(opJoin, left, right) ;
if ( ! SDB_QC.isOpSQL(left) || ! SDB_QC.isOpSQL(right) )
return super.transform(opJoin, left, right) ;
// Condition(s) in the left join. Punt for now.
if ( opJoin.getExprs() != null )
return super.transform(opJoin, left, right) ;
SqlNode sqlLeft = ((OpSQL)left).getSqlNode() ;
SqlNode sqlRight = ((OpSQL)right).getSqlNode() ;
// Check for coalesce.
// Do optional variables on the right appear only as optional variables on the left?
Set scopes = sqlLeft.getIdScope().findScopes() ;
// Find optional-on-left
Set scopes2 = toSet(filter(scopes.iterator(), ScopeEntry.OptionalFilter)) ;
Set leftOptVars = toSet(map(scopes2.iterator(), ScopeEntry::getVar)) ; // Vars from left optionals.
if ( false )
{
Iter iter = Iter.iter(scopes) ;
Set leftOptVars_ = iter.filter(ScopeEntry.OptionalFilter).map(ScopeEntry::getVar).toSet() ;
}
// Find optional-on-right (easier - it's all variables)
Set rightOptVars = sqlRight.getIdScope().getVars() ;
// And finally, calculate the intersection of the two.
// SetUtils extension - one side could be an iterator
Set coalesceVars = intersection(leftOptVars, rightOptVars) ;
// Future simplification : LeftJoinClassifier.nonLinearVars
// if ( ! coalesceVars.equals(LeftJoinClassifier.nonLinearVars( opJoin.getLeft(), opJoin.getRight() )) )
// { unexpected }
if ( coalesceVars.size() > 0 )
{
String alias = request.genId(AliasesSql.CoalesceAliasBase) ;
SqlNode sqlNode = SqlBuilder.leftJoinCoalesce(request, alias,
sqlLeft, sqlRight,
coalesceVars) ;
return new OpSQL(sqlNode, opJoin, request) ;
// Punt
//return super.transform(opJoin, left, right) ;
}
return new OpSQL(SqlBuilder.leftJoin(request, sqlLeft, sqlRight, null), opJoin, request) ;
}
@Override
public Op transform(OpFilter opFilter, Op op)
{
// Can't really do much here because we are working in node id space, and the lexicial forms etc
// of nodes aren't available until the bridge is added.
// See QueryCompilerMain.
return super.transform(opFilter, op) ;
}
// Modifiers: the structure is:
// slice
// distinct/reduced
// project
// order
// having
// group
// [toList]
// modifier : having
// modifier : group
// ---- Modifiers
@Override
public Op transform(OpDistinct opDistinct, Op subOp)
{
if ( ! SDB_QC.isOpSQL(subOp) )
return super.transform(opDistinct, subOp) ;
// Derby does not support DISTINCT on CLOBS
if ( ! request.DistinctTranslation )
return super.transform(opDistinct, subOp) ;
OpSQL opSubSQL = (OpSQL)subOp ;
SqlNode sqlSubOp = opSubSQL.getSqlNode() ;
SqlNode n = SqlSelectBlock.distinct(request, sqlSubOp) ;
OpSQL opSQL = new OpSQL(n, opDistinct, request) ;
// Pull up bridge, if any
opSQL.setBridge(opSubSQL.getBridge()) ;
return opSQL ;
}
@Override
public Op transform(OpProject opProject, Op subOp)
{
//request.getStore().getSQLBridgeFactory().create(request, null, null)
if ( ! SDB_QC.isOpSQL(subOp) )
return super.transform(opProject, subOp) ;
// Need to not do bridge elsewhere.
List vars = opProject.getVars() ;
return doBridge(request, (OpSQL)subOp, vars, opProject) ;
}
@Override
public Op transform(OpService opService, Op subOp)
{
// Do not walk in any further.
// See ARQ Optimize class for a better way to do this.
return opService ;
}
// See QueryCompilerMain.SqlNodesFinisher.visit(OpExt op)
// Be careful about being done twice.
// XXX SHARE CODE!
static private OpSQL doBridge(SDBRequest request, OpSQL opSQL, List projectVars, Op original)
{
SqlNode sqlNode = opSQL.getSqlNode() ;
SQLBridgeFactory f = request.getStore().getSQLBridgeFactory() ;
SQLBridge bridge = f.create(request, sqlNode, projectVars) ;
bridge.build();
sqlNode = bridge.getSqlNode() ;
opSQL = new OpSQL(sqlNode, original, request) ;
opSQL.setBridge(bridge) ;
opSQL.resetSqlNode(sqlNode) ; // New is better?
return opSQL ;
}
// Now done in QueryCompilerMain at a later stage.
// @Override
// public Op transform(OpSlice opSlice, Op subOp)
// {
// if ( ! request.LimitOffsetTranslation )
// return super.transform(opSlice, subOp) ;
//
// // Not a slice of SQL
// if ( ! SDB_QC.isOpSQL(subOp) )
// return super.transform(opSlice, subOp) ;
//
// // Two cases are currently handled:
// // (slice (project (sql expression)))
// // (slice (sql expression))
//
// if ( isProject(opSlice.getSubOp()) )
// // This should not happen because the pre-transform done in QueryEngineMain
// // rewrites the case into the equivalent (project (slice ...))
// return transformSliceProject(opSlice, (OpSQL)subOp, ((OpSQL)subOp).getSqlNode()) ;
//
// // (slice (sql expression))
// return transformSlice(opSlice, ((OpSQL)subOp).getSqlNode()) ;
// }
//
// private Op transformSlice(OpSlice opSlice, SqlNode sqlSubOp)
// {
// SqlNode n = SqlSelectBlock.slice(request, sqlSubOp, opSlice.getStart(), opSlice.getLength()) ;
// OpSQL x = new OpSQL(n, opSlice, request) ;
// return x ;
// }
//
// private Op transformSliceProject(OpSlice opSlice, OpSQL subOp, SqlNode sqlOp)
// {
// // This put the LIMIT outside all the projects left joins, which is a bit of a pity.
// // Could improve by rewriting (slice (project...)) into (project (slice...)) in QueryCompilerMain
// OpSQL x = (OpSQL)transformSlice(opSlice, sqlOp) ;
// SQLBridge bridge = subOp.getBridge() ;
// return x ;
// }
// // ----
//
// private boolean translateConstraints = true ;
//
// private SDBConstraint transformFilter(OpFilter opFilter)
// {
// if ( ! translateConstraints )
// return null ;
//
// ExprList exprs = opFilter.getExprs() ;
// List x = exprs.getList() ;
// for ( Expr expr : x )
// {
// ConditionCompiler cc = new RegexCompiler() ;
// SDBConstraint psc = cc.recognize(expr) ;
// if ( psc != null )
// return psc ;
// }
// return null ;
// }
//
// private Set getVarsInFilter(Expr expr)
// {
// Set vars = expr.getVarsMentioned() ;
// return vars ;
// }
@Override
public Op transform(OpDatasetNames opDatasetNames)
{
if ( false )
return super.transform(opDatasetNames) ;
// Basically, an implementation of "GRAPH ?g {}"
Node g = opDatasetNames.getGraphNode() ;
if ( ! Var.isVar(g) )
throw new SDBInternalError("OpDatasetNames - not a variable: "+g) ;
Var v = Var.alloc(g) ;
// Inner SELECT SQL: (SELECT DISTINCT g FROM Quads)
TableDescQuads quads = request.getStore().getQuadTableDesc() ;
SqlTable sqlTableQ = new SqlTable(quads.getTableName()) ;
sqlTableQ.setIdColumnForVar(v, new SqlColumn(sqlTableQ, quads.getGraphColName())) ;
SqlNode sqlNodeQ = SqlSelectBlock.distinct(request, sqlTableQ) ;
// Will have the value left join added later.
return new OpSQL(sqlNodeQ, opDatasetNames, request) ;
}
}