Extralite 3.0.0: fast and innovative SQLite wrapper for Ruby

thunderbong1 pts0 comments

Extralite 3.0.0 Released - NoteflakesExtralite 3.0.0 Released<br>02·07·2026<br>I’m pleased to announce the release of Extralite 3.0.0. This release marks the<br>addition of object graph transforms, the transition to modern defaults for<br>opening SQLite databases, and a minor security enhancement.

Extralite is a fast and<br>innovative SQLite wrapper for Ruby with a rich set of features. It provides<br>multiple ways of retrieving data from SQLite databases, makes it possible to use<br>SQLite databases in multi-threaded and multi-fibered Ruby apps, and includes a<br>comprehensive set of tools for managing SQLite databases.

Object Graph Transforms

In the last few years I’ve been transitioning from using an ORM (such as<br>ActiveRecord) as a database abstraction to a more explicit, less magical<br>approach where queries are expressed directly in SQL, and rows are represented<br>using hashes and arrays. In my opinion, this approach has numerous advantages:<br>less dependencies, less object allocations, better performance.

But something that was missing for me was the ability to take a query involving<br>table joins, and generate an object graph that represents the relationships<br>between the different entities returned in the result set. Suppose we have a<br>database with two tables: posts and authors. Each post has a single author, i.e.<br>a one-to-one relationship, and each author may have multiple posts, so a<br>one-to-many relationship. To get a list of posts together with their authors, we<br>could use the following query:

select posts.id, posts.title, posts.content, authors.id, authors.name<br>from posts<br>left join authors<br>on posts.author_id = authors.id

The result set will look something like the following:

posts.id<br>posts.title<br>posts.content<br>authors.id<br>authors.name

"foo"<br>"…"<br>"Jim"

"bar"<br>"…"<br>"Jim"

"baz"<br>"…"<br>"Joe"

Extralite will happily convert this result set into an array of hashes:

{ "posts.id" => 1, ..., "authors.id" => 1, ... },<br>{ "posts.id" => 2, ..., "authors.id" => 1, ... },<br>{ "posts.id" => 3, ..., "authors.id" => 2, ... },<br>...

While this contains all the data we asked for from the database, the data<br>remains flat. Also, the fact that the same author information is repeated across<br>multiple records is wasteful, and we have no way of knowing (other than checking<br>the author id) that we’re dealing with the same author. For a many-to-many<br>relationship, it would look even worse. Consider the following query:

select posts.id, posts.title, tags.id, tags.name<br>from posts<br>left join posts_tags<br>on posts.id = posts_tags.post_id<br>left join tags<br>on tags.id = posts_tags.tag_id

The result set will be returned by Extralite in the form:

{ "posts.id" => 1, ..., "tags.id" => 1, ... },<br>{ "posts.id" => 1, ..., "tags.id" => 2, ... },<br>{ "posts.id" => 2, ..., "tags.id" => 2, ... },<br>{ "posts.id" => 2, ..., "tags.id" => 3, ... },<br>...

Here, each post or tag may be repeated across multiple rows, which makes it<br>more difficult for us to make sense of this information - we’ll need to<br>continuously check the post and tag id in order to ensure we’re not processing<br>the same post or tag multiple times. What if we had a tool that allowed us to<br>make sense of such a result set without needing to do special processing. What<br>if we had a way to get the result set in a form that reflects the<br>relationships between the different entities?

Enter object graph transforms: the basic idea is that when we issue a query to<br>an Extralite database instance, we provide, along with the SQL string, a<br>transform specification that tells Extralite how to form the rows in the result<br>set. Let’s look again at the posts-tags example above. We might express the<br>object graph transform as follows:

# select posts.id, posts.title, tags.id, tags.name from ...<br>transform = Extralite::Transform.new do<br>id: integer.identity,<br>title: text,<br>tags: [{<br>id: integer.identity,<br>name: text<br>}]<br>end

With this transform we map the four columns we requested in our SQL query to<br>nested entities. The transform DSL not only lets us express the form of the<br>object graph, it also lets us express optional type coercions (since by default<br>SQLite does not enforce column types)<br>and which column is used to identify which entity in order to prevent<br>duplicates. Will also enclose the tag entity in square brackets, in order to let<br>Extralite know that a post may have multiple tags. The result set generated<br>using the transform will look like:

id: 1,<br>title: ...,<br>tags: [<br>{ id: 1, ... },<br>{ id: 2, ... }<br>},<br>id: 2,<br>title: ...<br>tags: [<br>{ id: 2, ... },<br>{ id: 3, ... }

This object graph will be generated in such a way as to remove all duplication.<br>The first two rows pertaining to post #1 have been merged into a single post<br>entity with two tags. In addition, tag #2, which appears in the list of tags for<br>both post #1 and post #2, is actually the same object.

Under the hood, Extralite converts the transform spec defined using the<br>transform DSL into corresponding internal C<br>structs<br>in the form of a tree. When a query is ran using a transform, Extralite...

posts tags extralite authors transform object

Related Articles