Sqlite 没有使用绑定参数查询的结果,适用于具有硬编码值的相同查询

Sqlite no results for query with a bound parameter, works with identical query with hardcoded value

我遇到了一个关于 Sqlite 3 的奇怪问题。我有一个相对简单的查询,它在其 WHERE 谓词中采用绑定参数。使用绑定参数执行查询时,我得到 0 个结果,但是 运行 使用硬编码值的完全相同的查询,我得到预期的结果数 (> 0)。

我已经通过调用 sqlite3_expanded_sql() 并检查生成的 SQL 字符串仔细检查了我的查询是否已按预期准备好。

我的绑定参数查询:

SELECT
    b.name,
    Y(Transform(b.geom, 4326)) "y",
    X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText(?, 4326), 27700)) > 0

我为绑定参数提供的值:

let rectWkt = """
POLYGON((
    -0.1381030196154711 51.51132617405723,
    -0.12929698038450965 51.51132617405723,
    -0.12929698038450965 51.50863378616471,
    -0.1381030196154711 51.50863378616471,
    -0.1381030196154711 51.51132617405723
))
"""

并且具有硬编码值的查询有效:

SELECT
    b.name,
    Y(Transform(b.geom, 4326)) "y",
    X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText('POLYGON((
    -0.1381030196154711 51.51132617405723,
    -0.12929698038450965 51.51132617405723,
    -0.12929698038450965 51.50863378616471,
    -0.1381030196154711 51.50863378616471,
    -0.1381030196154711 51.51132617405723
))', 4326), 27700)) > 0

最后,这是 sqlite3_expanded_sql() 的输出,我在绑定上述值后调用它:

SELECT
    b.name,
    Y(Transform(b.geom, 4326)) "y",
    X(Transform(b.geom, 4326)) "x"
FROM buildings b
WHERE Within(b.geom, Transform(PolygonFromText('POLYGON((
    -0.1381030196154711 51.51132617405723,
    -0.12929698038450965 51.51132617405723,
    -0.12929698038450965 51.50863378616471,
    -0.1381030196154711 51.50863378616471,
    -0.1381030196154711 51.51132617405723
))', 4326), 27700)) > 0

除非我遗漏了什么,否则这些是相同的语句,但在执行它们时我会得到完全不同的结果。

我正在使用 sqlite c API 使用 Swift 绑定执行查询,并且我已经加载了 Spatialite 扩展(用于几何函数)。

我检查用我的值调用 sqlite3_bind_text()SQLITE_OK。当我尝试遍历结果行时没有错误,我只是得到 SQLITE_DONE 作为第一个结果,即。结果集为空。

我设法解决了它:我需要将 SQLITE_TRANSIENT 作为最后一个参数传递给我 致电 sqlite3_bind_text。我相信这是因为我呼吁执行 查询在不同的范围内,到那时字符串值不在 范围。传递 SQLITE_TRANSIENT 作为第五个 arg 指示 Sqlite 使其 自己的值副本。

具体在Swift中,这个需要手动定义为 let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self) 因为它是在 Sqlite 头文件中用宏定义的(参见:https://www.sqlite.org/c3ref/c_static.html)。

我之前绑定的调用是这样的:

guard
    sqlite3_bind_text(stmnt, 1, rectangleWKT, -1, nil) == SQLITE_OK
else {
    throw DatabaseError.Bind(message: errorMessage)
}

但应该更像:

let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)
guard
    sqlite3_bind_text(stmnt, 1, rectangleWKT, -1, SQLITE_TRANSIENT) == SQLITE_OK
else {
    throw DatabaseError.Bind(message: errorMessage)
}

然后我可以将准备好的语句传递给另一个函数来执行并​​获取结果:

return loadBuildings(forStatement: stmnt)