<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Nduli's World - Engineering</title><link href="https://blog.jnduli.co.ke/" rel="alternate"/><link href="https://blog.jnduli.co.ke/feeds/engineering.atom.xml" rel="self"/><id>https://blog.jnduli.co.ke/</id><updated>2021-07-31T00:00:00+03:00</updated><entry><title>Static Website with Pandoc</title><link href="https://blog.jnduli.co.ke/static_website_with_pandoc.html" rel="alternate"/><published>2021-07-31T00:00:00+03:00</published><updated>2021-07-31T00:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2021-07-31:/static_website_with_pandoc.html</id><summary type="html">&lt;p&gt;I needed to pull off a quick website that would have various mathematics
questions, and thought to try and use pandoc. Here are my learnings:&lt;/p&gt;
&lt;p&gt;I first installed &lt;cite&gt;pandoc-bin&lt;/cite&gt; because I didn't to deal with a lot of
haskell dependencies.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;yay&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;pandoc-bin
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first iteration I made generated an …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I needed to pull off a quick website that would have various mathematics
questions, and thought to try and use pandoc. Here are my learnings:&lt;/p&gt;
&lt;p&gt;I first installed &lt;cite&gt;pandoc-bin&lt;/cite&gt; because I didn't to deal with a lot of
haskell dependencies.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;yay&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;pandoc-bin
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first iteration I made generated an &lt;cite&gt;index.html&lt;/cite&gt; file with
all the contents and a table of contents. The following bashscript shows
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;output
pandoc&lt;span class="w"&gt; &lt;/span&gt;--toc&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;markdown&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;html&lt;span class="w"&gt; &lt;/span&gt;--mathjax&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;./output/index.html&lt;span class="w"&gt; &lt;/span&gt;*.md
rsync&lt;span class="w"&gt; &lt;/span&gt;-rav&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ssh -p PORT&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;output/&lt;span class="w"&gt; &lt;/span&gt;username@domain:/destination_folder
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A sample file would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gh"&gt;# Title that will be in table of contents&lt;/span&gt;

Content body
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The order in the table of contents is on the alphabetical ordering of
the files. In my case, the date prefixed all files, so they ended up in
the order I wanted them to.&lt;/p&gt;
&lt;p&gt;The file however became too large after some time. I didn't want to get
a static site manager for this, so I tweaked the bashscript.&lt;/p&gt;
&lt;p&gt;I can compile each file to a separate html file with pandoc, but I first
need to get a list of all files I want.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;find&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-maxdepth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-type&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="w"&gt; &lt;/span&gt;-name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;*.md&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-printf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%f\n&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;xargs&lt;span class="w"&gt; &lt;/span&gt;-I&lt;span class="w"&gt; &lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;pandoc&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;markdown&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;html&lt;span class="w"&gt; &lt;/span&gt;--mathjax&lt;span class="w"&gt; &lt;/span&gt;--metadata&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;questions&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;%.html&lt;span class="w"&gt;  &lt;/span&gt;%
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I need to ensure compile the files to a common directory so that its
easy to sync with my server. An index file that links up to all the
children files should also be generated dynamically, so that I can
easily find the links. I created a custom function &lt;cite&gt;gen_html&lt;/cite&gt; that does
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gen_html&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;test_compile
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;file_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;%.*&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;](./&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.html)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;index.md
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;index.md
&lt;span class="w"&gt;    &lt;/span&gt;pandoc&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;markdown&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;html&lt;span class="w"&gt; &lt;/span&gt;--mathjax&lt;span class="w"&gt; &lt;/span&gt;--metadata&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;questions&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;./test_compile/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.html&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$file_name&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;xargs runs in a different process, so the function &lt;cite&gt;gen_html&lt;/cite&gt; will not
be available for that. To fix this, we first export it and then call it
with the xargs context. The &lt;cite&gt;_&lt;/cite&gt; is a place holder for &lt;cite&gt;argv[0]&lt;/cite&gt; and &lt;cite&gt;%&lt;/cite&gt;
is the parameter defined by &lt;cite&gt;-I&lt;/cite&gt;. See &lt;a class="reference external" href="https://stackoverflow.com/a/11003457"&gt;this link&lt;/a&gt;
for more information.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;gen_html
find&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;-maxdepth&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-type&lt;span class="w"&gt; &lt;/span&gt;f&lt;span class="w"&gt; &lt;/span&gt;-name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;*.md&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-printf&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%f\n&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sort&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;xargs&lt;span class="w"&gt; &lt;/span&gt;-I&lt;span class="w"&gt; &lt;/span&gt;%&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;gen_html &amp;quot;$@&amp;quot;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;%
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After which I generate the index with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pandoc&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;markdown&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;html&lt;span class="w"&gt; &lt;/span&gt;--mathjax&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;./test_compile/index.html&lt;span class="w"&gt; &lt;/span&gt;index.md
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can find the full code in &lt;a class="reference external" href="https://gist.github.com/jnduli/0d07305c79c542282d105c48e31d004c"&gt;this gihub gist&lt;/a&gt;.&lt;/p&gt;
</content><category term="Engineering"/></entry><entry><title>Comment System Using Recursive Queries</title><link href="https://blog.jnduli.co.ke/comment-system-using-recursive-queries.html" rel="alternate"/><published>2021-06-26T00:00:00+03:00</published><updated>2021-06-26T00:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2021-06-26:/comment-system-using-recursive-queries.html</id><summary type="html">&lt;p&gt;I was reading SQL antipatterns and got into the chapter about recursive
queries and how there are different ways to approach this. I wanted to
experiment with this and found that postgresql supports recursive
queries. I decided to make a simple commenting database and see how it
would work.&lt;/p&gt;
&lt;div class="section" id="fibonnacci-with-ctes"&gt;
&lt;h2&gt;Fibonnacci …&lt;/h2&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;I was reading SQL antipatterns and got into the chapter about recursive
queries and how there are different ways to approach this. I wanted to
experiment with this and found that postgresql supports recursive
queries. I decided to make a simple commenting database and see how it
would work.&lt;/p&gt;
&lt;div class="section" id="fibonnacci-with-ctes"&gt;
&lt;h2&gt;Fibonnacci with CTEs&lt;/h2&gt;
&lt;p&gt;Adopted from &lt;a class="reference external" href="https://wiki.postgresql.org/wiki/Fibonacci_Numbers"&gt;https://wiki.postgresql.org/wiki/Fibonacci_Numbers&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- non recursive part --&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- recursive part --&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;limit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here's how this would work:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;There are three tables, intermediate, working and result table. The
non-recursive part is first calculated, setting the working and
result table to values (0, 1)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The recursive part of the query &lt;cite&gt;select b, a + b from fib&lt;/cite&gt; is then
run on. &lt;cite&gt;fib&lt;/cite&gt; here is the working table, which contains (0, 1).  This
sets the intermediate table to (1, 1). This gets appended to the result
table, and replaces the working table.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;In the next run, the working table is (1, 1), resulting in (1, 2) in
the intermediate table, which gets appended to the result table, and
replaces the working table. This goes on and on.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The recursion should stop when the intermediate table is empty but
since fibonacci is infinite this does not happen. Adding a where
clause to the recursive part would cause this to occur. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- non recursive part --&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;values&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;-- recursive part --&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;

&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The last select statement: &lt;cite&gt;SELECT a from fib limit 20;&lt;/cite&gt; picks the
information from the result table. Limiting this too can cause the
infinite recursion to stop.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Detailed explanation of how this works can be found here:
&lt;a class="reference external" href="https://wiki.postgresql.org/wiki/CTEReadme"&gt;CTE Read me&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="comment-system"&gt;
&lt;h2&gt;Comment System&lt;/h2&gt;
&lt;p&gt;Here's the table structure I used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;SERIAL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PRIMARY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;date_created&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;parent 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;parent 2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-02&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;another child of 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-03&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-03&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-03&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 6&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-04&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 7&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-05&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-06&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;child of 5&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2021-01-05&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A simple recursive query getting all children for comment_id 1 is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A good use case is when you have partitioned tables and want to see all
descendant of a particular parent.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;DROP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;topic_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HASH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HASH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topic_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grand_child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grand_child_2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modulus&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_partition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inhparent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inhrelid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_inherits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inhparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;parent&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;
&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inhparent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inhrelid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pg_inherits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_partition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pg_cat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inhparent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inhrelid&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inhparent&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inhrelid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_partition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which results in:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="n"&gt;child&lt;/span&gt;
&lt;span class="c1"&gt;---------+---------------&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_2&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grand_child_1&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;grand_child_2&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="cycle-prevention"&gt;
&lt;h2&gt;Cycle Prevention&lt;/h2&gt;
&lt;p&gt;To introduce a cycle in this query we just have to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To prevent this while running our query, we have to keep a state of all
the parents we've visited and filter these out in the recursive bit. In
this case, we maintain an array of visited parents and ignore all
children comments that have that id.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;visited_parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visited_parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;visited_parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visited_parents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;child_comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But how do we prevent cycle creation in the query itself? One method is
to have a trigger that gets all parents of a child comment, and doesn't
update if the update would cause a cycle.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FUNCTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cycle_prevention&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RETURNS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;trigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;cycle_prevention$&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;parents_not_allowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;-- Check that parent id doesn&amp;#39;t cause a cycle&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;parent id: %, comment_id %&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;WITH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;RECURSIVE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;UNION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ALL&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INNER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LIMIT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parents_not_allowed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="n"&gt;raise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notice&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Value: %&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parents_not_allowed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;comment_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parents_not_allowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;THEN&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;RAISE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXCEPTION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cycle found in query&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;RETURN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;cycle_prevention$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;LANGUAGE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TRIGGER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cycle_prevention&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BEFORE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;FOR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EACH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXECUTE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;PROCEDURE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cycle_prevention&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the update fails with &lt;cite&gt;ERROR:  cycle found in query&lt;/cite&gt;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Engineering"/></entry><entry><title>Copying Pass Secrets to Another Machine</title><link href="https://blog.jnduli.co.ke/copying-pass-secrets-to-another-machine.html" rel="alternate"/><published>2021-03-27T00:00:00+03:00</published><updated>2021-03-27T00:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2021-03-27:/copying-pass-secrets-to-another-machine.html</id><summary type="html">&lt;p&gt;I had a new laptop but couldn't access my previous one, so I had to
figure out how to get my &lt;a class="reference external" href="https://www.passwordstore.org/"&gt;pass&lt;/a&gt;
secrets. I had these problems:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;How would I get my old password store?&lt;/li&gt;
&lt;li&gt;How would I be able get my old gpg keys into this new laptop?&lt;/li&gt;
&lt;li&gt;I …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;I had a new laptop but couldn't access my previous one, so I had to
figure out how to get my &lt;a class="reference external" href="https://www.passwordstore.org/"&gt;pass&lt;/a&gt;
secrets. I had these problems:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;How would I get my old password store?&lt;/li&gt;
&lt;li&gt;How would I be able get my old gpg keys into this new laptop?&lt;/li&gt;
&lt;li&gt;I had started storing new passwords using a new gpg key, so how would
this work the old password store (generated with a different key)?&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="getting-the-old-secrets"&gt;
&lt;h2&gt;Getting the old secrets&lt;/h2&gt;
&lt;p&gt;My home directory was compressed using &lt;a class="reference external" href="https://borgbackup.readthedocs.io/en/stable/"&gt;borg&lt;/a&gt; and sent to me using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;borg&lt;span class="w"&gt; &lt;/span&gt;init&lt;span class="w"&gt; &lt;/span&gt;/path/to/folder
borg&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;--progess&lt;span class="w"&gt; &lt;/span&gt;/path/to/backup::susa&lt;span class="w"&gt; &lt;/span&gt;/home/username
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And on my laptop I ran:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;borg&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;backup::susa-initial&lt;span class="w"&gt; &lt;/span&gt;/home/username/borg_mount
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This folder is mounted as read only, limiting what I could do in it.
Here are some of the errors I got because of this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--homedir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--list-keys
gpg:&lt;span class="w"&gt; &lt;/span&gt;failed&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;temporary&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home/username/borg_mount/home/username/.password-store/.#lk0x000055b881984170.archlinux.39288&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Read-only&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;system
gpg:&lt;span class="w"&gt; &lt;/span&gt;keyblock&lt;span class="w"&gt; &lt;/span&gt;resource&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home/username/borg_mount/home/username/.password-store/pubring.kbx&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Read-only&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;system
gpg:&lt;span class="w"&gt; &lt;/span&gt;failed&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;temporary&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home/username/borg_mount/home/username/.password-store/.#lk0x000055b881981700.archlinux.39288&amp;#39;&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;Read-only&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;system
gpg:&lt;span class="w"&gt; &lt;/span&gt;Fatal:&lt;span class="w"&gt; &lt;/span&gt;can&lt;span class="s1"&gt;&amp;#39;t create lock for &amp;#39;&lt;/span&gt;/home/username/borg_mount/home/username/.password-store/trustdb.gpg&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I copied over the gpg key into a folder outside the mounted borg folder:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;-r&lt;span class="w"&gt; &lt;/span&gt;borg_mount/home/username/.gnupg/*&lt;span class="w"&gt; &lt;/span&gt;gpg_tests/
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gpg_tests
gpg&lt;span class="w"&gt; &lt;/span&gt;--homedir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--list-keys&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# this now works&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And exported my keys with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--homedir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--output&lt;span class="w"&gt; &lt;/span&gt;personal_gpg.gpg&lt;span class="w"&gt; &lt;/span&gt;--armor&lt;span class="w"&gt; &lt;/span&gt;--export&lt;span class="w"&gt; &lt;/span&gt;ID_FOR_KEY
gpg&lt;span class="w"&gt; &lt;/span&gt;--homedir&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--output&lt;span class="w"&gt; &lt;/span&gt;personal_sec.gpg&lt;span class="w"&gt; &lt;/span&gt;--armor&lt;span class="w"&gt; &lt;/span&gt;--export-secret-keys&lt;span class="w"&gt; &lt;/span&gt;ID_FOR_KEY
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I imported them them with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;gpg&lt;span class="w"&gt; &lt;/span&gt;--import&lt;span class="w"&gt; &lt;/span&gt;personal_gpg.gpg
gpg&lt;span class="w"&gt; &lt;/span&gt;--allow-secret-key-import&lt;span class="w"&gt; &lt;/span&gt;--import&lt;span class="w"&gt; &lt;/span&gt;personal_sec.gpg
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="working-with-both-password-stores"&gt;
&lt;h2&gt;Working with both password stores&lt;/h2&gt;
&lt;p&gt;Pass stores the &lt;cite&gt;gpg_id&lt;/cite&gt; in its root folder, so I just copied over the
previous pass store into a subdirectory in my new password store.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;home/username/.password_store&lt;span class="w"&gt; &lt;/span&gt;~/.password_store/oldpasswords
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now running pass shows all my passwords, both on from my old laptop and
new laptop. It's a little inconvenient because I now have an extra node
to go through to access my old passwords but this is an acceptable
compromise.&lt;/p&gt;
&lt;p&gt;Another unexplored alternative is to convert all the old passwords to
use the new gpg key. It should be a simple script that just gets the
password from the old gpg key and saves it into the new store.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Engineering"/></entry><entry><title>Setting up Hspec on a Haskell Project</title><link href="https://blog.jnduli.co.ke/setting-up-hspec-on-haskell-project.html" rel="alternate"/><published>2021-01-23T00:00:00+03:00</published><updated>2021-01-23T00:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2021-01-23:/setting-up-hspec-on-haskell-project.html</id><summary type="html">&lt;p&gt;I'd started the &lt;a class="reference external" href="https://github.com/jnduli/kindle_highlights"&gt;Kindle-Highlights project&lt;/a&gt; without tests. This was
a problem because I couldn't add features as fast as I wanted because
I'd break something without knowing. I needed to add tests to speed up
my development. For haskell, &lt;a class="reference external" href="https://hspec.github.io/"&gt;hspec&lt;/a&gt;  is a
tool/library for this. Setting this up on …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I'd started the &lt;a class="reference external" href="https://github.com/jnduli/kindle_highlights"&gt;Kindle-Highlights project&lt;/a&gt; without tests. This was
a problem because I couldn't add features as fast as I wanted because
I'd break something without knowing. I needed to add tests to speed up
my development. For haskell, &lt;a class="reference external" href="https://hspec.github.io/"&gt;hspec&lt;/a&gt;  is a
tool/library for this. Setting this up on the project was surprisingly
challenging, mostly because I didn't properly read the docs.&lt;/p&gt;
&lt;p&gt;Some of the things I missed out on or had to do are:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;There is auto detection of test files with &lt;a class="reference external" href="https://hspec.github.io/hspec-discover.html"&gt;hspec-discover&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Test files should have the suffix Spec (I'd added them with a Test
Suffix) to be auto discovered by hspec-discover.&lt;/li&gt;
&lt;li&gt;Each test file has to export a &lt;cite&gt;spec&lt;/cite&gt; of type Spec&lt;/li&gt;
&lt;li&gt;I needed to set up some of my modules properly in the cabal file.&lt;/li&gt;
&lt;li&gt;I had to move executable to a different directory to prevent warning
on missing dependencies, see &lt;a class="reference external" href="https://github.com/commercialhaskell/stack/issues/3109"&gt;github stack issue `&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I switched to cabal version 2.2 to use the shared properties field.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the steps I took to having a properly working and tested code
base:&lt;/p&gt;
&lt;div class="section" id="cabal-setup"&gt;
&lt;h2&gt;Cabal Setup&lt;/h2&gt;
&lt;p&gt;I modified my cabal config by:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Using cabal version 2.2: this is so that I could use common stanzas
preventing duplication of dependencies, see &lt;a class="reference external" href="https://cabal.readthedocs.io/en/latest/developing-packages.html#common-stanzas"&gt;cabal common-stanzas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Adding a library section that exposed the modules that I wanted to
test.&lt;/li&gt;
&lt;li&gt;Adding a test section that contained hspec options.&lt;/li&gt;
&lt;li&gt;Added an executable section and moved the executable code &lt;cite&gt;Main.hs&lt;/cite&gt; to
another folder to prevent warning errors on missing dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;cabal-version:       2.2
name:                kindle-highlights
.
.

common shared-properties
  default-language:    Haskell2010
  build-depends:       base &amp;gt;= 4.7 &amp;amp;&amp;amp; &amp;lt; 5,
                       parsec,
                       text,
                       containers,
                       time

library
  import:              shared-properties
  exposed-modules:     KindleHighlights,
                       CommandOptions
  hs-source-dirs:      src
  build-depends:       optparse-applicative

executable kindle-highlights
  import:              shared-properties
  hs-source-dirs:      app
  main-is:             Main.hs
  build-depends:       optparse-applicative,
                       kindle-highlights,

test-suite spec
  import:              shared-properties
  type:                exitcode-stdio-1.0
  hs-source-dirs:      test
  main-is:             Spec.hs
  other-modules:       ParserSpec
  build-depends:       kindle-highlights,
                       hspec &amp;gt;= 2.7,
                       hspec-discover &amp;gt;= 2.7
  ghc-options: -Wall
  build-tool-depends: hspec-discover:hspec-discover == 2.*
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Key things here are that the test-suite spec points to the 'test'
directory, with &lt;cite&gt;hspec&lt;/cite&gt; and &lt;cite&gt;hspec-discover&lt;/cite&gt; added as build-depends.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="writing-the-tests"&gt;
&lt;h2&gt;Writing the Tests&lt;/h2&gt;
&lt;p&gt;To enable automatic test discovery, the &lt;cite&gt;Spec.hs&lt;/cite&gt; file has:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="cm"&gt;{-# OPTIONS_GHC -F -pgmF hspec-discover #-}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which means it will automatically pick up tests that I write. The test
files has to have the &lt;cite&gt;Spec&lt;/cite&gt; suffix for this to work.&lt;/p&gt;
&lt;p&gt;To write a test file, for example, &lt;cite&gt;ParserSpec.hs&lt;/cite&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;ParserSpec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;where&lt;/span&gt;

&lt;span class="nf"&gt;spec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;Spec&lt;/span&gt;
&lt;span class="nf"&gt;spec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;describe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;KindleHighlights&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;do&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;has a string definition for end of group&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;eogString&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="n"&gt;shouldBe&lt;/span&gt;&lt;span class="p"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;==========&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;has to export the spec function, which will be called by hspec.&lt;/p&gt;
&lt;p&gt;Running the tests with &lt;cite&gt;stack test&lt;/cite&gt; should show:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;kindle-highlights&amp;gt; test (suite: spec)

Progress 1/2: kindle-highlights
Parser
  KindleHighlights
    has a string definition for end of group
    highlights
    highlights 2
    groups

Finished in 0.0004 seconds
4 examples, 0 failures

kindle-highlights&amp;gt; Test suite spec passed
Completed 2 action(s).
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which shows the tests ran successfully.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Engineering"/><category term="projects"/></entry><entry><title>Control: Controllability and Observability</title><link href="https://blog.jnduli.co.ke/control-controllability-and-observability.html" rel="alternate"/><published>2018-02-21T15:00:00+03:00</published><updated>2018-02-21T15:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2018-02-21:/control-controllability-and-observability.html</id><summary type="html">&lt;p&gt;The state space equations are given by:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x'(t) = Ax(t) + Bu(t)\\
y(t) = Cx(t) + Du(t)
\end{align*}
&lt;/div&gt;
&lt;p&gt;If we discretize the system, the equation can be written as:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x(n+1) = A_dx(n) + B_du(n)\\
y(n) = C_dx(n) + D_du(n)
\end …&lt;/div&gt;</summary><content type="html">&lt;p&gt;The state space equations are given by:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x'(t) = Ax(t) + Bu(t)\\
y(t) = Cx(t) + Du(t)
\end{align*}
&lt;/div&gt;
&lt;p&gt;If we discretize the system, the equation can be written as:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x(n+1) = A_dx(n) + B_du(n)\\
y(n) = C_dx(n) + D_du(n)
\end{align*}
&lt;/div&gt;
&lt;p&gt;The discretization steps can be found &lt;a class="reference external" href="https://en.wikibooks.org/wiki/Control_Systems/State-Space_Equations#Discretization"&gt;here:discretization&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="controllability"&gt;
&lt;h2&gt;Controllability&lt;/h2&gt;
&lt;p&gt;A system is said to be controllable if the state can be changed by
changing the inputs.&lt;/p&gt;
&lt;p&gt;Using the discretized equation, we can come up with the following:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x(1) &amp;amp;= A_dx(0) + B_du(0)\\
x(2) &amp;amp;= A_dx(1) + B_du(1) = A_d^2 x(0) + A_dB_du(0) + B_du(1)\\
.\\
x(n) &amp;amp;= A_d^n x(0) + A_d^{n-1}B_du(0) + A_d^{n-2}B_du(1) + ...
+ B_du(n-1)
\end{align*}
&lt;/div&gt;
&lt;p&gt;The last equation can be written as:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
x(n) -  A_d^n x(0)=
\begin{bmatrix}
B_d &amp;amp; A_dB_d &amp;amp; A_d^2B_d &amp;amp; ... &amp;amp; A_d^{n-2}B_d &amp;amp; A_d^{n-1}B_d
\end{bmatrix} *
\begin{bmatrix}
u(n-1) \\ . \\ . \\ . \\u(2) \\ u(1) \\ u(0)
\end{bmatrix}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;Replacing with the controllability matrix with C, we get:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
x(n) - A_d^n x(0) = C * U \\
\therefore\\
U = C^{-1} * (x(n) - A_d^n x(0))
\end{align*}
&lt;/div&gt;
&lt;p&gt;This means that the equation is solvable when &lt;span class="math"&gt;\(C^{-1}\)&lt;/span&gt; exists,
i.e. its a non singular matrix.&lt;/p&gt;
&lt;p&gt;Therefore a system is controllable when:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
\begin{bmatrix}
B_d &amp;amp; A_dB_d &amp;amp; A_d^2B_d &amp;amp; ... &amp;amp; A_d^{n-2}B_d &amp;amp; A_d^{n-1}B_d
\end{bmatrix}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;has a determinant.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="observability"&gt;
&lt;h2&gt;Observability&lt;/h2&gt;
&lt;p&gt;Using the discretized equation too:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{align*}
y(0) = C_dx(0)\\
y(1) = C_dx(1) = C_dA_dx(0)\\
y(2) = C_dx(2) = C_dA_d^2x(0) \\
y(n) = C_dx(n) = C_dA_d^nx(0)
\end{align*}
&lt;/div&gt;
&lt;p&gt;This can be written as:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
\begin{bmatrix}
y(0)\\y(1)\\y(2)\\.\\y(n)
\end{bmatrix} =
\begin{bmatrix}
C_d \\ C_dA_d \\ C_dA_d^2 \\ . \\C_dA_d^n
\end{bmatrix} *
x(0)
\end{equation*}
&lt;/div&gt;
&lt;p&gt;The observability matrix is:&lt;/p&gt;
&lt;div class="math"&gt;
\begin{equation*}
\begin{bmatrix}
C_d \\ C_dA_d \\ C_dA_d^2 \\ . \\C_dA_d^n
\end{bmatrix}
\end{equation*}
&lt;/div&gt;
&lt;p&gt;And the systems has a unique solution if the system has a rank n.
If the system is a square matrix, the solution is of rank n if the
determinant is not 0.&lt;/p&gt;
&lt;p&gt;The observability and controllability derivations can be found
from &lt;a class="reference external" href="http://www.ece.rutgers.edu/~gajic/psfiles/chap5.pdf"&gt;here:rutgers&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;script type='text/javascript'&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Engineering"/><category term="control"/></entry><entry><title>System Clock Prescalers and Their Hazards</title><link href="https://blog.jnduli.co.ke/system_clock_prescalers_and_their_hazards.html" rel="alternate"/><published>2018-02-04T15:00:00+03:00</published><updated>2018-02-04T15:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2018-02-04:/system_clock_prescalers_and_their_hazards.html</id><summary type="html">&lt;p&gt;One means of saving power with the attiny13A is by slowing down
the clock frequency of the microcontroller. One option of doing
this is by using the system clock prescaler. The attiny13A is
shipped with running on a 9.6MHz internal RC Oscillator. It has
the CKDIV8 fuse set (which …&lt;/p&gt;</summary><content type="html">&lt;p&gt;One means of saving power with the attiny13A is by slowing down
the clock frequency of the microcontroller. One option of doing
this is by using the system clock prescaler. The attiny13A is
shipped with running on a 9.6MHz internal RC Oscillator. It has
the CKDIV8 fuse set (which provides a system clock prescaler of
8), thus it runs on a frequency of 1.2MHz.&lt;/p&gt;
&lt;p&gt;Supposing we want to slow it further, we can set up the CLKPR
(Clock Prescale Register) bits for the same. If we set this to a
prescaler of 16 for example, the attiny will run at a frequency of
600KHz. However, there is a special write procedure provided for
this to prevent unintentional changes:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Write the Clock Prescaler Change Enable (CLKPCE) bit to one and all other bits in
CLKPR to zero.&lt;/li&gt;
&lt;li&gt;Within four cycles, write the desired value to CLKPS while writing a zero to CLKPCE.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To follow this, our code will be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// set CLKPCE to 1 and other bits to zero&lt;/span&gt;
&lt;span class="n"&gt;CLKPR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mb"&gt;0b10000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// set prescaler to 256&lt;/span&gt;
&lt;span class="n"&gt;CLKPR&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mb"&gt;0b00001000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Furthermore, when compiling we have to use the optimization flags
-0s, otherwise the process might take more than four cycles. The
microcontroller will be running at a clock speed of 37.5KHz after
the operation.&lt;/p&gt;
&lt;div class="section" id="hazards"&gt;
&lt;h2&gt;Hazards&lt;/h2&gt;
&lt;p&gt;If successful, however, programming the microcontroller will be
difficult. This is because of syncing issues, whereby the
microcontroller is significantly slower than the programmer. To
fix this you can set the -B flag using avrdude. However, the
usbasp programmer I am using does not have this functionality.&lt;/p&gt;
&lt;p&gt;To fix this we have to analyze how the microcontroller sets the
new frequency. First, the microcontroller will boot up. At this
state CLKPR is still its default value. Then the two instructions
setting the prescaler are run. This therefore means that if we can
find a way to stop those two instructions from running, we can
program the microcontroller with another firmware.&lt;/p&gt;
&lt;p&gt;IF we activate the RESET pin, before the clock prescaler is set,
the chip can be reprogrammed. Connect Ground signal to the RESET
pin and then provide power to the programmer (usbasp). This
maintains the microcontroller in Reset mode, thus the instructions
changing the prescaler are not carried out. Then immediately flash
the attiny. If successful, the error will be gone.&lt;/p&gt;
&lt;p&gt;Another way to prevent this is to have a considerable delay before
the CLKPCE bits are set. This delay allows for reprogramming if
necessary.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Engineering"/><category term="projects"/></entry><entry><title>Prepare Kicad PCB for Elecrow Production</title><link href="https://blog.jnduli.co.ke/prepare-kicad-pcb-elecrow.html" rel="alternate"/><published>2017-12-07T08:00:00+03:00</published><updated>2017-12-07T08:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2017-12-07:/prepare-kicad-pcb-elecrow.html</id><summary type="html">&lt;p&gt;Before designing the PCB, set the following design rules:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Minimum PCB Track : greater than 0.2mm&lt;/li&gt;
&lt;li&gt;Minimum Track /Vias space: greater than 0.2mm&lt;/li&gt;
&lt;li&gt;Minimum pads space: greater than 0.2mm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The defaults provided by Kicad are OK, so no need to change them
unless you have some special requirements …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Before designing the PCB, set the following design rules:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Minimum PCB Track : greater than 0.2mm&lt;/li&gt;
&lt;li&gt;Minimum Track /Vias space: greater than 0.2mm&lt;/li&gt;
&lt;li&gt;Minimum pads space: greater than 0.2mm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The defaults provided by Kicad are OK, so no need to change them
unless you have some special requirements.&lt;/p&gt;
&lt;p&gt;Once the pcb has been completed, go go:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;File&amp;gt;&amp;gt;Plot
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Make sure the following options are checked in Layers section:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;F.Cu&lt;/li&gt;
&lt;li&gt;B.Cu&lt;/li&gt;
&lt;li&gt;B.SilkS&lt;/li&gt;
&lt;li&gt;F.SilkS&lt;/li&gt;
&lt;li&gt;B.Mask&lt;/li&gt;
&lt;li&gt;F.Mask&lt;/li&gt;
&lt;li&gt;Edge.Cuts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure the Plot format is set to Gerber.
It is also advisable to set an OutputDirectory for your gerber
files. This prevents mixing up the gerber with normal project
files.&lt;/p&gt;
&lt;p&gt;Make sure the following are checked in Options:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Plot footprint values&lt;/li&gt;
&lt;li&gt;Plot footprint references&lt;/li&gt;
&lt;li&gt;Exclude PCB edge layer from other layers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For Gerber options, make sure this is checked:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Use Protel filename extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Click on Plot to generate the gerber files.&lt;/p&gt;
&lt;p&gt;To generate the drill files, click on &amp;quot;Generate Drill File&amp;quot;.
I set the Drill Units to Millimeters, Zeros Format to Decimal
Format and Dril Map File Format to Gerber. Also Drill Origin was
set to Absolute. Then click Drill File.&lt;/p&gt;
&lt;p&gt;Kicad outputs files with a different naming convention as that
expected by elecrow. So you'll have to rename these files. For
examples:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;pump-B.Cu.gbl becomes pump.gbl&lt;/li&gt;
&lt;li&gt;pump-B.Mask.gbs becomes pump.gbs&lt;/li&gt;
&lt;li&gt;pump-B.SilkS.gbo becomes pump.gbo&lt;/li&gt;
&lt;li&gt;pump-Edge.Cuts.gm1 becomes pump.gml&lt;/li&gt;
&lt;li&gt;pump-F.Cu.gtl becomes pump.gtl&lt;/li&gt;
&lt;li&gt;pump-F.Mask.gts becomes pump.gts&lt;/li&gt;
&lt;li&gt;pump-F.SilkS.gto becomes pump.gto&lt;/li&gt;
&lt;li&gt;pump.drl becomes pump.txt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After renaming the files you can then compress all of them into a
zip file and make the order.&lt;/p&gt;
</content><category term="Engineering"/></entry><entry><title>Scrolling Text With 5*4 LED Array</title><link href="https://blog.jnduli.co.ke/5-by-4-led-array-scrolling-text.html" rel="alternate"/><published>2017-10-29T10:20:00+03:00</published><updated>2017-10-29T10:20:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2017-10-29:/5-by-4-led-array-scrolling-text.html</id><summary type="html">&lt;p&gt;I usually see lights in Nairobi scrolling text from right to left.
It is an interesting thing to see, and if done right is usually
beautiful. I wanted to make one, and so this is how I went about
it.&lt;/p&gt;
&lt;div class="section" id="multiplexing-leds"&gt;
&lt;h2&gt;Multiplexing LEDs&lt;/h2&gt;
&lt;p&gt;To light and LED one needs 5V supplied …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;I usually see lights in Nairobi scrolling text from right to left.
It is an interesting thing to see, and if done right is usually
beautiful. I wanted to make one, and so this is how I went about
it.&lt;/p&gt;
&lt;div class="section" id="multiplexing-leds"&gt;
&lt;h2&gt;Multiplexing LEDs&lt;/h2&gt;
&lt;p&gt;To light and LED one needs 5V supplied to cathode and GND supplied
to anode. This means if we have 2 leds, both receiving 5V and only
one receiving GND, only one LED will light. The same occurs when
both receive GND and only one receives 5V. This is the basic
building block of an LED matrix.&lt;/p&gt;
&lt;p&gt;So how does one keep this so. Well, one option is using a
microcontroller. The microcontroller's pins can be set to output a
HIGH (5V) or a LOW(GND/0V). So having a microcontroller will help
us out in choosing which LED to use.&lt;/p&gt;
&lt;p&gt;The following is the schematic I then came up with:&lt;/p&gt;
&lt;img alt="5_by_4 led array image" src="https://blog.jnduli.co.ke/images/5_by_4_led_array.png" /&gt;
&lt;p&gt;To have the scrolling effect, I then wrote code that turns on and
off LEDs at a particular pattern. Making this faster will result
in less flickering of light. I used an atmega 8 for this and added
the respective current limiting resistors. The circuit and code
for this can be found &lt;a class="reference external" href="https://github.com/jnduli/led_array_atmega8"&gt;here:githublink&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is the image of the final fabricated circuit:&lt;/p&gt;
&lt;img alt="5_by_4 led array image" src="https://blog.jnduli.co.ke/images/5_by_4_led_physical_circuit.jpg" /&gt;
&lt;p&gt;You can find the video of the working circuit here. The text being
shown is &amp;quot;HAPPY EASTER&amp;quot; in case it is the video is not clear
enough.&lt;/p&gt;
&lt;iframe width="560" height="315" src="https://www.youtube.com/embed/Zk5a-tJbTKo" frameborder="0" gesture="media" allowfullscreen&gt;&lt;/iframe&gt;&lt;/div&gt;
</content><category term="Engineering"/><category term="avr"/><category term="projects"/></entry><entry><title>Disabling Blanking in Raspberry Pi</title><link href="https://blog.jnduli.co.ke/disabling-blanking-raspberry-pi.html" rel="alternate"/><published>2017-10-17T15:00:00+03:00</published><updated>2017-10-17T15:00:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2017-10-17:/disabling-blanking-raspberry-pi.html</id><summary type="html">&lt;p&gt;Blanking is whereby a screen just goes blank (i.e. black screen).
This usually happens after some time of no usage on the raspberry
pi which is about 15 minutes. This can sometimes become
frustrating especially if it is a screen set up for monitoring
something.&lt;/p&gt;
&lt;p&gt;So to disable it …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Blanking is whereby a screen just goes blank (i.e. black screen).
This usually happens after some time of no usage on the raspberry
pi which is about 15 minutes. This can sometimes become
frustrating especially if it is a screen set up for monitoring
something.&lt;/p&gt;
&lt;p&gt;So to disable it temporarily:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;xset&lt;span class="w"&gt; &lt;/span&gt;s&lt;span class="w"&gt; &lt;/span&gt;off&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# disables screen saver&lt;/span&gt;
xset&lt;span class="w"&gt; &lt;/span&gt;-dpms&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;#disable power management settings&lt;/span&gt;
xset&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;noblank
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To disable it permanently, open the file
/etc/lightdm/lightdm.conf:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vi&lt;span class="w"&gt; &lt;/span&gt;/etc/lightdm/lightdm.conf
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add the following line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;xserver-command&lt;span class="o"&gt;=&lt;/span&gt;X&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-dpms
&lt;/pre&gt;&lt;/div&gt;
</content><category term="Engineering"/><category term="raspberry pi"/></entry><entry><title>Setting Up Raspberry Pi</title><link href="https://blog.jnduli.co.ke/setting-up-raspberry-pi.html" rel="alternate"/><published>2017-07-16T08:20:00+03:00</published><updated>2017-07-16T08:20:00+03:00</updated><author><name>John Nduli</name></author><id>tag:blog.jnduli.co.ke,2017-07-16:/setting-up-raspberry-pi.html</id><summary type="html">&lt;p&gt;The raspberry pi is a mini computer with GPIO pins that can be
used to interface with various sensors and actuators. More
information of the raspberry pi can be found &lt;a class="reference external" href="https://www.raspberrypi.org/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The SD card should be of a size greater than 4GB, especially if
you want to install Raspbian Jesse …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The raspberry pi is a mini computer with GPIO pins that can be
used to interface with various sensors and actuators. More
information of the raspberry pi can be found &lt;a class="reference external" href="https://www.raspberrypi.org/"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The SD card should be of a size greater than 4GB, especially if
you want to install Raspbian Jesse.&lt;/p&gt;
&lt;p&gt;Connect the card to your computer and get its id. To do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;fdisk&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you do the above command before inserting the card and after
inserting the card, you'll find a new addition which is the card.&lt;/p&gt;
&lt;p&gt;Format the card to fat partition. To do this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/sdcardfound
sudo&lt;span class="w"&gt; &lt;/span&gt;mkfs.fat&lt;span class="w"&gt; &lt;/span&gt;/dev/sdcardfound
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After this the sd card is ready for use.&lt;/p&gt;
&lt;p&gt;Download the OS from
&lt;a class="reference external" href="https://www.raspberrypi.org/downloads/raspbian/"&gt;here:raspbian&lt;/a&gt;,
and unzip it. I chose Raspbian Jesse with Desktop for my OS.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;unzip&lt;span class="w"&gt; &lt;/span&gt;downloadedraspbian.zip
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then burn the OS into the sd card using dd:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unzippedraspbian.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/sdcardfound&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;progress
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once this step is completed the OS is ready for use.&lt;/p&gt;
&lt;div class="section" id="with-screen-keyboard-and-mouse"&gt;
&lt;h2&gt;With Screen, Keyboard and Mouse&lt;/h2&gt;
&lt;p&gt;Attach the sd card, screen, keyboard and mouse to the raspberry
pi. For power, a supply rated 5V 2.5A is recommended. In my case,
I used one rate 5V and 1A and it worked well. When powered, there
are two LED lights, a red one and a green one. The red should be
on indicating the pi is receiving power. The green light should be
blinking. If it is not this indicated either a problem with the sd
card or the power supply.&lt;/p&gt;
&lt;p&gt;Once the pi is started, wait until the desktop environment is
loaded. Then you can then enable vnc and ssh.&lt;/p&gt;
&lt;p&gt;To do this, go to the terminal and type:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;raspi-config
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Go to interface and enable both vnc and ssh.&lt;/p&gt;
&lt;p&gt;You can also go to MENU &amp;gt; PREFERENCES &amp;gt; Interfaces. From this you
can also enable ssh and vnc.&lt;/p&gt;
&lt;p&gt;To set up a virtual keyboard, first connect to a wifi/LAN network
and do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;update
sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;matchbox-keyboard
sudo&lt;span class="w"&gt; &lt;/span&gt;reboot
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The keyboard should be in accessibility menu.
If it isn't go to MENU &amp;gt; Preferences &amp;gt; Main Menu Editor &amp;gt;
Accesorites. Disable and enable the Keyboard option.&lt;/p&gt;
&lt;p&gt;To ssh into the pi, first get the ip address. To do this, hover on
the wifi icon and the ip will be displayed. You can also get the
ip address from the terminal by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ip&lt;span class="w"&gt; &lt;/span&gt;address&lt;span class="w"&gt; &lt;/span&gt;show
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;From another computer, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;pi@piipaddress
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The password is : raspberry&lt;/p&gt;
&lt;p&gt;The pi already comes with a vnc server known as realvnc. So just
install the viewer on your laptop and use it. For archlinux:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;yaourt&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;realvnc-vnc-viewer
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then confirm that vnc is enabled on the pi. To use it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;vncviewer&lt;span class="w"&gt; &lt;/span&gt;piipaddress
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Realvnc can also be setup to run via the cloud. Just visit their
website &lt;a class="reference external" href="https://www.realvnc.com/en/"&gt;here:realvnc&lt;/a&gt; to find out
how.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Engineering"/><category term="raspberry pi"/></entry></feed>