On the other hand I know the day will come.... Dealing with databases and database components (Delphi) for about 15 years I find it very difficult to deal with SQLDb's transaction handling. From my point of view the transaction handling is broken by design (maybe just my limited view), because it seems to open a transaction even for read-only operations.
Well, you are not forced to use transactions, certainly not in the case of Sqlite.
Transactions are not a concept native to Freepascal, they actually are also present in Delphi.
In complex applications they are in both flavors of Pascal the preferred method. Strange you find them hard to use or flawed: they aren't.
You can also use TSqlite3Dataset directly. Problem solved.
But indeed, transactions are tightly bound to a TYPE of transaction. Be it a select, insert or update. If you use CommitRetaining use it only for the same TYPE of transaction, otherwise commit and close.
Re-open the transaction ( or recreate) if the type of transaction differs is good practice and may help in your case.
Otherwise: use TSqlite3Dataset directly. No transactions needed. Again, Delphi offers the same options.
The reason that transactions are used even for read-only operations is that e.g. a database can be fully locked because of maintenance (re-writes, re-index) which can lead to invalid reads/indices/caches.
Transactions are really there for multi-user database use.