1 /*
<lambda>null2  * Copyright 2018 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 
17 package androidx.room.solver.prepared.result
18 
19 import androidx.room.compiler.codegen.CodeLanguage
20 import androidx.room.compiler.codegen.XCodeBlock.Builder.Companion.applyTo
21 import androidx.room.compiler.codegen.XMemberName.Companion.packageMember
22 import androidx.room.compiler.codegen.XPropertySpec
23 import androidx.room.compiler.processing.XType
24 import androidx.room.compiler.processing.isInt
25 import androidx.room.compiler.processing.isKotlinUnit
26 import androidx.room.compiler.processing.isLong
27 import androidx.room.compiler.processing.isVoid
28 import androidx.room.compiler.processing.isVoidObject
29 import androidx.room.ext.KotlinTypeNames
30 import androidx.room.ext.RoomTypeNames
31 import androidx.room.parser.QueryType
32 import androidx.room.solver.CodeGenScope
33 import androidx.room.solver.prepared.binder.PreparedQueryResultBinder
34 
35 /**
36  * An adapter for [PreparedQueryResultBinder] that executes queries with INSERT, UPDATE or DELETE
37  * statements.
38  */
39 class PreparedQueryResultAdapter(private val returnType: XType, private val queryType: QueryType) {
40     companion object {
41         fun create(returnType: XType, queryType: QueryType) =
42             if (isValidReturnType(returnType, queryType)) {
43                 PreparedQueryResultAdapter(returnType, queryType)
44             } else {
45                 null
46             }
47 
48         private fun isValidReturnType(returnType: XType, queryType: QueryType): Boolean {
49             if (returnType.isVoid() || returnType.isVoidObject() || returnType.isKotlinUnit()) {
50                 return true
51             } else {
52                 return when (queryType) {
53                     QueryType.INSERT -> returnType.isLong()
54                     QueryType.UPDATE,
55                     QueryType.DELETE -> returnType.isInt()
56                     else -> false
57                 }
58             }
59         }
60     }
61 
62     fun executeAndReturn(
63         stmtQueryVal: String,
64         preparedStmtProperty: XPropertySpec?,
65         dbProperty: XPropertySpec,
66         scope: CodeGenScope
67     ) {
68         scope.builder.apply {
69             val stmtMethod =
70                 if (queryType == QueryType.INSERT) {
71                     "executeInsert"
72                 } else {
73                     "executeUpdateDelete"
74                 }
75             if (preparedStmtProperty != null) {
76                 beginControlFlow("try")
77             }
78             addStatement("%N.beginTransaction()", dbProperty)
79             beginControlFlow("try").apply {
80                 if (returnType.isVoid() || returnType.isVoidObject() || returnType.isKotlinUnit()) {
81                     addStatement("%L.%L()", stmtQueryVal, stmtMethod)
82                     addStatement("%N.setTransactionSuccessful()", dbProperty)
83                     if (returnType.isVoidObject()) {
84                         addStatement("return null")
85                     } else if (returnType.isKotlinUnit()) {
86                         applyTo(CodeLanguage.JAVA) {
87                             addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
88                         }
89                     }
90                 } else {
91                     val resultVar = scope.getTmpVar("_result")
92                     addLocalVal(
93                         resultVar,
94                         returnType.asTypeName(),
95                         "%L.%L()",
96                         stmtQueryVal,
97                         stmtMethod
98                     )
99                     addStatement("%N.setTransactionSuccessful()", dbProperty)
100                     addStatement("return %L", resultVar)
101                 }
102             }
103             nextControlFlow("finally").apply { addStatement("%N.endTransaction()", dbProperty) }
104             endControlFlow()
105             if (preparedStmtProperty != null) {
106                 nextControlFlow("finally")
107                 addStatement("%N.release(%L)", preparedStmtProperty, stmtQueryVal)
108                 endControlFlow()
109             }
110         }
111     }
112 
113     fun executeAndReturn(connectionVar: String, statementVar: String, scope: CodeGenScope) {
114         scope.builder.applyTo { language ->
115             addStatement("%L.step()", statementVar)
116             val returnPrefix =
117                 when (language) {
118                     CodeLanguage.JAVA -> "return "
119                     CodeLanguage.KOTLIN -> ""
120                 }
121             if (returnType.isVoid() || returnType.isVoidObject() || returnType.isKotlinUnit()) {
122                 if (returnType.isVoidObject()) {
123                     addStatement("${returnPrefix}null")
124                 } else if (returnType.isVoid() && language == CodeLanguage.JAVA) {
125                     addStatement("return null")
126                 } else if (returnType.isKotlinUnit() && language == CodeLanguage.JAVA) {
127                     addStatement("return %T.INSTANCE", KotlinTypeNames.UNIT)
128                 }
129             } else {
130                 val returnFunctionName =
131                     when (queryType) {
132                         QueryType.INSERT -> "getLastInsertedRowId"
133                         QueryType.UPDATE,
134                         QueryType.DELETE -> "getTotalChangedRows"
135                         else -> error("No return function name for query type $queryType")
136                     }
137                 addStatement(
138                     "$returnPrefix%M(%L)",
139                     RoomTypeNames.CONNECTION_UTIL.packageMember(returnFunctionName),
140                     connectionVar
141                 )
142             }
143         }
144     }
145 }
146