<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>database &amp;mdash; Swagg::Blogg</title>
    <link>https://blog.swagg.net/tag:database</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>Tue, 28 Apr 2026 13:59:50 +0000</pubDate>
    <image>
      <url>https://i.snap.as/gopN8vBC.png</url>
      <title>database &amp;mdash; Swagg::Blogg</title>
      <link>https://blog.swagg.net/tag:database</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>I Accidentally Reply Model</title>
      <link>https://blog.swagg.net/i-accidentally-reply-model?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[What should I do...&#xA;&#xA;Is this dangerous?&#xA;&#xA;So I ran into a funny little snafu last night. I finally implemented my Reply model for my little textboard project but once I did this I noticed the app no longer shows in the snazzy built-in error pages. That&#39;s bummer because I find them real helpful when running apps in development mode. I kinda gave up on this last night and looked at it again this morning and noticed the following in the console output (requesting a route that does not exist, expect a 404):&#xA;&#xA;[2022-08-20 13:08:43.53717] [27345] [trace] [CgcDACZgwvD4] GET &#34;/swagg&#34;&#xA;Mojo::Reactor::Poll: I/O watcher failed: Can&#39;t locate object method &#34;exception&#34; via package &#34;PostText::Model::Reply&#34; at /home/daniel/perl5/lib/perl5/Mojolicious.pm line 200.&#xA;&#xA;It must be trying to dereference exception from an object named reply... Let&#39;s see...&#xA;&#xA;[daniel@netburst mojo]$ grep -i -r &#39;\-  reply&#39; * | grep -i &#39;lite&#39;&#xA;t/mojolicious/exceptionliteapp.t:  $c-  reply-  exception(undef);&#xA;t/mojolicious/exceptionliteapp.t:  $c-  reply-  exception;&#xA;t/mojolicious/exceptionliteapp.t:  $c-  reply-  exception(Mojo::Exception-  new);&#xA;t/mojolicious/liteapp.t:  $c-  rendermaybe(&#39;thisdoesnoteverexist&#39;) or $c-  reply-  static($file);&#xA;t/mojolicious/liteapp.t:get &#39;/static&#39; =  sub { shift-  reply-  static(&#39;hello.txt&#39;) };&#xA;t/mojolicious/longpollingliteapp.t:  Mojo::IOLoop-  timer(0.25 =  sub { $c-  reply-  static(&#39;hello.txt&#39;) });&#xA;t/mojolicious/staticliteapp.t:get &#39;/hello3.txt&#39; =  sub { shift-  reply-  static(&#39;hello2.txt&#39;) };&#xA;t/mojolicious/staticliteapp.t:  $c-  reply-  static(&#39;hello2.txt&#39;);&#xA;t/mojolicious/staticliteapp.t:  $c-  reply-  asset($mem);&#xA;t/mojolicious/staticliteapp.t:  $c-  reply-  file(curfile-  sibling(&#39;templates2&#39;, &#39;42.html.ep&#39;));&#xA;&#xA;Gah! I think I can work around this I suppose by just renaming the reply helper to something other than reply but for consistency I&#39;m going to go ahead and migrate everything to use the name Remark instead of Reply. &#39;Comment&#39; would remind me too much of social media so I guess these threads are gettin remarked upon.&#xA;&#xA;This, of course, means another night of databases. 😩&#xA;&#xA;perl&#xA;mojolicious&#xA;webdev&#xA;database&#xA;&#xA;Homepage&#xD;&#xA;Code&#xD;&#xA;Mail&#xD;&#xA;Social&#xD;&#xA;Chat&#xD;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<h2 id="what-should-i-do" id="what-should-i-do">What should I do...</h2>

<h3 id="is-this-dangerous" id="is-this-dangerous">Is this dangerous?</h3>

<p>So I ran into a funny little snafu last night. I finally implemented my <a href="https://github.com/swaggboi/PostText/commit/3bec9a71f1c44f7970e4031e2288bc1201c28b89">Reply model</a> for my little textboard project but once I did this I noticed the app no longer shows in the snazzy built-in <a href="https://docs.mojolicious.org/Mojolicious/Guides/Tutorial#Built-in-exception-and-not_found-pages">error pages</a>. That&#39;s bummer because I find them real helpful when running apps in development mode. I kinda gave up on this last night and looked at it again this morning and noticed the following in the console output (requesting a route that does not exist, expect a 404):</p>

<pre><code>[2022-08-20 13:08:43.53717] [27345] [trace] [CgcDACZgwvD4] GET &#34;/swagg&#34;
Mojo::Reactor::Poll: I/O watcher failed: Can&#39;t locate object method &#34;exception&#34; via package &#34;PostText::Model::Reply&#34; at /home/daniel/perl5/lib/perl5/Mojolicious.pm line 200.
</code></pre>

<p>It must be trying to dereference <code>exception</code> from an object named <code>reply</code>... Let&#39;s see...</p>

<pre><code>[daniel@netburst mojo]$ grep -i -r &#39;\-&gt;reply&#39; * | grep -i &#39;lite&#39;
t/mojolicious/exception_lite_app.t:  $c-&gt;reply-&gt;exception(undef);
t/mojolicious/exception_lite_app.t:  $c-&gt;reply-&gt;exception;
t/mojolicious/exception_lite_app.t:  $c-&gt;reply-&gt;exception(Mojo::Exception-&gt;new);
t/mojolicious/lite_app.t:  $c-&gt;render_maybe(&#39;this_does_not_ever_exist&#39;) or $c-&gt;reply-&gt;static($file);
t/mojolicious/lite_app.t:get &#39;/static&#39; =&gt; sub { shift-&gt;reply-&gt;static(&#39;hello.txt&#39;) };
t/mojolicious/longpolling_lite_app.t:  Mojo::IOLoop-&gt;timer(0.25 =&gt; sub { $c-&gt;reply-&gt;static(&#39;hello.txt&#39;) });
t/mojolicious/static_lite_app.t:get &#39;/hello3.txt&#39; =&gt; sub { shift-&gt;reply-&gt;static(&#39;hello2.txt&#39;) };
t/mojolicious/static_lite_app.t:  $c-&gt;reply-&gt;static(&#39;hello2.txt&#39;);
t/mojolicious/static_lite_app.t:  $c-&gt;reply-&gt;asset($mem);
t/mojolicious/static_lite_app.t:  $c-&gt;reply-&gt;file(curfile-&gt;sibling(&#39;templates2&#39;, &#39;42.html.ep&#39;));
</code></pre>

<p>Gah! I think I can work around this I suppose by just renaming the <code>reply</code> <a href="https://docs.mojolicious.org/Mojolicious/Guides/Tutorial#Helpers">helper</a> to something other than <code>reply</code> but for consistency I&#39;m going to go ahead and <a href="https://docs.mojolicious.org/Mojo/Pg/Migrations">migrate</a> everything to use the name Remark instead of Reply. &#39;Comment&#39; would remind me too much of social media so I guess these threads are gettin remarked upon.</p>

<p>This, of course, means another night of databases. 😩</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:webdev" class="hashtag"><span>#</span><span class="p-category">webdev</span></a>
<a href="https://blog.swagg.net/tag:database" class="hashtag"><span>#</span><span class="p-category">database</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-accidentally-reply-model</guid>
      <pubDate>Sat, 20 Aug 2022 23:08:13 +0000</pubDate>
    </item>
  </channel>
</rss>