Skip to content

Commit 2df0bbe

Browse files
authored
Fix getGeneratedKeys (#109)
1 parent d244bbc commit 2df0bbe

File tree

4 files changed

+92
-31
lines changed

4 files changed

+92
-31
lines changed

kotlin/local-data-api/src/com/koxudaxi/localDataApi/Application.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,11 @@ fun Application.module(testing: Boolean = false) {
9494
)
9595
val executeStatementResponse = try {
9696
val statement = if (request.parameters == null) {
97-
resource.connection.prepareStatementWithReturnGeneratedKeys(request.sql)
97+
resource.connection.prepareStatement(request.sql)
9898
} else {
9999
val parameters = Parameters.parse(request.sql)
100100
val statement =
101-
resource.connection.prepareStatementWithReturnGeneratedKeys(parameters.sql)
101+
resource.connection.prepareStatement(parameters.sql)
102102
parameters.apply(statement, request.parameters.map { Pair(it.name, it.castValue) }.toMap())
103103
statement
104104
}
@@ -117,7 +117,7 @@ fun Application.module(testing: Boolean = false) {
117117
} else {
118118
ExecuteStatementResponse(
119119
updatedCount,
120-
statement.updateResults.lastOrNull() ?: emptyList(),
120+
getGeneratedKeys(statement.connection),
121121
)
122122
}
123123
if (resource.transactionId == null) {
@@ -151,7 +151,7 @@ fun Application.module(testing: Boolean = false) {
151151
val batchExecuteStatementResponse = try {
152152
val parameters = Parameters.parse(request.sql)
153153
val statement =
154-
resource.connection.prepareStatementWithReturnGeneratedKeys(parameters.sql)
154+
resource.connection.prepareStatement(parameters.sql, Statement.RETURN_GENERATED_KEYS)
155155

156156
request.parameterSets.forEach { parameterSet ->
157157
parameters.apply(statement, parameterSet.map { Pair(it.name, it.castValue) }.toMap())

kotlin/local-data-api/src/com/koxudaxi/localDataApi/LocalDataApi.kt

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ fun createField(resultSet: ResultSet, index: Int): Field {
2727
}
2828
}
2929

30+
fun isPostgreSQL(connection: Connection): Boolean = "PostgreSQL" in connection.metaData.databaseProductName
31+
32+
fun getGeneratedKeys(connection: Connection): List<Field> {
33+
if (isPostgreSQL(connection)) return emptyList()
34+
val resultSet = connection.createStatement().executeQuery("SELECT LAST_INSERT_ID()")
35+
resultSet.next()
36+
return IntRange(1, resultSet.metaData.columnCount).mapNotNull { index ->
37+
resultSet.getInt(index)
38+
}.filter { it > 0 }.map { Field(longValue = it.toLong()) }.toList()
39+
}
40+
3041
val Statement.updateResults: List<List<Field>>
3142
get() {
3243
return this.generatedKeys.let { resultSet ->
@@ -56,19 +67,6 @@ val Statement.records: List<List<Field>>
5667
return records.toList()
5768
}
5869

59-
fun isReturnGeneratedKeysType(sql: String): Boolean {
60-
val match = Regex("^[^a-zA-Z]*([a-zA-Z]+)").find(sql) ?: return false
61-
return match.destructured.component1().toUpperCase() in listOf("INSERT", "UPDATE", "DELETE")
62-
}
63-
64-
fun Connection.prepareStatementWithReturnGeneratedKeys(sql: String): PreparedStatement {
65-
return if (isReturnGeneratedKeysType(sql)) {
66-
this.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
67-
} else {
68-
this.prepareStatement(sql)
69-
}
70-
}
71-
7270
fun createColumnMetadata(resultSet: ResultSet): List<ColumnMetadata> {
7371
return resultSet.metaData.let {
7472
IntRange(1, resultSet.metaData.columnCount).map { index ->

kotlin/local-data-api/test/com/koxudaxi/localDataApi/ApplicationTest.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,72 @@ class ApplicationTest {
300300
}
301301
}
302302

303+
@Test
304+
fun testExecuteInsertParametersPostgresql() {
305+
ResourceManager.INSTANCE.setResource("h2:./test;MODE=PostgreSQL", dummyResourceArn, null, null, emptyMap())
306+
307+
mockkStatic("com.koxudaxi.localDataApi.LocalDataApiKt")
308+
every { isPostgreSQL(any()) } returns true
309+
310+
withTestApplication({ module(testing = true) }) {
311+
handleRequest(HttpMethod.Post, "/Execute") {
312+
addHeader(HttpHeaders.ContentType, "*/*")
313+
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
314+
"CREATE TABLE TEST(id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10), age INT)"
315+
)))
316+
}.apply {
317+
assertEquals(
318+
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":[],\"records\":null,\"columnMetadata\":null}",
319+
response.content)
320+
assertEquals(HttpStatusCode.OK, response.status())
321+
}
322+
handleRequest(HttpMethod.Post, "/Execute") {
323+
addHeader(HttpHeaders.ContentType, "*/*")
324+
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
325+
"select * from TEST")))
326+
}.apply {
327+
assertEquals(
328+
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[],\"columnMetadata\":null}",
329+
response.content)
330+
assertEquals(HttpStatusCode.OK, response.status())
331+
}
332+
handleRequest(HttpMethod.Post, "/Execute") {
333+
addHeader(HttpHeaders.ContentType, "*/*")
334+
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
335+
"INSERT INTO TEST (name, age) VALUES ('cat', 1)")))
336+
}.apply {
337+
assertEquals(
338+
"{\"numberOfRecordsUpdated\":1,\"generatedFields\":[],\"records\":null,\"columnMetadata\":null}",
339+
response.content)
340+
assertEquals(HttpStatusCode.OK, response.status())
341+
}
342+
343+
handleRequest(HttpMethod.Post, "/Execute") {
344+
addHeader(HttpHeaders.ContentType, "*/*")
345+
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
346+
"INSERT INTO test (name, age) VALUES (:name, :age)", parameters = listOf(
347+
SqlParameter("name", Field(stringValue = "dog")),
348+
SqlParameter("age", Field(longValue = 3)),
349+
))))
350+
}.apply {
351+
assertEquals(
352+
"{\"numberOfRecordsUpdated\":1,\"generatedFields\":[],\"records\":null,\"columnMetadata\":null}",
353+
response.content)
354+
assertEquals(HttpStatusCode.OK, response.status())
355+
}
356+
handleRequest(HttpMethod.Post, "/Execute") {
357+
addHeader(HttpHeaders.ContentType, "*/*")
358+
setBody(Json.encodeToString(ExecuteStatementRequest(dummyResourceArn, dummySecretArn,
359+
"select * from TEST", includeResultMetadata = true)))
360+
}.apply {
361+
assertEquals(
362+
"{\"numberOfRecordsUpdated\":0,\"generatedFields\":null,\"records\":[[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":1,\"stringValue\":null},{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"cat\"},{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":1,\"stringValue\":null}],[{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":2,\"stringValue\":null},{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":null,\"stringValue\":\"dog\"},{\"blobValue\":null,\"booleanValue\":null,\"doubleValue\":null,\"isNull\":null,\"longValue\":3,\"stringValue\":null}]],\"columnMetadata\":[{\"arrayBaseColumnType\":0,\"isAutoIncrement\":true,\"isCaseSensitive\":true,\"isCurrency\":false,\"isSigned\":true,\"label\":\"ID\",\"name\":\"ID\",\"nullable\":0,\"precision\":10,\"scale\":0,\"schemaName\":\"PUBLIC\",\"tableName\":\"TEST\",\"type\":4,\"typeName\":\"INTEGER\"},{\"arrayBaseColumnType\":0,\"isAutoIncrement\":false,\"isCaseSensitive\":true,\"isCurrency\":false,\"isSigned\":true,\"label\":\"NAME\",\"name\":\"NAME\",\"nullable\":1,\"precision\":10,\"scale\":0,\"schemaName\":\"PUBLIC\",\"tableName\":\"TEST\",\"type\":12,\"typeName\":\"VARCHAR\"},{\"arrayBaseColumnType\":0,\"isAutoIncrement\":false,\"isCaseSensitive\":true,\"isCurrency\":false,\"isSigned\":true,\"label\":\"AGE\",\"name\":\"AGE\",\"nullable\":1,\"precision\":10,\"scale\":0,\"schemaName\":\"PUBLIC\",\"tableName\":\"TEST\",\"type\":4,\"typeName\":\"INTEGER\"}]}",
363+
response.content)
364+
assertEquals(HttpStatusCode.OK, response.status())
365+
}
366+
}
367+
}
368+
303369
@Test
304370
fun testExecuteInsertParametersWithTransaction() {
305371
withTestApplication({ module(testing = true) }) {

kotlin/local-data-api/test/com/koxudaxi/localDataApi/LocalDataApiTest.kt

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,6 @@ class LocalDataApiTest {
2222
assertEquals(listOf(Types.TIMESTAMP, Types.TIMESTAMP_WITH_TIMEZONE), DATETIME)
2323
}
2424

25-
@Test
26-
fun testIsReturnGeneratedKeysType() {
27-
assertEquals( false, isReturnGeneratedKeysType("selEct 1"))
28-
assertEquals( false, isReturnGeneratedKeysType(" selEct 1"))
29-
assertEquals( true, isReturnGeneratedKeysType(" insert 1"))
30-
assertEquals( true, isReturnGeneratedKeysType(" Update 1"))
31-
assertEquals( true, isReturnGeneratedKeysType(" DELETE 1"))
32-
assertEquals( false, isReturnGeneratedKeysType(" CREATE 1"))
33-
assertEquals( false, isReturnGeneratedKeysType(" ;"))
34-
assertEquals( false, isReturnGeneratedKeysType(""))
35-
}
36-
3725
@Test
3826
fun testMySQL() {
3927
mockkStatic(System::class)
@@ -78,7 +66,11 @@ class LocalDataApiTest {
7866
val resource = mockk<Resource>(relaxed = true)
7967
mockkStatic(Resource::class)
8068
every {
81-
Resource(Resource.Config("mysql", "arn:aws:rds:us-east-1:123456789012:cluster:dummy", "127.0.0.1", 3306, emptyMap()),
69+
Resource(Resource.Config("mysql",
70+
"arn:aws:rds:us-east-1:123456789012:cluster:dummy",
71+
"127.0.0.1",
72+
3306,
73+
emptyMap()),
8274
"root",
8375
"example",
8476
null,
@@ -107,7 +99,12 @@ class LocalDataApiTest {
10799
val resource = mockk<Resource>(relaxed = true)
108100
mockkStatic(Resource::class)
109101
every {
110-
Resource(Resource.Config("postgresql", "abc", "localhost", 1234, mapOf("stringtype" to "unspecified")), "user", "pass", null, null, "xyz")
102+
Resource(Resource.Config("postgresql", "abc", "localhost", 1234, mapOf("stringtype" to "unspecified")),
103+
"user",
104+
"pass",
105+
null,
106+
null,
107+
"xyz")
111108
} returns resource
112109

113110
val env = mapOf(

0 commit comments

Comments
 (0)