99import org .labkey .api .data .SimpleFilter .FilterClause ;
1010import org .labkey .api .data .SimpleFilter .InClause ;
1111import org .labkey .api .data .SimpleFilter .OrClause ;
12+ import org .labkey .api .data .SimpleFilter .SQLClause ;
1213import org .labkey .api .query .FieldKey ;
1314import org .labkey .api .query .SchemaKey ;
1415import org .labkey .api .query .TableSorter ;
1516import org .labkey .api .services .ServiceRegistry ;
17+ import org .labkey .api .util .ConfigurationException ;
1618import org .labkey .api .util .GUID ;
1719import org .labkey .api .util .logging .LogHelper ;
1820import org .labkey .vfs .FileLike ;
@@ -30,7 +32,7 @@ public interface DatabaseMigrationService
3032{
3133 Logger LOG = LogHelper .getLogger (DatabaseMigrationService .class , "Information about database migration" );
3234
33- record DomainFilter (Set <GUID > containers , String column , FilterClause condition ) {}
35+ record DataFilter (Set <GUID > containers , String column , FilterClause condition ) {}
3436
3537 static @ NotNull DatabaseMigrationService get ()
3638 {
@@ -84,11 +86,13 @@ record Sequence(String schemaName, String tableName, String columnName, long las
8486
8587 List <TableInfo > getTablesToCopy ();
8688
89+ FilterClause getTableFilter (TableInfo sourceTable , FieldKey containerFieldKey , Set <GUID > containers );
90+
8791 FilterClause getContainerClause (TableInfo sourceTable , FieldKey containerFieldKey , Set <GUID > containers );
8892
8993 @ Nullable FieldKey getContainerFieldKey (TableInfo sourceTable );
9094
91- void addDomainDataFilter (OrClause orClause , DomainFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames );
95+ void addDomainDataFilter (OrClause orClause , DataFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames );
9296
9397 // Do any necessary clean up after the target table has been populated. notCopiedFilter selects all rows in the
9498 // source table that were NOT copied to the target table. (For example, they were filtered out due to container
@@ -144,6 +148,12 @@ public List<TableInfo> getTablesToCopy()
144148 .collect (Collectors .toCollection (ArrayList ::new )); // Ensure mutable
145149 }
146150
151+ @ Override
152+ public FilterClause getTableFilter (TableInfo sourceTable , FieldKey containerFieldKey , Set <GUID > containers )
153+ {
154+ return getContainerClause (sourceTable , containerFieldKey , containers );
155+ }
156+
147157 @ Override
148158 public FilterClause getContainerClause (TableInfo sourceTable , FieldKey containerFieldKey , Set <GUID > containers )
149159 {
@@ -192,14 +202,17 @@ public FilterClause getContainerClause(TableInfo sourceTable, FieldKey container
192202 }
193203
194204 @ Override
195- public void addDomainDataFilter (OrClause orClause , DomainFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
205+ public void addDomainDataFilter (OrClause orClause , DataFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
196206 {
197- addDomainDataStandardFilter (orClause , filter , sourceTable , fKey , selectColumnNames );
207+ addDataFilter (orClause , filter , sourceTable , fKey , selectColumnNames );
198208 }
199209
200- protected void addDomainDataStandardFilter (OrClause orClause , DomainFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
210+ // Add a filter and return true if the column exists directly on the table
211+ protected boolean addDataFilter (OrClause orClause , DataFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
201212 {
202- if (selectColumnNames .contains (filter .column ()))
213+ boolean columnExists = selectColumnNames .contains (filter .column ());
214+
215+ if (columnExists )
203216 {
204217 // Select all rows in this domain-filtered container that meet its criteria
205218 orClause .addClause (
@@ -209,37 +222,44 @@ protected void addDomainDataStandardFilter(OrClause orClause, DomainFilter filte
209222 )
210223 );
211224 }
225+
226+ return columnExists ;
212227 }
213228
214- // Special domain data filter method for provisioned tables that have a built-in Flag field
215- protected void addDomainDataFlagFilter (OrClause orClause , DomainFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
229+ // Add a filter to select all rows where the object property with <propertyId> equals the filter value
230+ protected void addObjectPropertyFilter (OrClause orClause , DataFilter filter , TableInfo sourceTable , FieldKey fKey , int propertyId )
231+ {
232+ SQLFragment flagWhere = new SQLFragment ("lsid IN (SELECT ObjectURI FROM exp.Object WHERE ObjectId IN (SELECT ObjectId FROM exp.ObjectProperty WHERE StringValue = ? AND PropertyId = ?))" , filter .condition ().getParamVals ()[0 ], propertyId );
233+
234+ orClause .addClause (
235+ new AndClause (
236+ getContainerClause (sourceTable , fKey , filter .containers ()),
237+ new SQLClause (flagWhere )
238+ )
239+ );
240+ }
241+
242+ // Special domain data filter method for provisioned tables that have a built-in Flag field (currently used by data classes)
243+ protected void addDomainDataFlagFilter (OrClause orClause , DataFilter filter , TableInfo sourceTable , FieldKey fKey , Set <String > selectColumnNames )
216244 {
217245 if (filter .column ().equalsIgnoreCase ("Flag" ))
218246 {
219- SQLFragment flagWhere = new SQLFragment ("lsid IN (SELECT ObjectURI FROM exp.Object WHERE ObjectId IN (SELECT ObjectId FROM exp.ObjectProperty WHERE StringValue = ? AND PropertyId = ?))" , filter .condition ().getParamVals ()[0 ], getCommentPropertyId (sourceTable ));
220-
221- // Select all rows where the built-in flag column equals the filter value
222- orClause .addClause (
223- new AndClause (
224- getContainerClause (sourceTable , fKey , filter .containers ()),
225- new SimpleFilter .SQLClause (flagWhere )
226- )
227- );
247+ addObjectPropertyFilter (orClause , filter , sourceTable , fKey , getCommentPropertyId (sourceTable .getSchema ().getScope ()));
228248 }
229249 else
230250 {
231- addDomainDataStandardFilter (orClause , filter , sourceTable , fKey , selectColumnNames );
251+ addDataFilter (orClause , filter , sourceTable , fKey , selectColumnNames );
232252 }
233253 }
234254
235255 private Integer _commentPropertyId = null ;
236256
237- protected synchronized int getCommentPropertyId (TableInfo sourceTable )
257+ protected synchronized int getCommentPropertyId (DbScope scope )
238258 {
239259 if (_commentPropertyId == null )
240260 {
241261 // Get the exp.PropertyDescriptor table from the source scope
242- TableInfo propertyDescriptor = sourceTable . getSchema (). getScope () .getSchema ("exp" , DbSchemaType .Migration ).getTable ("PropertyDescriptor" );
262+ TableInfo propertyDescriptor = scope .getSchema ("exp" , DbSchemaType .Migration ).getTable ("PropertyDescriptor" );
243263 // Select the PropertyId associated with built-in Flag fields ("urn:exp.labkey.org/#Comment")
244264 Integer propertyId = new TableSelector (propertyDescriptor , Collections .singleton ("PropertyId" ), new SimpleFilter (FieldKey .fromParts ("PropertyURI" ), "urn:exp.labkey.org/#Comment" ), null ).getObject (Integer .class );
245265 if (propertyId == null )
@@ -274,22 +294,6 @@ interface MigrationTableHandler
274294 FilterClause getAdditionalFilterClause (Set <GUID > containers );
275295 }
276296
277- abstract class DefaultMigrationTableHandler implements MigrationTableHandler
278- {
279- private final TableInfo _tableInfo ;
280-
281- public DefaultMigrationTableHandler (TableInfo tableInfo )
282- {
283- _tableInfo = tableInfo ;
284- }
285-
286- @ Override
287- public TableInfo getTableInfo ()
288- {
289- return _tableInfo ;
290- }
291- }
292-
293297 /**
294298 * A MigrationFilter adds support for the named filter property in the migration configuration file. If present,
295299 * saveFilter() is called with the container guid and property value. Modules can register these to present
@@ -301,4 +305,26 @@ interface MigrationFilter
301305 // Implementations should validate guid nullity
302306 void saveFilter (@ Nullable GUID guid , String value );
303307 }
308+
309+ // Helper method that parses a data filter then adds it and its container to the provided collections, coalescing
310+ // cases where multiple containers specify the same filter
311+ static void addDataFilter (String filterName , List <DataFilter > dataFilters , Set <GUID > filteredContainers , GUID guid , String filter )
312+ {
313+ String [] filterParts = filter .split ("=" );
314+ if (filterParts .length != 2 )
315+ throw new ConfigurationException ("Bad " + filterName + " value; expected <columnName>=<value>: " + filter );
316+
317+ if (!filteredContainers .add (guid ))
318+ throw new ConfigurationException ("Duplicate " + filterName + " entry for container " + guid );
319+
320+ String column = filterParts [0 ];
321+ String value = filterParts [1 ];
322+ FilterClause clause = CompareType .EQUAL .createFilterClause (new FieldKey (null , column ), value );
323+ // If another container is already using this filter clause, then simply add this guid to that domain filter.
324+ // Otherwise, add a new domain filter to the list.
325+ dataFilters .stream ()
326+ .filter (df -> df .column ().equals (column ) && df .condition ().equals (clause ))
327+ .findFirst ()
328+ .ifPresentOrElse (df -> df .containers ().add (guid ), () -> dataFilters .add (new DataFilter (new HashSet <>(Set .of (guid )), filterParts [0 ], clause )));
329+ }
304330}
0 commit comments