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

Commit 7d8ce4be authored by Lee Shombert's avatar Lee Shombert Committed by Android (Google) Code Review
Browse files

Merge "Improve SQL statement type guesses" into main

parents b3644ceb d27f3177
Loading
Loading
Loading
Loading
+87 −37
Original line number Diff line number Diff line
@@ -77,6 +77,16 @@ public class DatabaseUtils {
    /** One of the values returned by {@link #getSqlStatementType(String)}. */
    public static final int STATEMENT_OTHER = 99;

    // The following statement types are "extended" and are for internal use only.  These types
    // are not public and are never returned by {@link #getSqlStatementType(String)}.

    /** An internal statement type @hide **/
    public static final int STATEMENT_WITH = 100;
    /** An internal statement type @hide **/
    public static final int STATEMENT_CREATE = 101;
    /** An internal statement type denoting a comment. @hide **/
    public static final int STATEMENT_COMMENT = 102;

    /**
     * Special function for writing an exception result at the header of
     * a parcel, to be used when returning an exception from a transaction.
@@ -1563,6 +1573,79 @@ public class DatabaseUtils {
        db.close();
    }

    /**
     * The legacy prefix matcher.
     */
    private static String getSqlStatementPrefixSimple(@NonNull String sql) {
        sql = sql.trim();
        if (sql.length() < 3) {
            return null;
        }
        return sql.substring(0, 3).toUpperCase(Locale.ROOT);
    }

    /**
     * Return the extended statement type for the SQL statement.  This is not a public API and it
     * can return values that are not publicly visible.
     * @hide
     */
    private static int categorizeStatement(@NonNull String prefix, @NonNull String sql) {
        if (prefix == null) return STATEMENT_OTHER;

        switch (prefix) {
            case "SEL": return STATEMENT_SELECT;
            case "INS":
            case "UPD":
            case "REP":
            case "DEL": return STATEMENT_UPDATE;
            case "ATT": return STATEMENT_ATTACH;
            case "COM":
            case "END": return STATEMENT_COMMIT;
            case "ROL":
                if (sql.toUpperCase(Locale.ROOT).contains(" TO ")) {
                    // Rollback to savepoint.
                    return STATEMENT_OTHER;
                }
                return STATEMENT_ABORT;
            case "BEG": return STATEMENT_BEGIN;
            case "PRA": return STATEMENT_PRAGMA;
            case "CRE": return STATEMENT_CREATE;
            case "DRO":
            case "ALT": return STATEMENT_DDL;
            case "ANA":
            case "DET": return STATEMENT_UNPREPARED;
            case "WIT": return STATEMENT_WITH;
            default:
                if (prefix.startsWith("--") || prefix.startsWith("/*")) {
                    return STATEMENT_COMMENT;
                }
                return STATEMENT_OTHER;
        }
    }

    /**
     * Return the extended statement type for the SQL statement.  This is not a public API and it
     * can return values that are not publicly visible.
     * @hide
     */
    public static int getSqlStatementTypeExtended(@NonNull String sql) {
        int type = categorizeStatement(getSqlStatementPrefixSimple(sql), sql);
        return type;
    }

    /**
     * Convert an extended statement type to a public SQL statement type value.
     * @hide
     */
    public static int getSqlStatementType(int extended) {
        switch (extended) {
            case STATEMENT_CREATE: return STATEMENT_DDL;
            case STATEMENT_WITH: return STATEMENT_OTHER;
            case STATEMENT_COMMENT: return STATEMENT_OTHER;
        }
        return extended;
    }

    /**
     * Returns one of the following which represent the type of the given SQL statement.
     * <ol>
@@ -1572,49 +1655,16 @@ public class DatabaseUtils {
     *   <li>{@link #STATEMENT_BEGIN}</li>
     *   <li>{@link #STATEMENT_COMMIT}</li>
     *   <li>{@link #STATEMENT_ABORT}</li>
     *   <li>{@link #STATEMENT_PRAGMA}</li>
     *   <li>{@link #STATEMENT_DDL}</li>
     *   <li>{@link #STATEMENT_UNPREPARED}</li>
     *   <li>{@link #STATEMENT_OTHER}</li>
     * </ol>
     * @param sql the SQL statement whose type is returned by this method
     * @return one of the values listed above
     */
    public static int getSqlStatementType(String sql) {
        sql = sql.trim();
        if (sql.length() < 3) {
            return STATEMENT_OTHER;
        }
        String prefixSql = sql.substring(0, 3).toUpperCase(Locale.ROOT);
        if (prefixSql.equals("SEL")) {
            return STATEMENT_SELECT;
        } else if (prefixSql.equals("INS") ||
                prefixSql.equals("UPD") ||
                prefixSql.equals("REP") ||
                prefixSql.equals("DEL")) {
            return STATEMENT_UPDATE;
        } else if (prefixSql.equals("ATT")) {
            return STATEMENT_ATTACH;
        } else if (prefixSql.equals("COM")) {
            return STATEMENT_COMMIT;
        } else if (prefixSql.equals("END")) {
            return STATEMENT_COMMIT;
        } else if (prefixSql.equals("ROL")) {
            boolean isRollbackToSavepoint = sql.toUpperCase(Locale.ROOT).contains(" TO ");
            if (isRollbackToSavepoint) {
                Log.w(TAG, "Statement '" + sql
                        + "' may not work on API levels 16-27, use ';" + sql + "' instead");
                return STATEMENT_OTHER;
            }
            return STATEMENT_ABORT;
        } else if (prefixSql.equals("BEG")) {
            return STATEMENT_BEGIN;
        } else if (prefixSql.equals("PRA")) {
            return STATEMENT_PRAGMA;
        } else if (prefixSql.equals("CRE") || prefixSql.equals("DRO") ||
                prefixSql.equals("ALT")) {
            return STATEMENT_DDL;
        } else if (prefixSql.equals("ANA") || prefixSql.equals("DET")) {
            return STATEMENT_UNPREPARED;
        }
        return STATEMENT_OTHER;
        return getSqlStatementType(getSqlStatementTypeExtended(sql));
    }

    /**
+3 −2
Original line number Diff line number Diff line
@@ -1096,7 +1096,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
        seqNum = mPreparedStatementCache.getLastSeqNum();
        try {
            final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
            final int type = DatabaseUtils.getSqlStatementType(sql);
            final int type = DatabaseUtils.getSqlStatementTypeExtended(sql);
            final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
            statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly,
                    seqNum);
@@ -1279,7 +1279,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen

    private static boolean isCacheable(int statementType) {
        if (statementType == DatabaseUtils.STATEMENT_UPDATE
                || statementType == DatabaseUtils.STATEMENT_SELECT) {
            || statementType == DatabaseUtils.STATEMENT_SELECT
            || statementType == DatabaseUtils.STATEMENT_WITH) {
            return true;
        }
        return false;
+44 −0
Original line number Diff line number Diff line
@@ -17,6 +17,15 @@
package android.database;

import static android.database.DatabaseUtils.bindSelection;
import static android.database.DatabaseUtils.getSqlStatementType;
import static android.database.DatabaseUtils.getSqlStatementTypeExtended;
import static android.database.DatabaseUtils.STATEMENT_COMMENT;
import static android.database.DatabaseUtils.STATEMENT_CREATE;
import static android.database.DatabaseUtils.STATEMENT_DDL;
import static android.database.DatabaseUtils.STATEMENT_OTHER;
import static android.database.DatabaseUtils.STATEMENT_SELECT;
import static android.database.DatabaseUtils.STATEMENT_UPDATE;
import static android.database.DatabaseUtils.STATEMENT_WITH;

import static org.junit.Assert.assertEquals;

@@ -63,4 +72,39 @@ public class DatabaseUtilsTest {
                bindSelection("foo=?10 AND bar=? AND meow=?1",
                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
    }

    @Test
    public void testStatementType() {
        final int sel = STATEMENT_SELECT;
        assertEquals(sel, getSqlStatementType("SELECT"));
        assertEquals(sel, getSqlStatementType("  SELECT"));
        assertEquals(sel, getSqlStatementType(" \n SELECT"));

        final int upd = STATEMENT_UPDATE;
        assertEquals(upd, getSqlStatementType("UPDATE"));
        assertEquals(upd, getSqlStatementType("  UPDATE"));
        assertEquals(upd, getSqlStatementType(" \n UPDATE"));

        final int ddl = STATEMENT_DDL;
        assertEquals(ddl, getSqlStatementType("ALTER TABLE t1 ADD COLUMN j int"));
        assertEquals(ddl, getSqlStatementType("CREATE TABLE t1 (i int)"));

        // Short statements, leading comments, and WITH are decoded to "other" in the public API.
        final int othr = STATEMENT_OTHER;
        assertEquals(othr, getSqlStatementType("SE"));
        assertEquals(othr, getSqlStatementType("SE LECT"));
        assertEquals(othr, getSqlStatementType("-- cmt\n SE"));
        assertEquals(othr, getSqlStatementType("WITH"));

        // Test the extended statement types.

        final int wit = STATEMENT_WITH;
        assertEquals(wit, getSqlStatementTypeExtended("WITH"));

        final int cmt = STATEMENT_COMMENT;
        assertEquals(cmt, getSqlStatementTypeExtended("-- cmt\n SELECT"));

        final int cre = STATEMENT_CREATE;
        assertEquals(cre, getSqlStatementTypeExtended("CREATE TABLE t1 (i int)"));
    }
}