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 @@ -16,7 +16,6 @@
package org.labkey.announcements.model;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.announcements.CommSchema;
import org.labkey.api.attachments.AttachmentParentType;
import org.labkey.api.data.SQLFragment;
Expand All @@ -41,8 +40,8 @@ public static AttachmentParentType get()
}

@Override
public @Nullable SQLFragment getSelectParentEntityIdsSql()
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId FROM ").append(CommSchema.getInstance().getTableInfoAnnouncements(), "ann");
return new SQLFragment("SELECT EntityId, Title AS Description FROM ").append(CommSchema.getInstance().getTableInfoAnnouncements(), "ann");
}
}
32 changes: 19 additions & 13 deletions api/src/org/labkey/api/attachments/AttachmentParentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.api.attachments;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.data.SQLFragment;

/**
Expand All @@ -25,7 +24,7 @@
*/
public interface AttachmentParentType
{
SQLFragment NO_ENTITY_IDS = new SQLFragment("SELECT NULL AS EntityId WHERE 1 = 0");
SQLFragment NO_ROWS = new SQLFragment("SELECT NULL AS EntityId, NULL AS Description WHERE 1 = 0");

AttachmentParentType UNKNOWN = new AttachmentParentType()
{
Expand All @@ -37,9 +36,9 @@ public String getUniqueName()
}

@Override
public void addWhereSql(SQLFragment sql, String parentColumn, String documentNameColumn)
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
sql.append("0 = 1");
return NO_ROWS;
}
};

Expand All @@ -56,20 +55,27 @@ public void addWhereSql(SQLFragment sql, String parentColumn, String documentNam
default void addWhereSql(SQLFragment sql, String parentColumn, String documentNameColumn)
{
SQLFragment selectSql = getSelectParentEntityIdsSql();
if (selectSql == null)
throw new IllegalStateException("Must override either addWhereSql() or getSelectParentEntityIdsSql()");
sql.append(parentColumn).append(" IN (").append(selectSql).append(")");
}

/**
* Return a SQLFragment that selects all the EntityIds that might be attachment parents from the table(s) that
* provide attachments of this type, without involving the core.Documents table. For example,
* {@code SELECT EntityId FROM comm.Announcements}. Return null if this is not-yet-implemented or inappropriate.
* For example, some attachments' parents are container IDs. If the method determines that no parents exist, then
* return a valid query that selects no rows, for example, {@code NO_ENTITY_IDS}.
* Return a SQLFragment that selects just the EntityId of rows that might be attachment parents from the table(s)
* that provide attachments of this type, without involving the core.Documents table.
*/
default @Nullable SQLFragment getSelectParentEntityIdsSql()
default @NotNull SQLFragment getSelectParentEntityIdsSql()
{
return null;
SQLFragment selectSql = getSelectEntityIdAndDescriptionSql();

// The returned SQL is always used inside a subselect, so the alias doesn't have to be unique
return new SQLFragment("SELECT EntityId FROM (").append(selectSql).append(") x");
}

/**
* Return a SQLFragment that selects the EntityId and an appropriate Description of all rows that might be
* attachment parents from the table(s) that provide attachment parents of this type, without involving the
* core.Documents table. For example, {@code SELECT EntityId, Title AS Description FROM comm.Announcements}.
* If the method determines that no parents exist, then return a valid query that selects no rows, for example,
* {@code NO_ROWS}.
*/
@NotNull SQLFragment getSelectEntityIdAndDescriptionSql();
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ public void addWhereSql(SQLFragment sql, String parentColumn, String documentNam
sql.append(documentNameColumn).append(" LIKE '" + AttachmentCache.LOGO_FILE_NAME_PREFIX + "%' OR ");
sql.append(documentNameColumn).append(" LIKE '" + AttachmentCache.MOBILE_LOGO_FILE_NAME_PREFIX + "%')");
}

@Override
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId, CASE WHEN Name IS NULL THEN '<Root>' ELSE Name END AS Description FROM ")
.append(CoreSchema.getInstance().getTableInfoContainers());
}
}
14 changes: 14 additions & 0 deletions api/src/org/labkey/api/collections/LabKeyCollectors.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.json.JSONArray;
import org.junit.Assert;
import org.junit.Test;
import org.labkey.api.data.SQLFragment;
import org.labkey.api.util.HtmlString;
import org.labkey.api.util.HtmlStringBuilder;

Expand All @@ -14,6 +15,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -222,6 +224,18 @@ public static Collector<HtmlString, HtmlStringBuilder, HtmlString> joining(HtmlS
);
}

/**
* Returns a {@link Collector} that joins {@link SQLFragment}s into a single {@link SQLFragment} separated by delimiter
*/
public static Collector<SQLFragment, List<SQLFragment>, SQLFragment> joining(SQLFragment delimiter) {
return Collector.of(
LinkedList::new,
List::add,
(list1, list2) -> {list1.addAll(list2); return list1;},
(list) -> SQLFragment.join(list, delimiter)
);
}

public static class TestCase extends Assert
{
@Test
Expand Down
29 changes: 13 additions & 16 deletions api/src/org/labkey/api/data/SQLFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import static org.labkey.api.query.ExprColumn.STR_TABLE_ALIAS;

Expand Down Expand Up @@ -1339,25 +1338,23 @@ public int hashCode()
* concatenation using the provided separator. The parameters are combined to form the new parameter list.
*
* @param fragments SQLFragments to join together
* @param separator Separator to use on the SQL portion
* @param separator Separator to use
* @return A new SQLFragment that joins all the SQLFragments
*/
public static SQLFragment join(Iterable<SQLFragment> fragments, String separator)
public static SQLFragment join(Iterable<SQLFragment> fragments, SQLFragment separator)
{
if (separator.contains("?"))
throw new IllegalStateException("separator must not include a parameter marker");
SQLFragment join = new SQLFragment();
boolean first = true;

// Join all the SQL statements
String sql = StreamSupport.stream(fragments.spliterator(), false)
.map(SQLFragment::getSQL)
.collect(Collectors.joining(separator));

// Collect all the parameters to a single list
List<?> params = StreamSupport.stream(fragments.spliterator(), false)
.map(SQLFragment::getParams)
.flatMap(Collection::stream)
.collect(Collectors.toList());
for (SQLFragment fragment : fragments)
{
if (first)
first = false;
else
join.append(separator);
join.append(fragment);
}

return new SQLFragment(sql, params);
return join;
}
}
35 changes: 19 additions & 16 deletions api/src/org/labkey/api/exp/Lsid.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.labkey.api.data.Builder;
import org.labkey.api.data.SQLFragment;
import org.labkey.api.data.dialect.SqlDialect;
import org.labkey.api.settings.AppProps;
import org.labkey.api.util.GUID;
import org.labkey.api.util.Pair;

import java.net.URI;
Expand All @@ -36,8 +38,6 @@
import java.nio.charset.StandardCharsets;
import java.util.Objects;

import static org.apache.commons.lang3.StringUtils.repeat;

/**
* Life-sciences identifier (LSID). A structured URI to describe things like samples, data files, assay runs, and protocols.
* User: migra
Expand Down Expand Up @@ -122,7 +122,6 @@ private Lsid(String src, String authority, String namespace, String objectId, St
*
* To spend less time parsing, maybe cached parsed Lsid in ExpObjectImpl? (not that straightforward)
*/

@Nullable
private static String[] parseLsid(@Nullable String s)
{
Expand All @@ -138,28 +137,32 @@ private static String[] parseLsid(@Nullable String s)
return new String[] {parts[2], parts[3], parts[4], parts.length < 6 ? null : parts[5]};
}


// Keep in sync with LSID_REGEX (above)
public static Pair<String, String> getSqlExpressionToExtractObjectId(String lsidExpression, SqlDialect dialect)
// Keep in sync with LSID_REGEX (above). Note: AttachmentServiceImpl.TestCase.testLsidGuidExtraction tests this.
public static Pair<SQLFragment, SQLFragment> getSqlExpressionToExtractObjectId(SQLFragment lsidExpression, SqlDialect dialect)
{
String objectId = GUID.SQL_LIKE_GUID_PATTERN;

if (dialect.isPostgreSQL())
{
// PostgreSQL SUBSTRING supports simple regular expressions. This captures all the text from the third
// colon to the end of the string (or to the fourth colon, if present).
String expression = "SUBSTRING(" + lsidExpression + " FROM '%urn:lsid:%:#\"%#\":?%' FOR '#')";
String where = lsidExpression + " SIMILAR TO '%urn:lsid:%:[0-9a-f\\-]{36}:?%'";
// PostgreSQL SUBSTRING supports simple regular expressions. This captures all the text from the fourth
// colon to the end of the string (or to the fifth colon, if present).
SQLFragment expression = new SQLFragment("SUBSTRING(")
.append(lsidExpression)
.append(" FROM '%urn:lsid:%:%:#\"[0-9a-f\\-]{36}#\":?%' FOR '#')");
SQLFragment where = new SQLFragment(lsidExpression).append(" SIMILAR TO '%urn:lsid:%:%:[0-9a-f\\-]{36}:?%'");

return new Pair<>(expression, where);
}

if (dialect.isSqlServer())
{
// SQL Server doesn't support regular expressions; this uses an unwieldy pattern to extract the objectid
String d = "[0-9a-f]"; // pattern for a single digit
String objectId = repeat(d, 8) + "-" + repeat(d, 4) + "-" + repeat(d, 4) + "-" + repeat(d, 4) + "-" + repeat(d, 12);

String expression = "SUBSTRING(" + lsidExpression + ", PATINDEX('%:" + objectId + "%', " + lsidExpression + ") + 1, 36)";
String where = lsidExpression + " LIKE '%urn:lsid:%:" + objectId + "%'";
// SQL Server doesn't support regular expressions
SQLFragment expression = new SQLFragment("SUBSTRING(")
.append(lsidExpression)
.append(", PATINDEX('%:" + objectId + "%', ")
.append(lsidExpression)
.append(") + 1, 36)");
SQLFragment where = new SQLFragment(lsidExpression).append(" LIKE '%urn:lsid:%:%:" + objectId + "%'");

return new Pair<>(expression, where);
}
Expand Down
5 changes: 2 additions & 3 deletions api/src/org/labkey/api/exp/api/ExpProtocolAttachmentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.api.exp.api;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.attachments.AttachmentParentType;
import org.labkey.api.data.SQLFragment;

Expand All @@ -40,8 +39,8 @@ private ExpProtocolAttachmentType()
}

@Override
public @Nullable SQLFragment getSelectParentEntityIdsSql()
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId FROM ").append(ExperimentService.get().getTinfoProtocol(), "ep");
return new SQLFragment("SELECT EntityId, Name AS Description FROM ").append(ExperimentService.get().getTinfoProtocol(), "ep");
}
}
5 changes: 2 additions & 3 deletions api/src/org/labkey/api/exp/api/ExpRunAttachmentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.api.exp.api;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.attachments.AttachmentParentType;
import org.labkey.api.data.SQLFragment;

Expand All @@ -40,8 +39,8 @@ private ExpRunAttachmentType()
}

@Override
public @Nullable SQLFragment getSelectParentEntityIdsSql()
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId FROM ").append(ExperimentService.get().getTinfoExperimentRun(), "er");
return new SQLFragment("SELECT EntityId, Name AS Description FROM ").append(ExperimentService.get().getTinfoExperimentRun(), "er");
}
}
5 changes: 2 additions & 3 deletions api/src/org/labkey/api/files/FileSystemAttachmentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.labkey.api.files;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.labkey.api.attachments.AttachmentParentType;
import org.labkey.api.data.CoreSchema;
import org.labkey.api.data.SQLFragment;
Expand All @@ -41,8 +40,8 @@ private FileSystemAttachmentType()
}

@Override
public @Nullable SQLFragment getSelectParentEntityIdsSql()
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId FROM ").append(CoreSchema.getInstance().getMappedDirectories(), "md");
return new SQLFragment("SELECT EntityId, Name AS Description FROM ").append(CoreSchema.getInstance().getMappedDirectories(), "md");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.labkey.api.query.FieldKey;
import org.labkey.api.query.SchemaKey;
import org.labkey.api.query.TableSorter;
import org.labkey.api.util.ConfigurationException;
import org.labkey.api.util.GUID;
import org.labkey.api.util.StringUtilsLabKey;
import org.labkey.api.util.logging.LogHelper;
Expand Down Expand Up @@ -249,20 +248,14 @@ public Collection<AttachmentParentType> copyAttachments(DatabaseMigrationConfigu
// requires querying and re-filtering the source tables instead.
Collection<AttachmentParentType> ret = new LinkedList<>();

// TODO: Select ParentType as well to avoid duplication
getAttachmentTypes().forEach(type -> {
SQLFragment sql = type.getSelectParentEntityIdsSql();
if (sql != null)
{
Collection<String> entityIds = new SqlSelector(targetSchema, sql).getCollection(String.class);
SQLFragment selectParents = new SQLFragment("Parent");
// This query against the source database is likely to contain a large IN clause, so use an alternative InClauseGenerator
sourceSchema.getSqlDialect().appendInClauseSqlWithCustomInClauseGenerator(selectParents, entityIds, getTempTableInClauseGenerator(sourceSchema.getScope()));
ret.addAll(copyAttachments(configuration, new SQLClause(selectParents), type));
}
else
{
throw new ConfigurationException("AttachmentType \"" + type.getUniqueName() + "\" is not configured to find parent EntityIds!");
}
Collection<String> entityIds = new SqlSelector(targetSchema, sql).getCollection(String.class);
SQLFragment selectParents = new SQLFragment("Parent");
// This query against the source database is likely to contain a large IN clause, so use an alternative InClauseGenerator
sourceSchema.getSqlDialect().appendInClauseSqlWithCustomInClauseGenerator(selectParents, entityIds, getTempTableInClauseGenerator(sourceSchema.getScope()));
ret.addAll(copyAttachments(configuration, new SQLClause(selectParents), type));
});

return ret;
Expand Down
2 changes: 1 addition & 1 deletion api/src/org/labkey/api/query/QueryView.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@

/**
* View that generates the majority of standard data grids/tables in the LabKey Server UI.
* The backing query is lazily invoked when it comes times to render the QueryView.
* The backing query is lazily invoked when it comes time to render the QueryView.
*/
public class QueryView extends WebPartView<Object> implements ContainerUser
{
Expand Down
9 changes: 7 additions & 2 deletions api/src/org/labkey/api/reports/report/ReportType.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.labkey.api.attachments.AttachmentParentType;
import org.labkey.api.data.CoreSchema;
import org.labkey.api.data.SQLFragment;
import org.labkey.api.data.TableInfo;

public class ReportType implements AttachmentParentType
{
Expand All @@ -40,8 +41,12 @@ private ReportType()
}

@Override
public @NotNull SQLFragment getSelectParentEntityIdsSql()
public @NotNull SQLFragment getSelectEntityIdAndDescriptionSql()
{
return new SQLFragment("SELECT EntityId FROM ").append(CoreSchema.getInstance().getTableInfoReport(), "reports");
TableInfo table = CoreSchema.getInstance().getTableInfoReport();
return new SQLFragment("SELECT EntityId, ")
.append(table.getSqlDialect().concatenate("ReportKey", "':'", "CAST(RowId AS VARCHAR)"))
.append(" AS Description FROM ")
.append(table, "reports");
}
}
Loading