Responding to your points in order: > In my code, a prepared statement is only used by a single thread. If my thread calls 'step' and starts fetching values, how will another 'step' be invoked which might alter a value? I'll stop referencing fetched values way before I invoke 'step' again. That was not apparent until just now. No thread without its own reference to the statement object can do stepping with it, nor get result column values. (This is apparent from the API together with the assumption that SQLite does not do much crazy-making.) > In my code, each value is only fetched one time per step. The invoking thread will retrieve the int64/double/text value, make a deep copy of it, then never fetch it again. If a value is only ever fetched once following a 'step', is there still a risk that its contents will auto-magically change? Depending on what was stored versus what type of value was asked for, there may be some auto-magical change but only upon that first access. This, too, puts your machinations in a better light. > In my code, statement text is generated programmatically, and every column's type is compiled into my code. A given value for a given table will always be written and fetched as the same type (either int64, real or text). My code does not initiate any type conversions. There should be no way my code would ever write a value out as an integer and try to read it in as text, or write it out as double and try to read it in as integer. Well, so far so good. And if that "text" is always stored with the same encoding as what you request, and no blobs become strings or vice-versa, I would even agree with your "does not initiate any type conversions" assertion. > ... this is a no-no, ... wondering how this has been working fine for me I believe that if you only rely on the guarantees that are part of the published API, then they will be met for a very long time (or soon made to be met if a bug is found whereby the code is shown to fail any guarantees.) However, when you rely on "undefined" behavior, (meaning not defined by the interface documentation), then it might work. Then, with some change in the way memory management is done, for time or space efficiency reasons, it could stop working. Fine today does not imply fine as the library evolves, even when it continues to honor past interface promises. (Sorry if this is too obvious to merit mention.) Personally, I have seen scads of bugs created by reliance upon undocumented features and behaviors of code with a well defined API. I consider it to be living in danger too much for my taste. But if you statically link the SQLite library and test each new release of your application that uses a later version of the library to become reasonably sure the not-promised but once-evident behaviors continue, then you could justify "reliance" on them.