Merge pull request #1338 from serhiizghama/fix/duckdb-bind-parameter-order

Keep DuckDB bind values in query order when a parameter repeats
This commit is contained in:
David Mezzetti
2026-10-05 08:53:15 -04:00
committed by GitHub
2 changed files with 13 additions and 10 deletions
+6 -10
View File
@@ -139,20 +139,16 @@ class DuckDB(Embedded):
# Unpack query args # Unpack query args
query, parameters = args query, parameters = args
# Iterate over parameters # Match all named parameters in a single pass, so values are listed in the order they appear in the query
# - Replace named parameters with ?'s # - Match on a word boundary, bind parameters can be followed by any non-word character, i.e. `in (:x)`
# - Build list of value with position indexes
params = [] params = []
for key, value in parameters.items(): if parameters:
# Match on a word boundary, bind parameters can be followed by any non-word character, i.e. `in (:x)` pattern = r"\:(" + "|".join(re.escape(key) for key in parameters) + r")\b"
pattern = rf"\:{key}\b" params = [parameters[match.group(1)] for match in re.finditer(pattern, query)]
for match in re.finditer(pattern, query):
params.append((match.start(), value))
query = re.sub(pattern, "?", query) query = re.sub(pattern, "?", query)
# Repack query and parameter list # Repack query and parameter list
args = (query, [value for _, value in sorted(params, key=lambda x: x[0])]) args = (query, params)
return args return args
+7
View File
@@ -745,6 +745,13 @@ class Common:
result = self.embeddings.search("select * from txtai where text in (:x, :y)", parameters={"x": self.data[1], "y": self.data[4]}) result = self.embeddings.search("select * from txtai where text in (:x, :y)", parameters={"x": self.data[1], "y": self.data[4]})
self.assertEqual({row["text"] for row in result}, {self.data[1], self.data[4]}) self.assertEqual({row["text"] for row in result}, {self.data[1], self.data[4]})
# Test a repeated bind parameter followed by another bind parameter
result = self.embeddings.search(
"select * from txtai where text like :document or text like :document or text like :document or id = :id",
parameters={"document": "%iceberg%", "id": 4},
)
self.assertEqual({row["text"] for row in result}, {self.data[1], self.data[4]})
def testSparse(self): def testSparse(self):
""" """
Test sparse vector search Test sparse vector search