19d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki/*
29d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * Copyright (C) 2016 The Android Open Source Project
39d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki *
49d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * Licensed under the Apache License, Version 2.0 (the "License");
59d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * you may not use this file except in compliance with the License.
69d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * You may obtain a copy of the License at
79d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki *
89d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki *      http://www.apache.org/licenses/LICENSE-2.0
99d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki *
109d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * Unless required by applicable law or agreed to in writing, software
119d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * distributed under the License is distributed on an "AS IS" BASIS,
129d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * See the License for the specific language governing permissions and
149d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * limitations under the License
159d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki */
169d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
179d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukipackage com.android.providers.contacts.sqlite;
189d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
199d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport android.database.Cursor;
209d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport android.database.sqlite.SQLiteDatabase;
219d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport android.util.Log;
229d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
239d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport com.android.providers.contacts.AbstractContactsProvider;
249d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
258411b174dee903acf8f7d16a8f46da2a7265f1abMakoto Onukiimport com.google.common.annotations.VisibleForTesting;
268411b174dee903acf8f7d16a8f46da2a7265f1abMakoto Onuki
279d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport java.util.ArrayList;
289d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukiimport java.util.List;
299d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
309d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki/**
319d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki * Class to extract table/view/column names from databases.
329d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki */
338411b174dee903acf8f7d16a8f46da2a7265f1abMakoto Onuki@VisibleForTesting
349d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onukipublic class DatabaseAnalyzer {
359d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    private static final String TAG = "DatabaseAnalyzer";
369d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
379d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    private static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING;
389d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
399d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    private DatabaseAnalyzer() {
409d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    }
419d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
429d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    /**
439d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     * Find and return all table/view names in a db.
449d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     */
459d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    private static List<String> findTablesAndViews(SQLiteDatabase db) {
469d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        final List<String> ret = new ArrayList<>();
479d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        try (final Cursor c = db.rawQuery(
489d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                "SELECT name FROM sqlite_master WHERE type in (\"table\", \"view\")", null)) {
499d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            while (c.moveToNext()) {
5079e61b15a11698197da4bb78cd1be469a38b9c35Makoto Onuki                ret.add(c.getString(0).toLowerCase());
519d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            }
529d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        }
539d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        return ret;
549d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    }
559d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
569d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    /**
579d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     * Find all columns in a table/view.
589d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     */
599d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    private static List<String> findColumns(SQLiteDatabase db, String table) {
609d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        final List<String> ret = new ArrayList<>();
619d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
629d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        // Open the table/view but requests 0 rows.
639d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        final Cursor c = db.rawQuery("SELECT * FROM " + table + " WHERE 0 LIMIT 0", null);
649d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        try {
659d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            // Collect the column names.
669d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            for (int i = 0; i < c.getColumnCount(); i++) {
6779e61b15a11698197da4bb78cd1be469a38b9c35Makoto Onuki                ret.add(c.getColumnName(i).toLowerCase());
689d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            }
699d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        } finally {
709d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            c.close();
719d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        }
729d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        return ret;
739d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    }
749d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki
759d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    /**
769d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     * Return all table/view names that clients shouldn't use in their queries -- basically the
779d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     * result contains all table/view names, except for the names that are column names of any
789d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     * tables.
799d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki     */
808411b174dee903acf8f7d16a8f46da2a7265f1abMakoto Onuki    @VisibleForTesting
819d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    public static List<String> findTableViewsAllowingColumns(SQLiteDatabase db) {
829d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        final List<String> tables = findTablesAndViews(db);
839d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        if (VERBOSE_LOGGING) {
849d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            Log.d(TAG, "Tables and views:");
859d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        }
869d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        final List<String> ret = new ArrayList<>(tables); // Start with the table/view list.
879d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        for (String name : tables) {
889d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            if (VERBOSE_LOGGING) {
899d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                Log.d(TAG, "  " + name);
909d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            }
919d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            final List<String> columns = findColumns(db, name);
929d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            if (VERBOSE_LOGGING) {
939d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                Log.d(TAG, "    Columns: " + columns);
949d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            }
959d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            for (String c : columns) {
969d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                if (ret.remove(c)) {
979d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                    Log.d(TAG, "Removing [" + c + "] from disallow list");
989d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki                }
999d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki            }
1009d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        }
1019d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki        return ret;
1029d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki    }
1039d70f53e0d2eda1356af27b2a083c96257ff872eMakoto Onuki}
104