Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,12 @@
import org.labkey.api.security.roles.SiteAdminRole;
import org.labkey.api.settings.AppProps;
import org.labkey.api.util.logging.LogHelper;
import org.labkey.experiment.api.ClosureQueryHelper;
import org.labkey.experiment.api.ExpSampleTypeImpl;
import org.labkey.experiment.api.ExperimentServiceImpl;
import org.labkey.experiment.api.MaterialSource;
import org.labkey.experiment.api.property.DomainImpl;
import org.labkey.experiment.api.property.DomainPropertyImpl;
import org.labkey.experiment.api.property.StorageProvisionerImpl;
import org.labkey.experiment.samples.SampleTimelineAuditProvider;

import java.sql.Connection;
import java.sql.SQLException;
Expand Down
74 changes: 58 additions & 16 deletions query/src/org/labkey/query/sql/QInLineage.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,42 +28,71 @@
import org.labkey.api.query.column.BuiltInColumnTypes;
import org.labkey.query.QueryServiceImpl;

import java.util.Objects;

import static org.labkey.query.sql.antlr.SqlBaseParser.EXPANCESTORSOF;
import static org.labkey.query.sql.antlr.SqlBaseParser.EXPDESCENDANTSOF;
import static org.labkey.query.sql.antlr.SqlBaseParser.EXPLINEAGEOF;

final public class QInLineage extends QExpr
{
final boolean _in;
final boolean _children;
final boolean _parents;
final String _method;

public QInLineage(boolean in, boolean parents)
public QInLineage(boolean in, int methodTokenType)
{
this._in = in;
this._parents = parents;
super(QNode.class);

_in = in;
_method = switch (methodTokenType)
{
case EXPANCESTORSOF -> {
_children = false;
_parents = true;
yield "EXPANCESTORSOF";
}
case EXPDESCENDANTSOF -> {
_children = true;
_parents = false;
yield "EXPDESCENDANTSOF";
}
case EXPLINEAGEOF -> {
_children = true;
_parents = true;
yield "EXPLINEAGEOF";
}
default -> throw new IllegalArgumentException("Invalid QInLineage method token type: " + methodTokenType);
};
}

String operator()
{
return (_in ? " IN " : " NOT IN ") + (_parents ? "EXPANCESTORSOF " : "EXPDESCENDANTSOF " );
return (_in ? " IN " : " NOT IN ") + _method + " ";
}

@Override
public void appendSql(SqlBuilder builder, Query query)
{
SQLTableInfo sqlti = new SQLTableInfo(query.getSchema().getDbSchema(), "_");
var children = childList();
var LHS = ((QExpr) getFirstChild());
var RHS = ((QQuery) getLastChild());
var LHS = (QExpr) firstOrThrow(children);
var RHS = (QQuery) secondOrThrow(children);

// LHS should be a 'lineage object', e.g. the result of calling {ExtTable}.expObject()
// LHS should be a 'lineage object', e.g., the result of calling {ExtTable}.expObject()
ColumnInfo lhsCol = null;
if (LHS instanceof QueryServiceImpl.QColumnInfo || LHS instanceof QMethodCall)
{
SQLTableInfo sqlti = new SQLTableInfo(query.getSchema().getDbSchema(), "_");
lhsCol = LHS.createColumnInfo(sqlti, "_", query);
}
if (lhsCol == null || !Strings.CS.equals(lhsCol.getConceptURI(), BuiltInColumnTypes.EXPOBJECTID_CONCEPT_URI))
{
query.getParseErrors().add(new QueryParseException(operator() + " requires argument to be a lineage object", null, getLine(), getColumn()));
query.getParseErrors().add(new QueryParseException(_method + " requires argument to be a lineage object", null, getLine(), getColumn()));
return;
}

// RHS should be SELECT with one column of 'lineage object', e.g. the result of calling {ExtTable}.expObject()
// RHS should be SELECT with one column of 'lineage object', e.g., the result of calling {ExtTable}.expObject()
QueryRelation r = RHS._select;
var map = r.getAllColumns();
var col = map.size() != 1 ? null : map.values().iterator().next();
Expand All @@ -72,15 +101,31 @@ public void appendSql(SqlBuilder builder, Query query)
col.copyColumnAttributesTo(rhsCol);
if (!Strings.CS.equals(rhsCol.getConceptURI(), BuiltInColumnTypes.EXPOBJECTID_CONCEPT_URI))
{
query.getParseErrors().add(new QueryParseException(operator() + " requires argument to be a lineage object", null, getLine(), getColumn()));
query.getParseErrors().add(new QueryParseException(_method + " requires argument to be a lineage object", null, getLine(), getColumn()));
return;
}

SqlBuilder subquery = new SqlBuilder(builder.getDialect());
RHS.appendSql(subquery, query);
// subquery will have surrounding parens, but the double parens don't cause a problem

ExpLineageOptions options = new ExpLineageOptions(_parents, !_parents, 1000);
// Parse depth argument
int depth = 1_000;
{
QNode depthExpr = child(children, 2);
if (depthExpr != null)
{
if (!(depthExpr instanceof QNumber n))
{
query.getParseErrors().add(new QueryParseException(_method + " requires second argument to be an integer", null, getLine(), getColumn()));
return;
}

depth = n.getValue().intValue();
}
}

ExpLineageOptions options = new ExpLineageOptions(_parents, _children, depth);
options.setUseObjectIds(true); // expObject() returns objectid not lsid
options.setOnlySelectObjectId(true); // generate one column SELECT, also don't join to material/data/protocolapplication
SQLFragment lineage = ExperimentService.get().generateExperimentTreeSQL(subquery, options);
Expand All @@ -93,7 +138,6 @@ public void appendSql(SqlBuilder builder, Query query)
builder.append("))");
}


@Override
public void appendSource(SourceBuilder builder)
{
Expand All @@ -109,18 +153,16 @@ public void appendSource(SourceBuilder builder)
builder.popPrefix(")");
}


@Override @NotNull
public JdbcType getJdbcType()
{
return JdbcType.BOOLEAN;
}


@Override
public boolean equalsNode(QNode other)
{
return (other instanceof QInLineage o) && _in == o._in && _parents == o._parents;
return other instanceof QInLineage o && _in == o._in && Objects.equals(_method, o._method);
}

@Override
Expand Down
36 changes: 32 additions & 4 deletions query/src/org/labkey/query/sql/QNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.antlr.runtime.CommonToken;
import org.antlr.runtime.tree.CommonTree;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.junit.Assert;
import org.junit.Test;
Expand All @@ -34,6 +35,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.labkey.query.sql.antlr.SqlBaseParser.FALSE;
import static org.labkey.query.sql.antlr.SqlBaseParser.IDENT;
Expand Down Expand Up @@ -109,7 +111,7 @@ public Iterable<QNode> children()
return _children;
}

public List<QNode> childList()
public LinkedList<QNode> childList()
{
return _children;
}
Expand Down Expand Up @@ -373,15 +375,12 @@ protected void dump(PrintWriter out, String nl, IdentityHashMap<Object,Object> d
c.dump(out, nl + " |", dumped);
}



public void addFieldRefs(Object referant)
{
for (QNode child : childList())
child.addFieldRefs(referant);
}


public void releaseFieldRefs(Object referant)
{
for (QNode child : childList())
Expand All @@ -398,6 +397,35 @@ public void setHasTransformableAggregate(boolean hasTransformableAggregate)
_hasTransformableAggregate = hasTransformableAggregate;
}

static @Nullable QNode child(LinkedList<QNode> children, int index)
{
return children.size() > index ? children.get(index) : null;
}

static @NotNull QNode childOrThrow(LinkedList<QNode> children, int index)
{
return Objects.requireNonNull(child(children, index));
}

static QNode first(LinkedList<QNode> children)
{
return child(children, 0);
}

static @NotNull QNode firstOrThrow(LinkedList<QNode> children)
{
return childOrThrow(children, 0);
}

static QNode second(LinkedList<QNode> children)
{
return child(children, 1);
}

static @NotNull QNode secondOrThrow(LinkedList<QNode> children)
{
return childOrThrow(children, 1);
}

public static class TestCase extends Assert
{
Expand Down
5 changes: 3 additions & 2 deletions query/src/org/labkey/query/sql/SqlBase.g
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ EXCEPT : 'except';
EXISTS : 'exists';
EXPDESCENDANTSOF : 'expdescendantsof';
EXPANCESTORSOF : 'expancestorsof';
EXPLINEAGEOF : 'explineageof';
FALSE : 'false';
FROM : 'from';
FULL : 'full';
Expand Down Expand Up @@ -423,7 +424,7 @@ fromRange


tableMethod
: (EXPANCESTORSOF | EXPDESCENDANTSOF) op=OPEN^ {$op.setType(METHOD_CALL);} subQuery CLOSE!
: (EXPANCESTORSOF | EXPDESCENDANTSOF | EXPLINEAGEOF) op=OPEN^ {$op.setType(METHOD_CALL);} subQuery (COMMA! expression)? CLOSE!
;


Expand Down Expand Up @@ -679,7 +680,7 @@ likeEscape


inList
: (EXPANCESTORSOF | EXPDESCENDANTSOF) op=OPEN^ {$op.setType(METHOD_CALL);} subQuery CLOSE!
: (EXPANCESTORSOF | EXPDESCENDANTSOF | EXPLINEAGEOF) op=OPEN^ {$op.setType(METHOD_CALL);} subQuery (COMMA! expression)? CLOSE!
| compoundExpr -> ^(IN_LIST compoundExpr)
;

Expand Down
Loading