For SQLite 3.14.0 and newer, we're using the v2 trace API. This means that the trace callback receives a pointer to the sqlite3_stmt object. We can use the sqlite3_stmt pointer to retrieve expanded SQL string.
The following statement...:
cur.executemany("insert into t values(?)", ((v,) for v in range(3)))
...will produce the following traces:
insert into t values(0)
insert into t values(1)
insert into t values(2)
...instead of:
insert into t values(?)
insert into t values(?)
insert into t values(?)
Ah, one of my very first contributions, bpo-40318, comes back to haunt me:
In bpo-40318, we migrated from the old SQLite trace API (sqlite3_trace) to SQLite trace v2 API (sqlite3_trace_v2). GH-19581, which introduced this change, introduced a bug: the old trace API _implicitly_ expanded bound parameters; the new trace API does not. However, there was no tests for this behaviour, so the regression was unnoticed[^1].
So, this bpo is actually a bug fix; not a feature. It should be backported to 3.10, which contains the regression.
I'm preparing a fix for GH-28240, and I'll prepare a 3.10 backport including both GH-2840 and its upcoming fix.
[^1]: There has been no bug reports regarding this change in behaviour, so it seems to have gone under most people's radar.