<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Sqlite on Cesar Gimenes</title><link>https://crg.eti.br/tags/sqlite/</link><description>Recent content in Sqlite on Cesar Gimenes</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>crg@crg.eti.br (Cesar Gimenes)</managingEditor><webMaster>crg@crg.eti.br (Cesar Gimenes)</webMaster><lastBuildDate>Sun, 31 May 2026 01:02:01 -0300</lastBuildDate><atom:link href="https://crg.eti.br/tags/sqlite/index.xml" rel="self" type="application/rss+xml"/><item><title>An encrypted vault in Go</title><link>https://crg.eti.br/pub/cofre-cifrado-em-go/</link><pubDate>Sun, 31 May 2026 01:02:01 -0300</pubDate><author>crg@crg.eti.br (Cesar Gimenes)</author><guid>https://crg.eti.br/pub/cofre-cifrado-em-go/</guid><description>&lt;p>Let&amp;rsquo;s put together the three previous pieces:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://crg.eti.br/pub/capturando-senhas-no-terminal-go/">terminal password input&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://crg.eti.br/pub/criptografia-at-rest-com-a-stdlib-go/">encryption at rest&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://crg.eti.br/pub/sqlite-na-ram-serialize-deserialize/">SQLite snapshot&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>A SQLite database that lives in RAM and, on disk, exists only as an encrypted blob.&lt;/p>
&lt;p>The database runs in &lt;code>:memory:&lt;/code>.
On exit, we take a &lt;em>snapshot&lt;/em> of the database bytes, encrypt it with the password, and write it out.
On startup, we read the blob, decrypt it with the password, and &lt;em>deserialize&lt;/em> it back into RAM.&lt;/p></description></item><item><title>SQLite in RAM: serialize and deserialize in Go</title><link>https://crg.eti.br/pub/sqlite-na-ram-serialize-deserialize/</link><pubDate>Sun, 31 May 2026 00:46:31 -0300</pubDate><author>crg@crg.eti.br (Cesar Gimenes)</author><guid>https://crg.eti.br/pub/sqlite-na-ram-serialize-deserialize/</guid><description>&lt;p>SQLite can run entirely in memory, without ever touching disk. And there is a little-known trick: you can take a &lt;em>snapshot&lt;/em> of the database bytes and load them back later, with no &lt;code>INSERT&lt;/code>, no rebuilding anything. The functions are &lt;code>sqlite3_serialize&lt;/code> and &lt;code>sqlite3_deserialize&lt;/code>.&lt;/p>
&lt;p>I will use the &lt;code>modernc.org/sqlite&lt;/code> driver, which is pure Go, no CGo.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#1f2328">db&lt;/span>&lt;span style="color:#1f2328">,&lt;/span> &lt;span style="color:#1f2328">err&lt;/span> &lt;span style="color:#0550ae">:=&lt;/span> &lt;span style="color:#1f2328">sql&lt;/span>&lt;span style="color:#1f2328">.&lt;/span>&lt;span style="color:#6639ba">Open&lt;/span>&lt;span style="color:#1f2328">(&lt;/span>&lt;span style="color:#0a3069">&amp;#34;sqlite&amp;#34;&lt;/span>&lt;span style="color:#1f2328">,&lt;/span> &lt;span style="color:#0a3069">&amp;#34;:memory:&amp;#34;&lt;/span>&lt;span style="color:#1f2328">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#cf222e">if&lt;/span> &lt;span style="color:#1f2328">err&lt;/span> &lt;span style="color:#0550ae">!=&lt;/span> &lt;span style="color:#cf222e">nil&lt;/span> &lt;span style="color:#1f2328">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6639ba">fatal&lt;/span>&lt;span style="color:#1f2328">(&lt;/span>&lt;span style="color:#1f2328">err&lt;/span>&lt;span style="color:#1f2328">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#1f2328">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#1f2328">db&lt;/span>&lt;span style="color:#1f2328">.&lt;/span>&lt;span style="color:#6639ba">SetMaxOpenConns&lt;/span>&lt;span style="color:#1f2328">(&lt;/span>&lt;span style="color:#0550ae">1&lt;/span>&lt;span style="color:#1f2328">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The catch is in &lt;code>SetMaxOpenConns(1)&lt;/code>. A &lt;code>:memory:&lt;/code> database belongs to the connection that opened it. Without limiting it to one connection, the &lt;code>database/sql&lt;/code> pool could open another one, and that one would see a different, empty database.&lt;/p></description></item><item><title>Triggers in SQLite</title><link>https://crg.eti.br/pub/trigger-no-sqlite/</link><pubDate>Fri, 03 Oct 2025 23:57:24 -0300</pubDate><author>crg@crg.eti.br (Cesar Gimenes)</author><guid>https://crg.eti.br/pub/trigger-no-sqlite/</guid><description>&lt;p>&lt;em>SQLite&lt;/em> does not support &lt;em>stored procedures&lt;/em> directly, but it does support &lt;em>triggers&lt;/em>, which is a very useful feature for automating certain actions.&lt;/p>
&lt;p>For example, automatically updating the &lt;code>updated_at&lt;/code> field.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sql" data-lang="sql">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#cf222e">CREATE&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">TABLE&lt;/span>&lt;span style="color:#fff"> &lt;/span>users&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#1f2328">(&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff"> &lt;/span>id&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#6639ba">INTEGER&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">PRIMARY&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">KEY&lt;/span>&lt;span style="color:#fff"> &lt;/span>AUTOINCREMENT&lt;span style="color:#1f2328">,&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff"> &lt;/span>nome&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#6639ba">TEXT&lt;/span>&lt;span style="color:#1f2328">,&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff"> &lt;/span>updated_at&lt;span style="color:#fff"> &lt;/span>DATETIME&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">DEFAULT&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">CURRENT_TIMESTAMP&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#1f2328">);&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#cf222e">CREATE&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">TRIGGER&lt;/span>&lt;span style="color:#fff"> &lt;/span>update_timestamp&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#cf222e">BEFORE&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">UPDATE&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">ON&lt;/span>&lt;span style="color:#fff"> &lt;/span>users&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#cf222e">FOR&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">EACH&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">ROW&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#cf222e">BEGIN&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">UPDATE&lt;/span>&lt;span style="color:#fff"> &lt;/span>users&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">SET&lt;/span>&lt;span style="color:#fff"> &lt;/span>updated_at&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#0550ae">=&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">CURRENT_TIMESTAMP&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">WHERE&lt;/span>&lt;span style="color:#fff"> &lt;/span>id&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#0550ae">=&lt;/span>&lt;span style="color:#fff"> &lt;/span>&lt;span style="color:#cf222e">OLD&lt;/span>&lt;span style="color:#1f2328">.&lt;/span>id&lt;span style="color:#1f2328">;&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#fff">&lt;/span>&lt;span style="color:#cf222e">END&lt;/span>&lt;span style="color:#1f2328">;&lt;/span>&lt;span style="color:#fff">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, whenever you update a record in the &lt;code>users&lt;/code> table, the &lt;code>updated_at&lt;/code> field will be automatically set to the current &lt;em>timestamp&lt;/em>.&lt;/p></description></item></channel></rss>