rogerbinns/apsw

`connection.setrowtrace` stops working when adapters are registered

Closed this issue · 5 comments

apsw 3.43.0

I'm trying to insert date(times) automagically. I'm building on the example here:
https://rogerbinns.github.io/apsw/example.html#example-type-conversion

Something like this:

import apsw.ext
from datetime import date, datetime


def _datetime_to_sqlite_value(d: Union[datetime, date]) -> str:
	return d.isoformat()

# ... database stuff

connection.setrowtrace(_factory_to_dict_apsw)
	

# Register datetypes to convert during insertion
_registrar = apsw.ext.TypesConverterCursorFactory()
connection.cursor_factory = _registrar

# Datetime
_registrar.register_adapter(datetime, self._datetime_to_sqlite_value)
_registrar.register_adapter(date, self._datetime_to_sqlite_value)

# ... database stuff

(Still a work in progress; not yet confirmed _datetime_to_sqlite_value does what I want because of the below)

Note the line: connection.setrowtrace(_factory_to_dict_apsw) - this used to work just fine, but ever since I've added the registrar, it's now ignored completely and my results are tuples again.

It's likely I'm misunderstanding something here, but from my reading of the docs, these things are independent. One is for reading, one is for writing (https://rogerbinns.github.io/apsw/ext.html#apsw.ext.TypesConverterCursorFactory.register_adapter).

If they can't be used together, or one overrides the other, it's be worth documenting this with the methods.

A cursor factory overrides the row trace. Heck it overrides the cursor! The best way to think of it is that row trace works with the builtin cursor, while a cursor factory takes over all cursor duties and does whatever it wants however it wants.

If you look closely you'll notice the cursor factory is the one providing the execute and executemany methods.

It is documented as doing conversion in and out:

Provides cursors that can convert objects into one of the types supported by SQLite, or back from SQLite

It is documented as doing conversion in and out:

Provides cursors that can convert objects into one of the types supported by SQLite, or back from SQLite

Indeed, but I presumed (through the power of "motivated reasoning" (aka: wishful thinking)) that meant different methods handled different components. Specifically:
register_adapter - Registers a callable that converts from klass to one of the supported SQLite types - to SQLite
register_converter - Registers a callable that converts from a SQLite value - from SQLite

I parse that as: if I set an adapter, that's writing to SQLite. My naive interpretation then is that the rowtrace will continue to handle the reading from SQLite. Evidently this is wrong, but you can hopefully see how the docs foster this misunderstanding.

A cursor factory overrides the row trace.

To my (very) lay mind, these are basically the same thing. After all, I point the rowtrace at a cursor factory. Obviously I'm wrong there too; I suppose my point here is that the learning curve behind apsw is deceptively steep.

row tracing is for row tracing. The types converter is for converting types.

I've just double checked and none of the doc for row tracing suggests using it for type conversion. (Testing is a good use case.) Adding doc clarification and pointers.

row tracing is for row tracing.

As a tautology, that presumes the user knows what "row tracing" is. https://rogerbinns.github.io/apsw/execution.html#row-tracer in turn is predicated on knowing what a "tracer" is - the docs don't really go into detail beyond saying they're useful for testing/diagnostics.

I actually got my rowtrace factory from your docs:

	def _factory_to_dict_apsw(cursor, row):
		# From: https://rogerbinns.github.io/apsw/tips.html#cursors
		return {k[0]: row[i] for i, k in enumerate(cursor.getdescription())}

I see it has gone now. Unfortunately this means there's now no documentation for how to achieve this (or maybe I'm missing it).

That example is showing how to return a dict instead of a tuple. The whole page it came from is about the execution model. You are right that there is a learning curve going from nothing to understanding SQLite's execution model to understanding how APSW simplifies and wraps that. The example/tour page tries to help. I often feel the same way when trying a completely new package with new concepts and ways of doing things. It always takes me a bit of work for it to click together.

Unfortunately this means there's now no documentation for how to achieve this (or maybe I'm missing it).

By "it" I believe you mean converting types going into the database and coming back out. The example section does show both directions. I added datetime in a commit yesterday which will be in the next release which should help your specific case.