188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar/*
288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * Copyright (C) 2017 The Android Open Source Project
388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar *
488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * you may not use this file except in compliance with the License.
688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * You may obtain a copy of the License at
788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar *
888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar *
1088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * Unless required by applicable law or agreed to in writing, software
1188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
1288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * See the License for the specific language governing permissions and
1488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * limitations under the License.
1588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar */
1688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
1764db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarpackage android.arch.persistence.room.verifier
1888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
1988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport columnInfo
2064db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.processor.Context
2164db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Entity
2264db0cc15b78b62a1d44a70fc8b4552e660d952cYigit Boyarimport android.arch.persistence.room.vo.Warning
2388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport java.io.File
2488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport java.sql.Connection
2588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport java.sql.DriverManager
2688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport java.sql.SQLException
2788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport java.util.UUID
2888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarimport javax.lang.model.element.Element
2988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
3088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar/**
3188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * Builds an in-memory version of the database and verifies the queries against it.
3288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar * This class is also used to resolve the return types.
3388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar */
3488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyarclass DatabaseVerifier private constructor(
3588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        val connection : Connection, val context : Context, val entities : List<Entity>) {
3688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    companion object {
3788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        /**
3888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar         * Tries to create a verifier but returns null if it cannot find the driver.
3988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar         */
4088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        fun create(context: Context, element: Element, entities: List<Entity>) : DatabaseVerifier? {
4188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            return try {
4288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                // see: https://github.com/xerial/sqlite-jdbc/issues/97
4388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                val tmpDir = System.getProperty("java.io.tmpdir")
4488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                if (tmpDir == null) {
45aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar                    context.logger.w(Warning.MISSING_JAVA_TMP_DIR,
46aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar                            element, DatabaseVerificaitonErrors.CANNOT_GET_TMP_JAVA_DIR)
4788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                    return null
4888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                }
4988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                val outDir = File(tmpDir, "room-${UUID.randomUUID()}")
5088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                outDir.mkdirs()
5188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                outDir.deleteOnExit()
5288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                System.setProperty("org.sqlite.tmpdir", outDir.absolutePath)
5388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                //force load:
5488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                Class.forName("org.sqlite.JDBC")
5588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                val connection = DriverManager.getConnection("jdbc:sqlite::memory:")
5688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                DatabaseVerifier(connection, context, entities)
5788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            } catch (ex : Exception) {
58aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar                context.logger.w(Warning.CANNOT_CREATE_VERIFICATION_DATABASE, element,
59aa82fce1d73394bdc7f4c2510cf94a3572032b24Yigit Boyar                        DatabaseVerificaitonErrors.cannotCreateConnection(ex))
6088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar                null
6188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            }
6288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        }
6388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    }
6488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    init {
6588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        entities.forEach { entity ->
6688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            val stmt = connection.createStatement()
6788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            stmt.executeUpdate(entity.createTableQuery)
6888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        }
6988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    }
7088865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar
7188865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    fun analyze(sql : String) : QueryResultInfo {
7288865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        return try {
7388865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            val stmt = connection.prepareStatement(sql)
7488865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            QueryResultInfo(stmt.columnInfo())
7588865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        } catch (ex : SQLException) {
7688865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar            QueryResultInfo(emptyList(), ex)
7788865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar        }
7888865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar    }
7977a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar
8077a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar    fun closeConnection() {
8177a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar        if (!connection.isClosed) {
8277a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar            try {
8377a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar                connection.close()
8477a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar            } catch (t : Throwable) {
8577a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar                //ignore.
8677a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar            }
8777a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar        }
8877a44be4d08eeed548f01560d9d332ef24138f45Yigit Boyar    }
8988865f77c35657a2bc545a718ca16a648fc8b62eYigit Boyar}
90