1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.arch.persistence.room.verifier
18
19import columnInfo
20import android.arch.persistence.room.processor.Context
21import android.arch.persistence.room.vo.Entity
22import android.arch.persistence.room.vo.Warning
23import java.io.File
24import java.sql.Connection
25import java.sql.DriverManager
26import java.sql.SQLException
27import java.util.UUID
28import javax.lang.model.element.Element
29
30/**
31 * Builds an in-memory version of the database and verifies the queries against it.
32 * This class is also used to resolve the return types.
33 */
34class DatabaseVerifier private constructor(
35        val connection : Connection, val context : Context, val entities : List<Entity>) {
36    companion object {
37        /**
38         * Tries to create a verifier but returns null if it cannot find the driver.
39         */
40        fun create(context: Context, element: Element, entities: List<Entity>) : DatabaseVerifier? {
41            return try {
42                // see: https://github.com/xerial/sqlite-jdbc/issues/97
43                val tmpDir = System.getProperty("java.io.tmpdir")
44                if (tmpDir == null) {
45                    context.logger.w(Warning.MISSING_JAVA_TMP_DIR,
46                            element, DatabaseVerificaitonErrors.CANNOT_GET_TMP_JAVA_DIR)
47                    return null
48                }
49                val outDir = File(tmpDir, "room-${UUID.randomUUID()}")
50                outDir.mkdirs()
51                outDir.deleteOnExit()
52                System.setProperty("org.sqlite.tmpdir", outDir.absolutePath)
53                //force load:
54                Class.forName("org.sqlite.JDBC")
55                val connection = DriverManager.getConnection("jdbc:sqlite::memory:")
56                DatabaseVerifier(connection, context, entities)
57            } catch (ex : Exception) {
58                context.logger.w(Warning.CANNOT_CREATE_VERIFICATION_DATABASE, element,
59                        DatabaseVerificaitonErrors.cannotCreateConnection(ex))
60                null
61            }
62        }
63    }
64    init {
65        entities.forEach { entity ->
66            val stmt = connection.createStatement()
67            stmt.executeUpdate(entity.createTableQuery)
68        }
69    }
70
71    fun analyze(sql : String) : QueryResultInfo {
72        return try {
73            val stmt = connection.prepareStatement(sql)
74            QueryResultInfo(stmt.columnInfo())
75        } catch (ex : SQLException) {
76            QueryResultInfo(emptyList(), ex)
77        }
78    }
79
80    fun closeConnection() {
81        if (!connection.isClosed) {
82            try {
83                connection.close()
84            } catch (t : Throwable) {
85                //ignore.
86            }
87        }
88    }
89}
90