fix(sql): Oracle bind names must not be reserved words (ORA-01745)
Found by the new OracleReadTests against a real Oracle 23 Free: EVERY Oracle
driver instance failed its Initialize-time catalog load, and every AdminUI browse
of a table's columns threw, with
ORA-01745: invalid host/bind variable name
Oracle rejects a bind variable named after a reserved word, and the catalog
readers bind a parameter whose base name is `table` (SqlCatalogLoader /
SqlBrowseSession bind `schema` + `table`), so `ListColumnsSql` could not execute
at all. The provider was structurally non-functional.
OracleDialect now namespaces every marker/bind name through one `p_` constant
(`:p_k0` / `p_k0`), which a reserved word can never collide with, and its catalog
SQL is respelled to match. Prefixing everything rather than carrying a
reserved-word list is deliberate: the list is long and version-dependent, and a
future base name (`date`, `user`, `comment`, `level`…) would silently re-open the
same hole. Marker and bind name are derived from the same constant, so they
cannot drift apart.
No offline test could have caught this — only a server parses the SQL — so the
regression test asserts the invariant that made it possible (a bind name is never
the bare base name), with `table` called out explicitly.
Live evidence: 6/6 OracleReadTests red with ORA-01745 before, 6/6 green after,
against gvenzl/oracle-free:23-slim.
This commit is contained in:
@@ -57,15 +57,45 @@ public class OracleDialectTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParameterMarker_isColonPrefixed_andTheBindNameIsBare()
|
||||
public void ParameterMarker_isColonPrefixed_andTheBindNameIsTheSameNameWithoutTheColon()
|
||||
{
|
||||
var d = new OracleDialect();
|
||||
|
||||
// The whole reason ISqlDialect models these as two members: on ODP.NET they are different strings.
|
||||
d.ParameterMarker("k0").ShouldBe(":k0");
|
||||
d.ParameterBindName("k0").ShouldBe("k0");
|
||||
d.ParameterMarker("schema").ShouldBe(":schema");
|
||||
d.ParameterBindName("schema").ShouldBe("schema");
|
||||
d.ParameterMarker("k0").ShouldBe(":p_k0");
|
||||
d.ParameterBindName("k0").ShouldBe("p_k0");
|
||||
d.ParameterMarker("schema").ShouldBe(":p_schema");
|
||||
d.ParameterBindName("schema").ShouldBe("p_schema");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No bind name may be spelled as its bare base name — the <c>p_</c> namespace is what stops one
|
||||
/// colliding with an Oracle reserved word.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>This is a regression test for a live-found, provider-fatal defect.</b> The catalog readers bind
|
||||
/// a parameter whose base name is <c>table</c>, and Oracle rejects a bind variable named after a
|
||||
/// reserved word outright: <c>ORA-01745: invalid host/bind variable name</c>. With the un-prefixed
|
||||
/// names this dialect first shipped with, <c>ListColumnsSql</c> could not execute at all — so every
|
||||
/// Oracle driver instance failed its Initialize-time catalog load and rejected every authored tag, and
|
||||
/// every AdminUI browse of a table's columns threw. It was invisible to every offline test (only a
|
||||
/// server parses the SQL) and was caught by <c>OracleReadTests</c> against a real database.
|
||||
/// <para><c>table</c> is asserted explicitly because it is the one base name in use today that is
|
||||
/// actually reserved; the others guard the general rule.</para>
|
||||
/// </remarks>
|
||||
[Theory]
|
||||
[InlineData("table")]
|
||||
[InlineData("schema")]
|
||||
[InlineData("k0")]
|
||||
public void ParameterBindName_neverSurfacesTheBareBaseName_soItCannotBeAReservedWord(string baseName)
|
||||
{
|
||||
var d = new OracleDialect();
|
||||
|
||||
d.ParameterBindName(baseName).ShouldNotBe(baseName);
|
||||
d.ParameterMarker(baseName).ShouldNotBe(":" + baseName);
|
||||
// The two must stay derived from one another, or a statement's marker and its bind drift apart and
|
||||
// ODP.NET silently leaves the parameter unbound.
|
||||
d.ParameterMarker(baseName).ShouldBe(":" + d.ParameterBindName(baseName));
|
||||
}
|
||||
|
||||
// ---- identity ----
|
||||
|
||||
Reference in New Issue
Block a user