<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>sql &amp;mdash; Swagg::Blogg</title>
    <link>https://blog.swagg.net/tag:sql</link>
    <description>or: How I Learned to Stop Worrying and Love the WWW&lt;br&gt;&lt;a href=&#34;https://www.swagg.net&#34;&gt;Home&lt;/a&gt;&amp;nbsp;&lt;a href=&#34;https://blog.swagg.net/feed/&#34;&gt;RSS&lt;/a&gt;</description>
    <pubDate>Thu, 07 May 2026 11:43:43 +0000</pubDate>
    <image>
      <url>https://i.snap.as/gopN8vBC.png</url>
      <title>sql &amp;mdash; Swagg::Blogg</title>
      <link>https://blog.swagg.net/tag:sql</link>
    </image>
    <item>
      <title>I Like to Migrate, Migrate</title>
      <link>https://blog.swagg.net/i-like-to-migrate-migrate?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I actually normally wouldn&#39;t enjoy this but thankfully this Mojo::Pg wrapper makes it easy. I honestly don&#39;t have any experience with the original DBD::Pg as I&#39;m still very new to the database world so I hope this doesn&#39;t read too much like I&#39;m lost in the sauce. First I took a look at my current tables using the psql tool:&#xA;&#xA;posttext=  \d&#xA;                   List of relations&#xA; Schema |         Name          |   Type   |   Owner&#xA;--------+-----------------------+----------+-----------&#xA; public | mojomigrations       | table    | posttext&#xA; public | replies               | table    | posttext&#xA; public | repliesreplyidseq  | sequence | posttext&#xA; public | threads               | table    | posttext&#xA; public | threadsthreadidseq | sequence | posttext&#xA;(5 rows)&#xA;&#xA;posttext=  \d replies;&#xA;                                            Table &#34;public.replies&#34;&#xA;     Column     |           Type           | Collation | Nullable |                  Default&#xA;----------------+--------------------------+-----------+----------+-------------------------------------------&#xA; replyid       | integer                  |           | not null | nextval(&#39;repliesreplyidseq&#39;::regclass)&#xA; threadid      | integer                  |           |          |&#xA; replydate     | timestamp with time zone |           | not null | now()&#xA; replyauthor   | character varying(64)    |           |          |&#xA; replybody     | character varying(4096)  |           |          |&#xA; hiddenstatus  | boolean                  |           | not null |&#xA; flaggedstatus | boolean                  |           | not null |&#xA;Indexes:&#xA;    &#34;repliespkey&#34; PRIMARY KEY, btree (replyid)&#xA;Foreign-key constraints:&#xA;    &#34;repliesthreadidfkey&#34; FOREIGN KEY (threadid) REFERENCES threads(threadid)&#xA;&#xA;These long lines are ugly so here&#39;s a pastebin link. Essentially I need to s/reply/remark/g; including that sequence, index and foreign-key constraint:&#xA;&#xA;-- This file is migrations/5/up.sql btw&#xA;&#xA; ALTER TABLE replies&#xA;RENAME TO remarks;&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME replyid&#xA;    TO remarkid;&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME replydate&#xA;    TO remarkdate;&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME replyauthor&#xA;   TO remarkauthor;&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME replybody&#xA;    TO remarkbody;&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME CONSTRAINT repliesthreadidfkey&#xA;    TO remarksthreadidfkey;&#xA;&#xA; ALTER INDEX repliespkey&#xA;RENAME TO remarkspkey;&#xA;&#xA; ALTER SEQUENCE repliesreplyidseq&#xA;RENAME TO remarksremarkidseq;&#xA;&#xA;I&#39;m also going to do the exact opposite for our rolling-back pleasure:&#xA;&#xA;-- This one is migrations/5/down.sql&#xA;&#xA; ALTER TABLE remarks&#xA;RENAME TO replies;&#xA;&#xA; ALTER TABLE replies&#xA;RENAME remarkid&#xA;    TO replyid;&#xA;&#xA; ALTER TABLE replies&#xA;RENAME remarkdate&#xA;    TO replydate;&#xA;&#xA; ALTER TABLE replies&#xA;RENAME remarkauthor&#xA;    TO replyauthor;&#xA;&#xA; ALTER TABLE replies&#xA;RENAME remarkbody&#xA;    TO replybody;&#xA;&#xA; ALTER TABLE replies&#xA;RENAME CONSTRAINT remarksthreadidfkey&#xA;    TO repliesthreadidfkey;&#xA;&#xA; ALTER INDEX remarkspkey&#xA;RENAME TO repliespkey;&#xA;&#xA; ALTER SEQUENCE remarksremarkidseq&#xA;RENAME TO repliesreplyidseq;&#xA;&#xA;Idk why but I always find this step feels weird to me because it feels like I&#39;m undoing the undo lol. But we will need it later so let&#39;s absolutely include it. Now I&#39;m gunna see if it works...&#xA;&#xA;daniel@netburst:~/git/PostText$ ./PostText.pl eval &#39;app-  pg-  migrations-  fromdir(&#34;migrations&#34;)-  migrate(5);&#39;&#xA;daniel@netburst:~/git/PostText$ echo $?&#xA;0&#xA;&#xA;No news is good news I guess. Let&#39;s whip out psql again and see how it looks:&#xA;&#xA;posttext=  \d&#xA;                   List of relations&#xA; Schema |         Name          |   Type   |   Owner&#xA;--------+-----------------------+----------+-----------&#xA; public | mojomigrations       | table    | posttext&#xA; public | remarks               | table    | posttext&#xA; public | remarksremarkidseq | sequence | posttext&#xA; public | threads               | table    | posttext&#xA; public | threadsthreadidseq | sequence | posttext&#xA;(5 rows)&#xA;&#xA;posttext=  \d remarks;&#xA;                                            Table &#34;public.remarks&#34;&#xA;     Column     |           Type           | Collation | Nullable |                  Default&#xA;----------------+--------------------------+-----------+----------+--------------------------------------------&#xA; remarkid      | integer                  |           | not null | nextval(&#39;remarksremarkidseq&#39;::regclass)&#xA; threadid      | integer                  |           |          |&#xA; remarkdate    | timestamp with time zone |           | not null | now()&#xA; remarkauthor  | character varying(64)    |           |          |&#xA; remarkbody    | character varying(4096)  |           |          |&#xA; hiddenstatus  | boolean                  |           | not null |&#xA; flaggedstatus | boolean                  |           | not null |&#xA;Indexes:&#xA;    &#34;remarkspkey&#34; PRIMARY KEY, btree (remarkid)&#xA;Foreign-key constraints:&#xA;    &#34;remarksthreadidfkey&#34; FOREIGN KEY (threadid) REFERENCES threads(threadid)&#xA;&#xA;Aaaaaand the pastebin. I think we&#39;re in good shape. I&#39;m going to migrate back for now as I still need to make some changes in the controller logic to use the new Remark model instead of my old Reply model.&#xA;&#xA;daniel@netburst:~/git/PostText$ ./PostText.pl eval &#39;app-  pg-  migrations-  fromdir(&#34;migrations&#34;)-  migrate(4);&#39;&#xA;daniel@netburst:~/git/PostText$ echo $?&#xA;0&#xA;&#xA;I keep the migration hard-coded in the method call in the app itself just to save myself from accidentally migrating to the latest before it&#39;s ready:&#xA;&#xA;From PostText.pl&#xA;app-  pg-  migrations-  fromdir(&#39;migrations&#39;)-  migrate(4);&#xA;&#xA;I know there&#39;s some reason I started doing that... I accidentally something but now I can&#39;t remember. Gunna just stick with the cargo cult and leave it be.&#xA;&#xA;Next I gotta work on the aforementioned controller logic. And then moar tests.&#xA;&#xA;database&#xA;mojolicious&#xA;perl&#xA;sql&#xA;webdev&#xA;&#xA;Homepage&#xD;&#xA;Code&#xD;&#xA;Mail&#xD;&#xA;Social&#xD;&#xA;Chat&#xD;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>I actually normally wouldn&#39;t enjoy this but thankfully this <a href="https://metacpan.org/pod/Mojo::Pg">Mojo::Pg</a> wrapper makes it easy. I honestly don&#39;t have any experience with the original <a href="https://metacpan.org/pod/DBD::Pg">DBD::Pg</a> as I&#39;m still very new to the database world so I hope this doesn&#39;t read too much like I&#39;m lost in the sauce. First I took a look at my current tables using the <code>psql</code> tool:</p>

<pre><code>post_text=&gt; \d
                   List of relations
 Schema |         Name          |   Type   |   Owner
--------+-----------------------+----------+-----------
 public | mojo_migrations       | table    | post_text
 public | replies               | table    | post_text
 public | replies_reply_id_seq  | sequence | post_text
 public | threads               | table    | post_text
 public | threads_thread_id_seq | sequence | post_text
(5 rows)

post_text=&gt; \d replies;
                                            Table &#34;public.replies&#34;
     Column     |           Type           | Collation | Nullable |                  Default
----------------+--------------------------+-----------+----------+-------------------------------------------
 reply_id       | integer                  |           | not null | nextval(&#39;replies_reply_id_seq&#39;::regclass)
 thread_id      | integer                  |           |          |
 reply_date     | timestamp with time zone |           | not null | now()
 reply_author   | character varying(64)    |           |          |
 reply_body     | character varying(4096)  |           |          |
 hidden_status  | boolean                  |           | not null |
 flagged_status | boolean                  |           | not null |
Indexes:
    &#34;replies_pkey&#34; PRIMARY KEY, btree (reply_id)
Foreign-key constraints:
    &#34;replies_thread_id_fkey&#34; FOREIGN KEY (thread_id) REFERENCES threads(thread_id)
</code></pre>

<p>These long lines are ugly so here&#39;s a <a href="https://paste.lgts.xyz/?3218cb8891eb58e7#6UADDjAN4bngeJwwiZndmTQyPauFeTwcknHSTkDzbtqJ">pastebin link</a>. Essentially I need to <code>s/reply_/remark_/g;</code> including that sequence, index and foreign-key constraint:</p>

<pre><code>-- This file is migrations/5/up.sql btw

 ALTER TABLE replies
RENAME TO remarks;

 ALTER TABLE remarks
RENAME reply_id
    TO remark_id;

 ALTER TABLE remarks
RENAME reply_date
    TO remark_date;

 ALTER TABLE remarks
RENAME reply_author
   TO remark_author;

 ALTER TABLE remarks
RENAME reply_body
    TO remark_body;

 ALTER TABLE remarks
RENAME CONSTRAINT replies_thread_id_fkey
    TO remarks_thread_id_fkey;

 ALTER INDEX replies_pkey
RENAME TO remarks_pkey;

 ALTER SEQUENCE replies_reply_id_seq
RENAME TO remarks_remark_id_seq;
</code></pre>

<p>I&#39;m also going to do the exact opposite for our rolling-back pleasure:</p>

<pre><code>-- This one is migrations/5/down.sql

 ALTER TABLE remarks
RENAME TO replies;

 ALTER TABLE replies
RENAME remark_id
    TO reply_id;

 ALTER TABLE replies
RENAME remark_date
    TO reply_date;

 ALTER TABLE replies
RENAME remark_author
    TO reply_author;

 ALTER TABLE replies
RENAME remark_body
    TO reply_body;

 ALTER TABLE replies
RENAME CONSTRAINT remarks_thread_id_fkey
    TO replies_thread_id_fkey;

 ALTER INDEX remarks_pkey
RENAME TO replies_pkey;

 ALTER SEQUENCE remarks_remark_id_seq
RENAME TO replies_reply_id_seq;
</code></pre>

<p>Idk why but I always find this step feels weird to me because it feels like I&#39;m undoing the undo lol. But we will need it later so let&#39;s absolutely include it. Now I&#39;m gunna see if it works...</p>

<pre><code>daniel@netburst:~/git/PostText$ ./PostText.pl eval &#39;app-&gt;pg-&gt;migrations-&gt;from_dir(&#34;migrations&#34;)-&gt;migrate(5);&#39;
daniel@netburst:~/git/PostText$ echo $?
0
</code></pre>

<p>No news is good news I guess. Let&#39;s whip out <code>psql</code> again and see how it looks:</p>

<pre><code>post_text=&gt; \d
                   List of relations
 Schema |         Name          |   Type   |   Owner
--------+-----------------------+----------+-----------
 public | mojo_migrations       | table    | post_text
 public | remarks               | table    | post_text
 public | remarks_remark_id_seq | sequence | post_text
 public | threads               | table    | post_text
 public | threads_thread_id_seq | sequence | post_text
(5 rows)

post_text=&gt; \d remarks;
                                            Table &#34;public.remarks&#34;
     Column     |           Type           | Collation | Nullable |                  Default
----------------+--------------------------+-----------+----------+--------------------------------------------
 remark_id      | integer                  |           | not null | nextval(&#39;remarks_remark_id_seq&#39;::regclass)
 thread_id      | integer                  |           |          |
 remark_date    | timestamp with time zone |           | not null | now()
 remark_author  | character varying(64)    |           |          |
 remark_body    | character varying(4096)  |           |          |
 hidden_status  | boolean                  |           | not null |
 flagged_status | boolean                  |           | not null |
Indexes:
    &#34;remarks_pkey&#34; PRIMARY KEY, btree (remark_id)
Foreign-key constraints:
    &#34;remarks_thread_id_fkey&#34; FOREIGN KEY (thread_id) REFERENCES threads(thread_id)
</code></pre>

<p>Aaaaaand the <a href="https://paste.lgts.xyz/?45b1e2168a8bd9c2#6ii86AWYJKtVkTzyjHe2uHyxRntnmPx7knuo4HSo27jK">pastebin</a>. I think we&#39;re in good shape. I&#39;m going to migrate back for now as I still need to make some changes in the controller logic to use the new Remark model instead of my old Reply model.</p>

<pre><code>daniel@netburst:~/git/PostText$ ./PostText.pl eval &#39;app-&gt;pg-&gt;migrations-&gt;from_dir(&#34;migrations&#34;)-&gt;migrate(4);&#39;
daniel@netburst:~/git/PostText$ echo $?
0
</code></pre>

<p>I keep the migration hard-coded in the method call in the app itself just to save myself from accidentally migrating to the latest before it&#39;s ready:</p>

<pre><code># From PostText.pl
app-&gt;pg-&gt;migrations-&gt;from_dir(&#39;migrations&#39;)-&gt;migrate(4);
</code></pre>

<p>I know there&#39;s some reason I started doing that... I accidentally something but now I can&#39;t remember. Gunna just stick with the <a href="https://en.wikipedia.org/wiki/Cargo_cult_programming">cargo cult</a> and leave it be.</p>

<p>Next I gotta work on the aforementioned controller logic. And then moar tests.</p>

<p><a href="https://blog.swagg.net/tag:database" class="hashtag"><span>#</span><span class="p-category">database</span></a>
<a href="https://blog.swagg.net/tag:mojolicious" class="hashtag"><span>#</span><span class="p-category">mojolicious</span></a>
<a href="https://blog.swagg.net/tag:perl" class="hashtag"><span>#</span><span class="p-category">perl</span></a>
<a href="https://blog.swagg.net/tag:sql" class="hashtag"><span>#</span><span class="p-category">sql</span></a>
<a href="https://blog.swagg.net/tag:webdev" class="hashtag"><span>#</span><span class="p-category">webdev</span></a></p>
<ul><li><a href="https://www.swagg.net">Homepage</a></li>
<li><a href="https://codeberg.org/swaggboi">Code</a></li>
<li><a href="mailto:swaggboi@slackware.uk">Mail</a></li>
<li><a href="https://eattherich.club/@swaggboi">Social</a></li>
<li><a href="https://discord.gg/6MXVZKU">Chat</a></li></ul>
]]></content:encoded>
      <guid>https://blog.swagg.net/i-like-to-migrate-migrate</guid>
      <pubDate>Mon, 22 Aug 2022 16:51:40 +0000</pubDate>
    </item>
    <item>
      <title>More Things I Should Have Already Figured Out Years Ago</title>
      <link>https://blog.swagg.net/more-things-i-should-have-already-figured-out-years-ago?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[So I used to read data into an array of arrays like so:&#xA;&#xA;sub getthreads($self) {&#xA;    $self-  pg-  db-  query(&lt;~&#39;ENDSQL&#39;)-arrays()&#xA;        SELECT threadid,&#xA;               TOCHAR(threaddate, &#39;Dy Mon DD HH:MI:SS AM TZ YYYY&#39;),&#xA;               threadauthor,&#xA;               threadtitle,&#xA;               threadbody&#xA;          FROM threads&#xA;         WHERE NOT hiddenstatus&#xA;         ORDER BY threaddate DESC;&#xA;       ENDSQL&#xA;}&#xA;&#xA;Then I&#39;d plop that data into templates like so:&#xA;&#xA;% for my $thread (@$threads) { =%&#xA;  article class=&#34;thread&#34;&#xA;    h3 class=&#34;title&#34;%= @$thread[3] %/h3&#xA;    h4 class=&#34;date&#34;%= @$thread[1] %/h4&#xA;    h5 class=&#34;author&#34;%= @$thread[2] %/h5&#xA;    p class=&#34;body&#34;%= @$thread[0] %/p&#xA;  /article&#xA;% } =%&#xA;&#xA;This is already pretty cool but I kept losing track of what index number went to which data field. Then I saw that there is a hashes() method in Mojo::Pg::Results and thought... What if we used 100% of the brain? (Edit: Formatting turned out bad for this one)&#xA;&#xA;sub getthreads($self) {&#xA;    $self-  pg-  db-  query(&lt;~&#39;ENDSQL&#39;)-hashes()&#xA;        SELECT threadid                                             AS id,&#xA;               TOCHAR(threaddate, &#39;Dy Mon DD HH:MI:SS AM TZ YYYY&#39;) AS date,&#xA;               threadauthor                                         AS author,&#xA;               threadtitle                                          AS title,&#xA;               threadbody                                           AS body&#xA;          FROM threads&#xA;         WHERE NOT hiddenstatus&#xA;         ORDER BY threaddate DESC;&#xA;       ENDSQL&#xA;}&#xA;&#xA;By using SQL to assign an alias to the column names my templates now look much cleaner:&#xA;&#xA;% for my $thread (@$threads) { =%&#xA;  article class=&#34;thread&#34;&#xA;    h3 class=&#34;title&#34;%= %$thread{&#39;title&#39;} %/h3&#xA;    h4 class=&#34;date&#34;%= %$thread{&#39;date&#39;} %/h4&#xA;    h5 class=&#34;author&#34;%= %$thread{&#39;author&#39;} %/h5&#xA;    p class=&#34;body&#34;%= %$thread{&#39;body&#39;} %/p&#xA;  /article&#xA;% } =%&#xA;&#xA;Readability becomes more and more important as my memory gets worse and worse... I need to learn POD so if I&#39;m a boi of my word there may be a blog post on that in the future. Knowing me... Years in the future.&#xA;&#xA;perl&#xA;mojolicious&#xA;sql&#xA;&#xA;Homepage&#xD;&#xA;Code&#xD;&#xA;Mail&#xD;&#xA;Social&#xD;&#xA;Chat&#xD;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>So I used to read data into an array of arrays like so:</p>

<pre><code>sub get_threads($self) {
    $self-&gt;pg-&gt;db-&gt;query(&lt;&lt;~&#39;END_SQL&#39;)-&gt;arrays()
        SELECT thread_id,
               TO_CHAR(thread_date, &#39;Dy Mon DD HH:MI:SS AM TZ YYYY&#39;),
               thread_author,
               thread_title,
               thread_body
          FROM threads
         WHERE NOT hidden_status
         ORDER BY thread_date DESC;
       END_SQL
}
</code></pre>

<p>Then I&#39;d plop that data into <a href="https://docs.mojolicious.org/Mojolicious/Guides/Rendering#Embedded-Perl">templates</a> like so:</p>

<pre><code>&lt;% for my $thread (@$threads) { =%&gt;
  &lt;article class=&#34;thread&#34;&gt;
    &lt;h3 class=&#34;title&#34;&gt;&lt;%= @$thread[3] %&gt;&lt;/h3&gt;
    &lt;h4 class=&#34;date&#34;&gt;&lt;%= @$thread[1] %&gt;&lt;/h4&gt;
    &lt;h5 class=&#34;author&#34;&gt;&lt;%= @$thread[2] %&gt;&lt;/h5&gt;
    &lt;p class=&#34;body&#34;&gt;&lt;%= @$thread[0] %&gt;&lt;/p&gt;
  &lt;/article&gt;
&lt;% } =%&gt;
</code></pre>

<p>This is already pretty cool but I kept losing track of what index number went to which data field. Then I saw that there is a <code>hashes()</code> method in <a href="https://metacpan.org/pod/Mojo::Pg::Results">Mojo::Pg::Results</a> and thought... What if we used <em>100%</em> of the brain? (Edit: Formatting turned out bad for <a href="https://pastebin.com/yTzau4T2">this one</a>)</p>

<pre><code>sub get_threads($self) {
    $self-&gt;pg-&gt;db-&gt;query(&lt;&lt;~&#39;END_SQL&#39;)-&gt;hashes()
        SELECT thread_id                                             AS id,
               TO_CHAR(thread_date, &#39;Dy Mon DD HH:MI:SS AM TZ YYYY&#39;) AS date,
               thread_author                                         AS author,
               thread_title                                          AS title,
               thread_body                                           AS body
          FROM threads
         WHERE NOT hidden_status
         ORDER BY thread_date DESC;
       END_SQL
}
</code></pre>

<p>By using <a href="https://www.sqlstyle.guide">SQL</a> to assign an alias to the column names my templates now look much cleaner:</p>

<pre><code>&lt;% for my $thread (@$threads) { =%&gt;
  &lt;article class=&#34;thread&#34;&gt;
    &lt;h3 class=&#34;title&#34;&gt;&lt;%= %$thread{&#39;title&#39;} %&gt;&lt;/h3&gt;
    &lt;h4 class=&#34;date&#34;&gt;&lt;%= %$thread{&#39;date&#39;} %&gt;&lt;/h4&gt;
    &lt;h5 class=&#34;author&#34;&gt;&lt;%= %$thread{&#39;author&#39;} %&gt;&lt;/h5&gt;
    &lt;p class=&#34;body&#34;&gt;&lt;%= %$thread{&#39;body&#39;} %&gt;&lt;/p&gt;
  &lt;/article&gt;
&lt;% } =%&gt;
</code></pre>

<p>Readability becomes more and more important as my memory gets worse and worse... I need to learn <a href="https://perldoc.perl.org/perlpod">POD</a> so if I&#39;m a boi of my word there may be a blog post on that in the future. Knowing me... Years in the <a href="https://en.wikipedia.org/wiki/Year_2038_problem">future</a>.</p>

<p><a href="https://blog.swagg.net/tag:perl" class="hashtag"><span>#</span><span class="p-category">perl</span></a>
<a href="https://blog.swagg.net/tag:mojolicious" class="hashtag"><span>#</span><span class="p-category">mojolicious</span></a>
<a href="https://blog.swagg.net/tag:sql" class="hashtag"><span>#</span><span class="p-category">sql</span></a></p>
<ul><li><a href="https://www.swagg.net">Homepage</a></li>
<li><a href="https://codeberg.org/swaggboi">Code</a></li>
<li><a href="mailto:swaggboi@slackware.uk">Mail</a></li>
<li><a href="https://eattherich.club/@swaggboi">Social</a></li>
<li><a href="https://discord.gg/6MXVZKU">Chat</a></li></ul>
]]></content:encoded>
      <guid>https://blog.swagg.net/more-things-i-should-have-already-figured-out-years-ago</guid>
      <pubDate>Fri, 05 Aug 2022 04:20:52 +0000</pubDate>
    </item>
  </channel>
</rss>