Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 82d783c4 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Support optional use of aggregation functions.

As long as the column being aggregated is valid with respect to
all existing projection checks, then we're willing to apply an
aggregation function on it.

Bug: 129220616
Test: atest --test-mapping packages/providers/MediaProvider
Change-Id: I8e4b665f5abd6553939a35d9f46247cc63c4c25f
parent f4a7a585
Loading
Loading
Loading
Loading
+43 −5
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
@@ -47,11 +48,15 @@ import java.util.regex.Pattern;
 */
public class SQLiteQueryBuilder {
    private static final String TAG = "SQLiteQueryBuilder";

    private static final Pattern sLimitPattern =
            Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
    private static final Pattern sAggregationPattern = Pattern.compile(
            "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL)\\((.+)\\)");

    private Map<String, String> mProjectionMap = null;
    private List<Pattern> mProjectionGreylist = null;
    private boolean mProjectionAggregationAllowed = false;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private String mTables = "";
@@ -203,6 +208,16 @@ public class SQLiteQueryBuilder {
        return mProjectionGreylist;
    }

    /** {@hide} */
    public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) {
        mProjectionAggregationAllowed = projectionAggregationAllowed;
    }

    /** {@hide} */
    public boolean isProjectionAggregationAllowed() {
        return mProjectionAggregationAllowed;
    }

    /**
     * Sets the cursor factory to be used for the query.  You can use
     * one factory for all queries on a database but it is normally
@@ -842,26 +857,48 @@ public class SQLiteQueryBuilder {
        return query.toString();
    }

    private static @NonNull String maybeWithOperator(@Nullable String operator,
            @NonNull String column) {
        if (operator != null) {
            return operator + "(" + column + ")";
        } else {
            return column;
        }
    }

    /** {@hide} */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    private String[] computeProjection(String[] projectionIn) {
    public String[] computeProjection(String[] projectionIn) {
        if (projectionIn != null && projectionIn.length > 0) {
            if (mProjectionMap != null) {
                String[] projection = new String[projectionIn.length];
                int length = projectionIn.length;

                for (int i = 0; i < length; i++) {
                    String operator = null;
                    String userColumn = projectionIn[i];
                    String column = mProjectionMap.get(userColumn);

                    // If aggregation is allowed, extract the underlying column
                    // that may be aggregated
                    if (mProjectionAggregationAllowed) {
                        final Matcher matcher = sAggregationPattern.matcher(userColumn);
                        if (matcher.matches()) {
                            operator = matcher.group(1);
                            userColumn = matcher.group(2);
                            column = mProjectionMap.get(userColumn);
                        }
                    }

                    if (column != null) {
                        projection[i] = column;
                        projection[i] = maybeWithOperator(operator, column);
                        continue;
                    }

                    if (!mStrict &&
                            ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
                        /* A column alias already exist */
                        projection[i] = userColumn;
                        projection[i] = maybeWithOperator(operator, userColumn);
                        continue;
                    }

@@ -878,7 +915,7 @@ public class SQLiteQueryBuilder {

                        if (match) {
                            Log.w(TAG, "Allowing abusive custom column: " + userColumn);
                            projection[i] = userColumn;
                            projection[i] = maybeWithOperator(operator, userColumn);
                            continue;
                        }
                    }
@@ -911,7 +948,8 @@ public class SQLiteQueryBuilder {
        return null;
    }

    private @Nullable String computeWhere(@Nullable String selection) {
    /** {@hide} */
    public @Nullable String computeWhere(@Nullable String selection) {
        final boolean hasInternal = !TextUtils.isEmpty(mWhereClause);
        final boolean hasExternal = !TextUtils.isEmpty(selection);