<?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/en/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/en/tags/sqlite/index.xml" rel="self" type="application/rss+xml"/><item><title>An encrypted vault in Go</title><link>https://crg.eti.br/en/post/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/en/post/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/en/post/capturando-senhas-no-terminal-go/">terminal password input&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://crg.eti.br/en/post/criptografia-at-rest-com-a-stdlib-go/">encryption at rest&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://crg.eti.br/en/post/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/en/post/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/en/post/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="color:#e6edf3;background-color:#0d1117;-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>db, err &lt;span style="color:#ff7b72;font-weight:bold">:=&lt;/span> sql.&lt;span style="color:#d2a8ff;font-weight:bold">Open&lt;/span>(&lt;span style="color:#a5d6ff">&amp;#34;sqlite&amp;#34;&lt;/span>, &lt;span style="color:#a5d6ff">&amp;#34;:memory:&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff7b72">if&lt;/span> err &lt;span style="color:#ff7b72;font-weight:bold">!=&lt;/span> &lt;span style="color:#79c0ff">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#d2a8ff;font-weight:bold">fatal&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>db.&lt;span style="color:#d2a8ff;font-weight:bold">SetMaxOpenConns&lt;/span>(&lt;span style="color:#a5d6ff">1&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/en/post/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/en/post/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="color:#e6edf3;background-color:#0d1117;-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:#ff7b72">CREATE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">TABLE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>users&lt;span style="color:#6e7681"> &lt;/span>(&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681"> &lt;/span>id&lt;span style="color:#6e7681"> &lt;/span>INTEGER&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">PRIMARY&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">KEY&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>AUTOINCREMENT,&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681"> &lt;/span>nome&lt;span style="color:#6e7681"> &lt;/span>TEXT,&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681"> &lt;/span>updated_at&lt;span style="color:#6e7681"> &lt;/span>DATETIME&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">DEFAULT&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">CURRENT_TIMESTAMP&lt;/span>&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>);&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>&lt;span style="color:#ff7b72">CREATE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">TRIGGER&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>update_timestamp&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>&lt;span style="color:#ff7b72">BEFORE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">UPDATE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">ON&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>users&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>&lt;span style="color:#ff7b72">FOR&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">EACH&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">ROW&lt;/span>&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>&lt;span style="color:#ff7b72">BEGIN&lt;/span>&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">UPDATE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>users&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">SET&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>updated_at&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72;font-weight:bold">=&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">CURRENT_TIMESTAMP&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">WHERE&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>id&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72;font-weight:bold">=&lt;/span>&lt;span style="color:#6e7681"> &lt;/span>&lt;span style="color:#ff7b72">OLD&lt;/span>.id;&lt;span style="color:#6e7681">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6e7681">&lt;/span>&lt;span style="color:#ff7b72">END&lt;/span>;&lt;span style="color:#6e7681">
&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>