Skip to content

Commit df248da

Browse files
committed
Allow postgres queries to contain '??' for '?'
Issue mauricio#60. Also makes query parsing about 20 times faster.
1 parent b91191d commit df248da

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

postgresql-async/src/main/scala/com/github/mauricio/async/db/postgresql/PostgreSQLConnection.scala

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -106,25 +106,35 @@ class PostgreSQLConnection
106106
promise.future
107107
}
108108

109+
private def prepareQueryParams(query : String) : (String, Int) = {
110+
val result = new StringBuilder(query.length+16)
111+
var offset = 0
112+
var params = 0
113+
while (offset < query.length) {
114+
val next = query.indexOf('?', offset)
115+
if (next == -1) {
116+
result ++= query.substring(offset)
117+
offset = query.length
118+
} else {
119+
result ++= query.substring(offset, next)
120+
offset = next + 1
121+
if (offset < query.length && query(offset) == '?') {
122+
result += '?'
123+
offset += 1
124+
} else {
125+
result += '$'
126+
params += 1
127+
result ++= params.toString
128+
}
129+
}
130+
}
131+
(result.toString, params)
132+
}
133+
109134
override def sendPreparedStatement(query: String, values: Seq[Any] = List()): Future[QueryResult] = {
110135
validateQuery(query)
111136

112-
var paramsCount = 0
113-
114-
val realQuery = if (query.contains("?")) {
115-
query.foldLeft(new StringBuilder()) {
116-
(builder, char) =>
117-
if (char == '?') {
118-
paramsCount += 1
119-
builder.append("$" + paramsCount)
120-
} else {
121-
builder.append(char)
122-
}
123-
builder
124-
}.toString()
125-
} else {
126-
query
127-
}
137+
val (realQuery, paramsCount) = prepareQueryParams(query)
128138

129139
if (paramsCount != values.length) {
130140
throw new InsufficientParametersException(paramsCount, values)

postgresql-async/src/test/scala/com/github/mauricio/async/db/postgresql/PreparedStatementSpec.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class PreparedStatementSpec extends Specification with DatabaseTestHelper {
3939
val messagesUpdate = "UPDATE messages SET content = ?, moment = ? WHERE id = ?"
4040
val messagesSelectOne = "SELECT id, content, moment FROM messages WHERE id = ?"
4141
val messagesSelectAll = "SELECT id, content, moment FROM messages"
42+
val messagesSelectEscaped = "SELECT id, content, moment FROM messages WHERE content LIKE '%??%' AND id > ?"
4243

4344
"prepared statements" should {
4445

@@ -118,6 +119,29 @@ class PreparedStatementSpec extends Specification with DatabaseTestHelper {
118119
}
119120
}
120121

122+
"support prepared statement with escaped placeholders" in {
123+
withHandler {
124+
handler =>
125+
126+
val firstContent = "Some? Moment"
127+
val secondContent = "Some Other Moment"
128+
val date = LocalDate.now()
129+
130+
executeDdl(handler, this.messagesCreate)
131+
executePreparedStatement(handler, this.messagesInsert, Array(Some(firstContent), None))
132+
executePreparedStatement(handler, this.messagesInsert, Array(Some(secondContent), Some(date)))
133+
134+
val rows = executePreparedStatement(handler, this.messagesSelectEscaped, Array(0)).rows.get
135+
136+
rows.length === 1
137+
138+
rows(0)("id") === 1
139+
rows(0)("content") === firstContent
140+
rows(0)("moment") === null
141+
142+
}
143+
}
144+
121145
}
122146

123147
}

0 commit comments

Comments
 (0)