<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PostgreSQL on Shallow Brook Software</title><link>https://shallowbrooksoftware.com/tags/postgresql/</link><description>Recent content in PostgreSQL on Shallow Brook Software</description><generator>Hugo</generator><language>en</language><lastBuildDate>Sun, 15 Dec 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://shallowbrooksoftware.com/tags/postgresql/index.xml" rel="self" type="application/rss+xml"/><item><title>Prepared Statement Caching: An Easy Win?</title><link>https://shallowbrooksoftware.com/posts/prepared-statement-caching-an-easy-win/</link><pubDate>Sun, 15 Dec 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/prepared-statement-caching-an-easy-win/</guid><description>&lt;p&gt;In PostgreSQL, &lt;a href="https://www.postgresql.org/docs/current/sql-prepare.html" class="external-link" target="_blank" rel="noopener"&gt;prepared statements&lt;/a&gt; are a means of optimization for executing SQL queries.&#10;Normally, SQL queries go through &lt;a href="https://www.postgresql.org/docs/current/query-path.html" class="external-link" target="_blank" rel="noopener"&gt;multiple phases&lt;/a&gt; of processing before being executed: parsing, rewriting, and planning.&#10;If your program executes the same query multiple times (as applications often do), then rerunning all of these steps each time is a bit redundant.&#10;By &amp;ldquo;preparing&amp;rdquo; the statement ahead of time, you can parse and rewrite the query once and then reuse the result.&#10;This means that a prepared statement only needs to be planned (and not parsed or rewritten) before execution.&lt;/p&gt;</description></item><item><title>Simple Database Migrations in Go</title><link>https://shallowbrooksoftware.com/posts/simple-database-migrations-in-go/</link><pubDate>Sun, 10 Nov 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/simple-database-migrations-in-go/</guid><description>&lt;p&gt;Historically, database migrations were a facet of web development that&amp;rsquo;d cause me to reach for a third-party library.&#10;I eventually discovered, however, that handing migrations is actually quite simple if you don&amp;rsquo;t need many features.&#10;At a high level, the process is as follows: list out the migration files, check which ones are missing, then apply them in order (each within a transaction).&#10;This whole workflow can be implemented in as few as ~100 lines of readable code!&lt;/p&gt;</description></item><item><title>Migrating Numeric IDs to UUIDs</title><link>https://shallowbrooksoftware.com/posts/migrating-numeric-ids-to-uuids/</link><pubDate>Sun, 03 Nov 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/migrating-numeric-ids-to-uuids/</guid><description>&lt;p&gt;There are a few ways to represent primary keys within a database.&#10;The most common way (in my experience) to use auto-incrementing integers.&#10;This means that first row&amp;rsquo;s key will be 1, the second&amp;rsquo;s will be 2, and so on.&#10;When it comes to my preferred style of software development, however, this approach has a major downside: new domain objects must first consult the database to determine their ID.&lt;/p&gt;&#10;&lt;p&gt;While I&amp;rsquo;m far from a &lt;a href="https://en.wikipedia.org/wiki/Domain-driven_design" class="external-link" target="_blank" rel="noopener"&gt;domain-driven design&lt;/a&gt; (DDD) purist, I like the idea of being able to represent the core domain objects and business logic of an application in complete isolation from the outside world.&#10;Having domain objects depend on the database for IDs makes this impossible (or at least very difficult).&#10;If only there existed a different kind of unique ID that could be generated by the domain objects themselves without requiring any coordination with an external system&amp;hellip;&lt;/p&gt;</description></item><item><title>Two Go + PostgreSQL Timestamp Gotchas</title><link>https://shallowbrooksoftware.com/posts/two-go-plus-postgresql-timestamp-gotchas/</link><pubDate>Sun, 30 Jun 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/two-go-plus-postgresql-timestamp-gotchas/</guid><description>&lt;p&gt;This week I added some additional tests to my &lt;a href="https://github.com/theandrew168/bloggulus" class="external-link" target="_blank" rel="noopener"&gt;Bloggulus&lt;/a&gt; project.&#10;In the process of doing so, I discovered a couple places where timestamp values weren&amp;rsquo;t matching what was expected.&#10;For some background, I generate, process, and store all timestamps in the &lt;a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time" class="external-link" target="_blank" rel="noopener"&gt;UTC time standard&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;The two gotchas were:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Unexpected conversion of UTC timestamps to local time&lt;/li&gt;&#10;&lt;li&gt;Mismatched precision between Go and PostgreSQL timestamps&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="unexpected-conversion"&gt;&#10; Unexpected Conversion&#10; &lt;a class="heading-link" href="#unexpected-conversion"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;I store timestamps in the database using PostgreSQL&amp;rsquo;s &lt;code&gt;timestamptz&lt;/code&gt; &lt;a href="https://www.postgresql.org/docs/current/datatype-datetime.html" class="external-link" target="_blank" rel="noopener"&gt;data type&lt;/a&gt;.&#10;The problem I ran into was that even when I was inserting proper UTC timestamps (generated by Go&amp;rsquo;s &lt;a href="https://pkg.go.dev/time" class="external-link" target="_blank" rel="noopener"&gt;time&lt;/a&gt; package), they were being returned from the database in my local time zone.&#10;What&amp;rsquo;s going on?&#10;Why are these UTC timestamps being converted to Central Standard Time when selected from the database?&lt;/p&gt;</description></item><item><title>Digimon Cards and Database Indexes</title><link>https://shallowbrooksoftware.com/posts/digimon-cards-and-database-indexes/</link><pubDate>Sun, 23 Jun 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/digimon-cards-and-database-indexes/</guid><description>&lt;p&gt;I&amp;rsquo;m a big fan of the &lt;a href="https://world.digimoncard.com/" class="external-link" target="_blank" rel="noopener"&gt;Digimon Card Game&lt;/a&gt; (the one released in 2020).&#10;The games are quick, the mechanics are engaging, and the community is friendly.&#10;Plus, I was a big fan of the Digimon anime as a kid so I have a lot of nostalgia for the characters.&#10;When I first got into the game, I realized that only a few sets had been released so far.&#10;I set my eyes on building and maintaining a complete collection: four copies of each card.&#10;With enough spreadsheets and time, this would allow me build any deck that I wanted.&#10;This was honestly a breath of fresh air coming from &lt;a href="https://magic.wizards.com/en" class="external-link" target="_blank" rel="noopener"&gt;Magic: The Gathering&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Reinforcing Indirect Joins</title><link>https://shallowbrooksoftware.com/posts/reinforcing-indirect-joins/</link><pubDate>Sun, 16 Jun 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/reinforcing-indirect-joins/</guid><description>&lt;p&gt;Recently, I ran into an issue where a very large PostgreSQL query (involving roughly 30 tables) was taking much longer to plan than to execute.&#10;At its worst, it was taking multiple &lt;em&gt;seconds&lt;/em&gt; to plan and a few hundred milliseconds to execute.&#10;My problem wasn&amp;rsquo;t really about standard query optimization: I was already past that.&#10;This was a problem of query complexity and trying to get the PostgreSQL &lt;a href="https://www.postgresql.org/docs/current/planner-optimizer.html" class="external-link" target="_blank" rel="noopener"&gt;planner/optimizer&lt;/a&gt; to more quickly arrive at an ideal query.&lt;/p&gt;</description></item><item><title>Using Newer PostgreSQL Client Tools in GitHub Actions</title><link>https://shallowbrooksoftware.com/posts/using-newer-postgres-client-tools-in-github-actions/</link><pubDate>Sun, 05 May 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/using-newer-postgres-client-tools-in-github-actions/</guid><description>&lt;p&gt;Recently, while updating my &lt;a href="https://github.com/theandrew168/pg2s3" class="external-link" target="_blank" rel="noopener"&gt;pg2s3 utility&lt;/a&gt;, I noticed that the project&amp;rsquo;s &lt;code&gt;docker-compose.yml&lt;/code&gt; file was pinning PostgreSQL to version 14.&#10;I couldn&amp;rsquo;t remember why I did that, so I went ahead and removed it (what could go wrong?).&#10;Unfortunately, this led to some automated tests failing in GitHub Actions!&lt;/p&gt;&#10;&lt;h2 id="the-problem"&gt;&#10; The Problem&#10; &lt;a class="heading-link" href="#the-problem"&gt;&#10; &lt;i class="fa-solid fa-link" aria-hidden="true" title="Link to heading"&gt;&lt;/i&gt;&#10; &lt;span class="sr-only"&gt;Link to heading&lt;/span&gt;&#10; &lt;/a&gt;&#10;&lt;/h2&gt;&#10;&lt;p&gt;Thankfully, the error was very clear:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;pg_dump: error: server version: 16.2 (Debian 16.2-1.pgdg120+2); pg_dump version: 14.11 (Ubuntu 14.11-1.pgdg22.04+1)&#10;pg_dump: error: aborting because of server version mismatch&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Classic version mismatch.&#10;The PostgreSQL 16 server (running in a container) was not compatible with the PostgreSQL 14 client tools (installed on the GitHub Actions runner).&#10;For some context, pg2s3 uses &lt;code&gt;pg_dump&lt;/code&gt; and &lt;code&gt;pg_restore&lt;/code&gt; to quickly export and import data.&lt;/p&gt;</description></item><item><title>Testing with Transactions</title><link>https://shallowbrooksoftware.com/posts/testing-with-transactions/</link><pubDate>Sun, 24 Mar 2024 00:00:00 +0000</pubDate><guid>https://shallowbrooksoftware.com/posts/testing-with-transactions/</guid><description>&lt;p&gt;Most web applications eventually end up with tests that need to interact with a database.&#10;Perhaps your business logic is tightly coupled to the database or maybe you are wanting to test a clearly-defined storage layer.&#10;Either way, a common problem arises: how do you clean up the data used during testing? What should you do with all those scattered rows?&lt;/p&gt;&#10;&lt;p&gt;Most of the time, developers will resort to a few common strategies:&lt;/p&gt;</description></item></channel></rss>