Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -357,8 +357,8 @@ public Iterable<HistoricItem> query(FilterCriteria filter) {
public Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alias) {
logIfManyQueuedTasks();
Instant start = Instant.now();
String filterDescription = filterToString(filter);
logger.trace("Got a query with filter {}", filterDescription);
String filterDescription = filter.toString();
logger.trace("Got a query with filter {}", filter);
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable filterDescription is assigned but never used after line 360. It should either be removed or used in the logging statement instead of calling filter.toString() implicitly.

Suggested change
logger.trace("Got a query with filter {}", filter);
logger.trace("Got a query with filter {}", filterDescription);

Copilot uses AI. Check for mistakes.
DynamoDbEnhancedAsyncClient localClient = client;
DynamoDBTableNameResolver localTableNameResolver = tableNameResolver;
if (!isProperlyConfigured) {
Expand Down Expand Up @@ -675,12 +675,4 @@ private void logIfManyQueuedTasks() {
}
}
}

private String filterToString(FilterCriteria filter) {
return String.format(
"FilterCriteria@%s(item=%s, pageNumber=%d, pageSize=%d, time=[%s, %s, %s], state=[%s, %s of %s] )",
System.identityHashCode(filter), filter.getItemName(), filter.getPageNumber(), filter.getPageSize(),
filter.getBeginDate(), filter.getEndDate(), filter.getOrdering(), filter.getOperator(),
filter.getState(), filter.getState() == null ? "null" : filter.getState().getClass().getSimpleName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ private static void addStateFilter(QueryEnhancedRequest.Builder queryBuilder,
return null;
}
});
if (filter.getOperator() != null && filter.getState() != null) {
if (filter.getState() != null) {
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for filter.getOperator() before calling getSymbol(). The null check for operator was removed but the code still assumes it's non-null. This could throw a NullPointerException if state is provided without an operator.

Suggested change
if (filter.getState() != null) {
if (filter.getState() != null) {
if (filter.getOperator() == null) {
throw new IllegalArgumentException("Operator must not be null when state is provided in filter.");
}

Copilot uses AI. Check for mistakes.
Copy link
Member Author

@ccutrer ccutrer Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getOperator() is @NonNull, so the check is unnecessary

// Convert filter's state to DynamoDBItem in order get suitable string representation for the state
Expression.Builder stateFilterExpressionBuilder = Expression.builder()
.expression(String.format("#attr %s :value", operatorAsString(filter.getOperator())));
.expression(String.format("#attr %s :value", filter.getOperator().getSymbol()));
// Following will throw IllegalArgumentException when filter state is not compatible with
// item. This is acceptable.
GenericItem stateToFind = DynamoDBPersistenceService.copyItem(item, item, filter.getItemName(),
Expand Down Expand Up @@ -206,32 +206,6 @@ private static void addFilterbyItemAndTimeFilter(QueryEnhancedRequest.Builder qu
}
}

/**
* Convert op to string suitable for dynamodb filter expression
*
* @param op
* @return string representation corresponding to the given the Operator
*/
private static String operatorAsString(Operator op) {
switch (op) {
case EQ:
return "=";
case NEQ:
return "<>";
case LT:
return "<";
case LTE:
return "<=";
case GT:
return ">";
case GTE:
return ">=";

default:
throw new IllegalStateException("Unknown operator " + op);
}
}

private static <T> void acceptAsDTO(Item item, boolean legacy, DynamoDBItemVisitor<T> visitor) {
ZonedDateTime dummyTimestamp = ZonedDateTime.now();
if (legacy) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,7 @@ public Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alia
return List.of();
}
if (serviceActivated && checkConnection()) {
logger.trace(
"Query-Filter: itemname: {}, ordering: {}, state: {}, operator: {}, getBeginDate: {}, getEndDate: {}, getPageSize: {}, getPageNumber: {}",
itemName, filter.getOrdering().toString(), filter.getState(), filter.getOperator(),
filter.getBeginDate(), filter.getEndDate(), filter.getPageSize(), filter.getPageNumber());
logger.trace("Query-Filter: {}", filter);

List<InfluxDBRepository.InfluxRow> results = influxDBRepository.query(filter,
configuration.getRetentionPolicy(), alias);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,4 @@ public interface FilterCriteriaQueryCreator {
* @return Created query as a String
*/
String createQuery(FilterCriteria criteria, String retentionPolicy, @Nullable String alias);

default String getOperationSymbol(FilterCriteria.Operator operator, InfluxDBVersion version) {
return switch (operator) {
case EQ -> "=";
case LT -> "<";
case LTE -> "<=";
case GT -> ">";
case GTE -> ">=";
case NEQ -> version == InfluxDBVersion.V1 ? "<>" : "!=";
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ public String createQuery(FilterCriteria criteria, String retentionPolicy, @Null
}

State filterState = criteria.getState();
if (filterState != null && criteria.getOperator() != null) {
if (filterState != null) {
where.and(new SimpleClause(COLUMN_VALUE_NAME_V1,
getOperationSymbol(criteria.getOperator(), InfluxDBVersion.V1), stateToObject(filterState)));
getOperationSymbol(criteria.getOperator()), stateToObject(filterState)));
}

if (criteria.getOrdering() == FilterCriteria.Ordering.DESCENDING) {
Expand Down Expand Up @@ -120,4 +120,11 @@ private String fullQualifiedTableName(String retentionPolicy, String tableName,
}
return sb.toString();
}

private String getOperationSymbol(FilterCriteria.Operator operator) {
if (operator == NEQ) {
return "<>";
}
return operator.getSymbol();
}
Comment on lines 123 to 128
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for operator parameter. If null is passed (from line 78 where filter.getOperator() is called), this will throw a NullPointerException when comparing with NEQ. Add a null check at the beginning of the method.

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ public String createQuery(FilterCriteria criteria, String retentionPolicy, @Null
}

State filterState = criteria.getState();
if (filterState != null && criteria.getOperator() != null) {
if (filterState != null) {
Restrictions restrictions = Restrictions.and(Restrictions.field().equal(FIELD_VALUE_NAME),
Restrictions.value().custom(stateToObject(filterState),
getOperationSymbol(criteria.getOperator(), InfluxDBVersion.V2)));
criteria.getOperator().getSymbol()));
flux = flux.filter(restrictions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ protected String histItemFilterQueryProvider(FilterCriteria filter, int numberDe
String simpleName, ZoneId timeZone) {
logger.debug(
"JDBC::getHistItemFilterQueryProvider filter = {}, numberDecimalcount = {}, table = {}, simpleName = {}",
StringUtilsExt.filterToString(filter), numberDecimalcount, table, simpleName);
filter, numberDecimalcount, table, simpleName);

String filterString = "";
ZonedDateTime beginDate = filter.getBeginDate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,31 +254,4 @@ public static List<Integer> substrPos(String s, String substr, boolean ignoreCas
}
return arr;
}

/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public static String filterToString(FilterCriteria filter) {
StringBuilder builder = new StringBuilder();
builder.append("FilterCriteria [itemName=");
builder.append(filter.getItemName());
builder.append(", beginDate=");
builder.append(filter.getBeginDate());
builder.append(", endDate=");
builder.append(filter.getEndDate());
builder.append(", pageNumber=");
builder.append(filter.getPageNumber());
builder.append(", pageSize=");
builder.append(filter.getPageSize());
builder.append(", operator=");
builder.append(filter.getOperator());
builder.append(", ordering=");
builder.append(filter.getOrdering());
builder.append(", state=");
builder.append(filter.getState());
builder.append("]");
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.openhab.core.persistence.PersistenceService;
import org.openhab.core.persistence.QueryablePersistenceService;
import org.openhab.core.persistence.strategy.PersistenceStrategy;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.openhab.persistence.jpa.internal.model.JpaPersistentItem;
import org.osgi.framework.BundleContext;
Expand Down Expand Up @@ -219,6 +220,7 @@ public Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alia

boolean hasBeginDate = false;
boolean hasEndDate = false;
State state = null;
String queryString = "SELECT n FROM " + JpaPersistentItem.class.getSimpleName()
+ " n WHERE n.realName = :itemName";
if (filter.getBeginDate() != null) {
Expand All @@ -229,6 +231,9 @@ public Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alia
queryString += " AND n.timestamp <= :endDate";
hasEndDate = true;
}
if ((state = filter.getState()) != null) {
Copy link

Copilot AI Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for filter.getOperator() before calling getSymbol(). If a state is provided without an operator, this will throw a NullPointerException. Add a check: if ((state = filter.getState()) != null && filter.getOperator() != null)

Suggested change
if ((state = filter.getState()) != null) {
if ((state = filter.getState()) != null && filter.getOperator() != null) {

Copilot uses AI. Check for mistakes.
queryString += " AND n.value " + filter.getOperator().getSymbol() + " :state";
}
queryString += " ORDER BY n.timestamp " + sortOrder;

logger.debug("The query: {}", queryString);
Expand All @@ -247,6 +252,9 @@ public Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alia
if (hasEndDate) {
query.setParameter("endDate", Date.from(filter.getEndDate().toInstant()));
}
if (state != null) {
query.setParameter("state", StateHelper.toString(state));
}

query.setFirstResult(filter.getPageNumber() * filter.getPageSize());
query.setMaxResults(filter.getPageSize());
Expand Down
Loading