<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Magistrate Postgres Blog by Devin Clark</title>
  <subtitle>Helping Developers understand their Postgres databases</subtitle>
  <link href="https://magistratehq.com/feed/postgres/devin.xml" rel="self"/>
  <link href="https://magistratehq.com/blog"/>
  <link>https://magistratehq.com/blog</link>
  <updated>2020-10-01T00:00:00+00:00</updated>
  <id>https://magistratehq.com/blog</id>
  <author>
    <name>Devin Clark</name>
    <email>dev@magistratehq.com</email>
  </author>
  
  <entry>
    <title>Gravatars and Postgres 12 using Generated Columns</title>
    <link href="https://magistratehq.com/blog/generated-columns-postgres/"/>
    <updated>2020-07-08T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;Generated columns are an exciting new feature of Postgres. They allow you to replace some of your existing trigger logic in a faster and easier to read way.&lt;/p&gt;
&lt;!--more--&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; users &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;  id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;primary&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  name &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  email &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  avatar_url &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt; generated always &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http://gravatar.com/avatar/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; md5&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; stored&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  created_at timestamptz &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  updated_at timestamptz &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting part of this block of code is where the &lt;code&gt;avatar_url&lt;/code&gt; column is being created. It is creating a new column that is generated and stored on disk using the value of another column (email). That also means if a user&#39;s email is updated, the &lt;code&gt;avatar_url&lt;/code&gt; will also be updated with the correct Gravatar url. This functionality was added in Postgres 12.&lt;/p&gt;
&lt;p&gt;Let&#39;s say we decide to show an avatar that is more optimized for our app. Gravatar allows you to pass a size parameter in pixels for the returned image. This can be accomplished with Gravatar by adding &lt;code&gt;s=200&lt;/code&gt; as a query parameter. All this takes is altering the column.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;alter table &lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt; drop avatar_url&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;alter table &lt;span class=&quot;token function&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;column&lt;/span&gt; avatar_url text generated always as &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http://gravatar.com/avatar/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; md5&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;?s=200&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; stored&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might be wondering why you would store this value in the database instead of generating it in your app. Computing MD5 hashes isn&#39;t a very fast process (by design), especially when it needs to be done anytime an avatar needs to be displayed. Storing the calculated value will certainly speed things up.&lt;/p&gt;
&lt;p&gt;For more information about the possible configuration of Gravatar URLs, see this article from the &lt;a href=&quot;https://en.gravatar.com/site/implement/images/&quot;&gt;Gravatar documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Rob Conery shared another use of &lt;a href=&quot;https://rob.conery.io/2019/10/24/virtual-computed-columns-in-postgresql-12/&quot;&gt;generated columns using json columns&lt;/a&gt; that is worth checking out.&lt;/p&gt;
&lt;p&gt;This is just one reason we love Postgres, but there are so many other things to love about it! If you liked this blog post, and want to learn more about what Postgres has to offer, sign up for our mailing list!&lt;/p&gt;
</content>
    <id>https://magistratehq.com/blog/generated-columns-postgres/</id>
  </entry>
  
  <entry>
    <title>Case-Insensitive Text Columns in Postgres with citext</title>
    <link href="https://magistratehq.com/blog/citext-extension/"/>
    <updated>2020-08-15T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;Recently we heard from a friend that they were curious if there was a better way to work with case-insensitive text columns in their Postgres database. They were frustrated because they found a bug where part of their application considered the column to be case insensitive and another part did not.&lt;/p&gt;
&lt;p&gt;The column we were asked about was an email column that would be used lookup users and would certainly benefit from an index. This got us thinking about indexing case-insensitive text. Typically, the column would be defined as a text type, and everywhere it is referenced you would call &lt;code&gt;lower()&lt;/code&gt; on the value.&lt;/p&gt;
&lt;p&gt;That has a few drawbacks though. You have to remember to call &lt;code&gt;lower()&lt;/code&gt; on the value, a unique constraint on the column will be case-sensitive, and it will not use an index (unless it is an index on &lt;code&gt;lower(email)&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;So, let&#39;s start with a typical user lookup query.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; lower&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lower&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;?&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can improve this by changing up our table schema a bit. We are going to leverage a Postgres extension to get access to a new column type, &lt;a href=&quot;https://www.postgresql.org/docs/current/citext.html&quot;&gt;citext&lt;/a&gt; (case insensitive text).&lt;/p&gt;
&lt;p&gt;But first, we need to check to see if the extension is available on our database. This can be done by running the following query on our database or by looking at the extensions tab of our magistrate dashboard.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; pg_available_extensions &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;citext&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We got a result from running that query so we know it is available on our database. Now we can install the extension to our database with this query.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;create&lt;/span&gt; extension &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;exists&lt;/span&gt; citext&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have all of that set up we can change email to use the citext type we just added.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; users&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;column&lt;/span&gt; email &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; citext&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now our user lookup can be simplified to this query because the citext type is handling the &lt;code&gt;lower()&lt;/code&gt; calls.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ?&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This change squashed our friend&#39;s bug, and now our friend knows about citext for future case-insensitive text needs!&lt;/p&gt;
&lt;p&gt;citext is just one of the many Postgres extensions we love. If you liked this blog post, and want to learn more about what Magistrate has to offer, sign up for our mailing list below!&lt;/p&gt;
</content>
    <id>https://magistratehq.com/blog/citext-extension/</id>
  </entry>
  
  <entry>
    <title>Storing Timezones in Postgres</title>
    <link href="https://magistratehq.com/blog/user-timezones-in-postgres/"/>
    <updated>2020-10-01T00:00:00+00:00</updated>
    <content type="html">&lt;p&gt;Storing a user’s timezone in Postgres can be an interesting task in apps. Often, this is accomplished by generating a hardcoded list of every timezone in the backend (or worse the frontend JavaScript) and storing an item from that list in the database for each user. This hardcoded magic list is both difficult to build accurately and hard to maintain. Timezones change more frequently than you would imagine ...but less frequently than time itself.&lt;/p&gt;
&lt;p&gt;Postgres has a system view called &lt;code&gt;pg_timezone_names&lt;/code&gt; that has the list of timezones used by Postgres internally. We can use this view to generate a dropdown or other input type for the user to select their timezone. We can filter out some of the timezones in your query (like all the ones that start with “posix/“) because this list is quite large by default.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; pg_timezone_names &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;like&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;posix%&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;and&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;not&lt;/span&gt; ilike &lt;span class=&quot;token string&quot;&gt;&#39;system%&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;order&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we need to figure out the best way to store the selection from our user. We can do this by adding a column to our users table to hold the timezone. The column is a text type because we are only storing the &lt;code&gt;name&lt;/code&gt; value from &lt;code&gt;pg_timezone_names&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;alter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;table&lt;/span&gt; users &lt;span class=&quot;token keyword&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;column&lt;/span&gt; timezone &lt;span class=&quot;token keyword&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we want to query the user’s events in the timezone we have for the user. We store the &lt;code&gt;start_time&lt;/code&gt; for the events as UTC &lt;code&gt;timestamptz&lt;/code&gt; columns. This solution leverages the &lt;a href=&quot;https://www.postgresql.org/docs/13/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT&quot;&gt;timezone&lt;/a&gt; function in Postgres. &lt;code&gt;AT TIME ZONE zone&lt;/code&gt; also works. The last &lt;code&gt;join&lt;/code&gt; in this query is optional but it is there in case you need to know the UTC offset or if the timezone is in DST.&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timezone&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timezone&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; start_time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; events&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; users &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;user_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; pg_timezone_names &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; users&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timezone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pg_timezone_names&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could be taken a step further by using the &lt;a href=&quot;https://www.postgresql.org/docs/current/functions-formatting.html&quot;&gt;to_timestamp&lt;/a&gt; function in Postgres to format the dates, and keep the application from formatting dates at all.&lt;/p&gt;
</content>
    <id>https://magistratehq.com/blog/user-timezones-in-postgres/</id>
  </entry>
</feed>