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 package androidx.sqlite.db 17 18 import java.util.regex.Pattern 19 20 /** A simple query builder to create SQL SELECT queries. */ 21 public class SupportSQLiteQueryBuilder private constructor(private val table: String) { 22 private var distinct = false 23 private var columns: Array<out String>? = null 24 private var selection: String? = null 25 private var bindArgs: Array<out Any?>? = null 26 private var groupBy: String? = null 27 private var having: String? = null 28 private var orderBy: String? = null 29 private var limit: String? = null 30 31 /** 32 * Adds DISTINCT keyword to the query. 33 * 34 * @return this 35 */ <lambda>null36 public fun distinct(): SupportSQLiteQueryBuilder = apply { this.distinct = true } 37 38 /** 39 * Sets the given list of columns as the columns that will be returned. 40 * 41 * @param columns The list of column names that should be returned. 42 * @return this 43 */ <lambda>null44 public fun columns(columns: Array<out String>?): SupportSQLiteQueryBuilder = apply { 45 this.columns = columns 46 } 47 48 /** 49 * Sets the arguments for the WHERE clause. 50 * 51 * @param selection The list of selection columns 52 * @param bindArgs The list of bind arguments to match against these columns 53 * @return this 54 */ selectionnull55 public fun selection( 56 selection: String?, 57 bindArgs: Array<out Any?>? 58 ): SupportSQLiteQueryBuilder = apply { 59 this.selection = selection 60 this.bindArgs = bindArgs 61 } 62 63 /** 64 * Adds a GROUP BY statement. 65 * 66 * @param groupBy The value of the GROUP BY statement. 67 * @return this 68 */ <lambda>null69 public fun groupBy(groupBy: String?): SupportSQLiteQueryBuilder = apply { 70 this.groupBy = groupBy 71 } 72 73 /** 74 * Adds a HAVING statement. You must also provide [groupBy] for this to work. 75 * 76 * @param having The having clause. 77 * @return this 78 */ <lambda>null79 public fun having(having: String?): SupportSQLiteQueryBuilder = apply { this.having = having } 80 81 /** 82 * Adds an ORDER BY statement. 83 * 84 * @param orderBy The order clause. 85 * @return this 86 */ <lambda>null87 public fun orderBy(orderBy: String?): SupportSQLiteQueryBuilder = apply { 88 this.orderBy = orderBy 89 } 90 91 /** 92 * Adds a LIMIT statement. 93 * 94 * @param limit The limit value. 95 * @return this 96 */ <lambda>null97 public fun limit(limit: String): SupportSQLiteQueryBuilder = apply { 98 val patternMatches = limitPattern.matcher(limit).matches() 99 require(limit.isEmpty() || patternMatches) { "invalid LIMIT clauses:$limit" } 100 this.limit = limit 101 } 102 103 /** 104 * Creates the [SupportSQLiteQuery] that can be passed into [SupportSQLiteDatabase.query]. 105 * 106 * @return a new query 107 */ createnull108 public fun create(): SupportSQLiteQuery { 109 require(!groupBy.isNullOrEmpty() || having.isNullOrEmpty()) { 110 "HAVING clauses are only permitted when using a groupBy clause" 111 } 112 val query = 113 buildString(120) { 114 append("SELECT ") 115 if (distinct) { 116 append("DISTINCT ") 117 } 118 if (!columns.isNullOrEmpty()) { 119 appendColumns(columns!!) 120 } else { 121 append("* ") 122 } 123 append("FROM ") 124 append(table) 125 appendClause(" WHERE ", selection) 126 appendClause(" GROUP BY ", groupBy) 127 appendClause(" HAVING ", having) 128 appendClause(" ORDER BY ", orderBy) 129 appendClause(" LIMIT ", limit) 130 } 131 return SimpleSQLiteQuery(query, bindArgs) 132 } 133 StringBuildernull134 private fun StringBuilder.appendClause(name: String, clause: String?) { 135 if (!clause.isNullOrEmpty()) { 136 append(name) 137 append(clause) 138 } 139 } 140 141 /** Add the names that are non-null in columns to string, separating them with commas. */ StringBuildernull142 private fun StringBuilder.appendColumns(columns: Array<out String>) { 143 val n = columns.size 144 for (i in 0 until n) { 145 val column = columns[i] 146 if (i > 0) { 147 append(", ") 148 } 149 append(column) 150 } 151 append(' ') 152 } 153 154 public companion object { 155 private val limitPattern = Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?") 156 157 /** 158 * Creates a query for the given table name. 159 * 160 * @param tableName The table name(s) to query. 161 * @return A builder to create a query. 162 */ 163 @JvmStatic buildernull164 public fun builder(tableName: String): SupportSQLiteQueryBuilder { 165 return SupportSQLiteQueryBuilder(tableName) 166 } 167 } 168 } 169