Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

connection.setrowtrace stops working when adapters are registered #516

Closed
mohmad-null opened this issue May 4, 2024 · 5 comments
Closed

Comments

@mohmad-null
Copy link

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.

@rogerbinns
Copy link
Owner

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

@mohmad-null
Copy link
Author

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.

@rogerbinns
Copy link
Owner

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.

@mohmad-null
Copy link
Author

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).

@rogerbinns
Copy link
Owner

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants