<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Alexander Korotkov's blog]]></title>
  <link href="https://akorotkov.github.io/atom.xml" rel="self"/>
  <link href="https://akorotkov.github.io/"/>
  <updated>2021-05-28T22:52:41+03:00</updated>
  <id>https://akorotkov.github.io/?utm_medium=social&amp;utm_source=rss</id>
  <author>
    <name><![CDATA[Alexander Korotkov]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[PostgreSQL 14: Substantion Change to Fulltext Query Parsing]]></title>
    <link href="https://akorotkov.github.io/blog/2021/05/22/pg-14-query-parsing/"/>
    <updated>2021-05-22T14:12:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2021/05/22/pg-14-query-parsing/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p><img class="no-border 2x" src="https://akorotkov.github.io/images/fts.png" width="490" height="235" /></p>

<p>Long story short, since PostgreSQL 14 <code>to_tsquery('pg_class')</code> becomes
<code>'pg' &lt;-&gt; 'class'</code> instead of <code>'pg' &amp; 'class'</code>
(<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=0c4f355c6a">commit 0c4f355c6a</a>).  That is for instance,
in PostgreSQL 13 and earlier <code>to_tsquery('pg_class')</code> matches
<code>to_tsvector('a class of pg')</code>.  But since PostgreSQL 14 it doesn’t match, but
still matches <code>to_tsvector('pg_class')</code> and <code>to_tsvector('pg*class')</code>.
This is incompatible change, which affects fts users, but we have to do this
in order to fix phrase search design problems.</p>

<p>The story started with a
<a href="https://www.postgresql.org/message-id/16592-70b110ff9731c07d@postgresql.org">bug</a>
when <code>to_tsvector('pg_class pg')</code> didn’t match to
<code>websearch_to_tsquery('"pg_class pg"')</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">to_tsvector</span><span class="p">(</span><span class="s1">&#39;pg_class pg&#39;</span><span class="p">)</span> <span class="o">@@</span>
</span><span class="line">         <span class="n">websearch_to_tsquery</span><span class="p">(</span><span class="s1">&#39;&quot;pg_class pg&quot;&#39;</span><span class="p">);</span>
</span><span class="line"> <span class="o">?</span><span class="k">column</span><span class="o">?</span>
</span><span class="line"><span class="c1">----------</span>
</span><span class="line"> <span class="n">f</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Looks strange!  Naturally, when you search for some
text in quotes, you expect it to match at least the exact same text in the document.
But it doesn’t. My first idea was that it’s just bug of <code>websearch_to_tsquery()</code>
function, but <code>to_tsquery()</code> appears to have the same problem:
<code>to_tsquery('pg_class &lt;-&gt; pg')</code> doesn’t match to <code>to_tsvector('pg_class pg')</code>
as well.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">to_tsvector</span><span class="p">(</span><span class="s1">&#39;pg_class pg&#39;</span><span class="p">)</span> <span class="o">@@</span>
</span><span class="line">         <span class="n">to_tsquery</span><span class="p">(</span><span class="s1">&#39;pg_class &lt;-&gt; pg&#39;</span><span class="p">);</span>
</span><span class="line"> <span class="o">?</span><span class="k">column</span><span class="o">?</span>
</span><span class="line"><span class="c1">----------</span>
</span><span class="line"> <span class="n">f</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>I was surprised that although phrase search arrived many years ago,
basic things don’t work.</p>

<!--more-->

<p>Looking under the hood, both <code>websearch_to_tsquery('"pg_class pg"')</code> and
<code>to_tsquery('pg_class &lt;-&gt; pg')</code> compiles into <code>( 'pg' &amp; 'class' ) &lt;-&gt; 'pg'</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">websearch_to_tsquery</span><span class="p">(</span><span class="s1">&#39;&quot;pg_class pg&quot;&#39;</span><span class="p">),</span>
</span><span class="line">         <span class="n">to_tsquery</span><span class="p">(</span><span class="s1">&#39;pg_class &lt;-&gt; pg&#39;</span><span class="p">);</span>
</span><span class="line">    <span class="n">websearch_to_tsquery</span>     <span class="o">|</span>         <span class="n">to_tsquery</span>
</span><span class="line"><span class="c1">-----------------------------+-----------------------------</span>
</span><span class="line"> <span class="p">(</span> <span class="s1">&#39;pg&#39;</span> <span class="o">&amp;</span> <span class="s1">&#39;class&#39;</span> <span class="p">)</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;pg&#39;</span> <span class="o">|</span> <span class="p">(</span> <span class="s1">&#39;pg&#39;</span> <span class="o">&amp;</span> <span class="s1">&#39;class&#39;</span> <span class="p">)</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;pg&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>This tsquery expects both <code>pg</code> and <code>class</code> to be one position left from another
<code>pg</code>.  That means both <code>pg</code> and <code>class</code> need to reside in the same position.
In principle, that’s possible, for instance, when a single word is split into two
synonyms by fulltext dictionary.  But that’s not our case.  When we parse
<code>pg_class pg</code> text, each word gets position sequentially.  No two of them
reside in the same position.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">to_tsvector</span><span class="p">(</span><span class="s1">&#39;pg_class pg&#39;</span><span class="p">);</span>
</span><span class="line">    <span class="n">to_tsvector</span>
</span><span class="line"><span class="c1">--------------------</span>
</span><span class="line"> <span class="s1">&#39;class&#39;</span><span class="p">:</span><span class="mi">2</span> <span class="s1">&#39;pg&#39;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span>
</span><span class="line"><span class="p">(</span><span class="mi">1</span> <span class="k">row</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Why does tsquery parsing work this way?  Historically, in PostgreSQL fulltext search
<code>to_tsquery('pg_class')</code> compiles into <code>'pg' &amp; 'class'</code>.  Therefore, <code>pg</code> and
<code>class</code> don’t have to appear together.  Before phrase search, that was the
only way to process this query as soon as we split <code>pg_class</code> into <code>pg</code> and
<code>class</code>.  Thus, querying compound words was a bit relaxed.  But now, when
combined with phrase search, it becomes unreasonably strict.</p>

<p>My original intention was to choose the way to compile <code>pg_class</code> depending
on the context.  With phrase search operator nearby <code>pg_class</code> should become
<code>'pg' &lt;-&gt; 'class'</code>, but be <code>'pg' &amp; 'class'</code> in the rest of cases.  But this
way required invasive refactoring of tsquery processing, taking more time than
I could to spend on this bug.</p>

<p>Fortunately, <a href="https://www.postgresql.org/message-id/10026.1609953512%40sss.pgh.pa.us">Tom Lane came with a proposal</a>
to always compile <code>pg_class</code> into <code>'pg' &lt;-&gt; 'class'</code>.  Thus, now both
<code>websearch_to_tsquery('"pg_class pg"')</code> and <code>to_tsquery('pg_class &lt;-&gt; pg')</code>
compile into <code>'pg' &lt;-&gt; 'class' &lt;-&gt; 'pg'</code>.  And both of them match to
<code>to_tsvector('pg_class pg')</code>.  That is a win!</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">websearch_to_tsquery</span><span class="p">(</span><span class="s1">&#39;&quot;pg_class pg&quot;&#39;</span><span class="p">),</span>
</span><span class="line">         <span class="n">to_tsquery</span><span class="p">(</span><span class="s1">&#39;pg_class &lt;-&gt; pg&#39;</span><span class="p">);</span>
</span><span class="line">   <span class="n">websearch_to_tsquery</span>    <span class="err">│</span>        <span class="n">to_tsquery</span>
</span><span class="line"><span class="err">───────────────────────────┼───────────────────────────</span>
</span><span class="line"> <span class="s1">&#39;pg&#39;</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;class&#39;</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;pg&#39;</span> <span class="err">│</span> <span class="s1">&#39;pg&#39;</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;class&#39;</span> <span class="o">&lt;-&gt;</span> <span class="s1">&#39;pg&#39;</span>
</span><span class="line">
</span><span class="line"><span class="o">#</span> <span class="k">select</span> <span class="n">to_tsvector</span><span class="p">(</span><span class="s1">&#39;pg_class pg&#39;</span><span class="p">)</span> <span class="o">@@</span> <span class="n">websearch_to_tsquery</span><span class="p">(</span><span class="s1">&#39;&quot;pg_class pg&quot;&#39;</span><span class="p">),</span>
</span><span class="line">         <span class="n">to_tsvector</span><span class="p">(</span><span class="s1">&#39;pg_class pg&#39;</span><span class="p">)</span> <span class="o">@@</span> <span class="n">to_tsquery</span><span class="p">(</span><span class="s1">&#39;pg_class &lt;-&gt; pg&#39;</span><span class="p">);</span>
</span><span class="line"> <span class="o">?</span><span class="k">column</span><span class="o">?</span> <span class="err">│</span> <span class="o">?</span><span class="k">column</span><span class="o">?</span>
</span><span class="line"><span class="err">──────────┼──────────</span>
</span><span class="line"> <span class="n">t</span>        <span class="err">│</span> <span class="n">t</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>This approach would make
all queries involving compound words more strict. But at first, this appears
the only easy way to fix this design bug. Secondly, this is probably a better
way to handle compound words themselves.</p>

<p>And AFAICS, this approach seems to be the right way.  Thanks to it, yet another
<a href="https://www.postgresql.org/message-id/CA%2B0DEqiZs7gdOd4ikmg%3D0UWG%2BSwWOLxPsk_JW-sx9WNOyrb0KQ%40mail.gmail.com">phrase search bug</a>
appears to be quite <a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=eb086056fec44516efdd5db71244a079fed65c7f">easy to fix</a>.</p>

<p>Happy phrase searching in PostgreSQL 14!  Hopefully, we would further manage
without incompatible changes :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Rainbow Your Psql Output]]></title>
    <link href="https://akorotkov.github.io/blog/2021/05/17/rainbow-psql-output/"/>
    <updated>2021-05-17T23:30:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2021/05/17/rainbow-psql-output/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p><img class="no-border 2x" src="https://akorotkov.github.io/images/rainbow-psql.png" width="850" height="655" />
It seems a good idea to change grey psql output to a lovely rainbow in honor
of <a href="https://en.wikipedia.org/wiki/International_Day_Against_Homophobia,_Transphobia_and_Biphobia">IDAHOT</a> day.
Thankfully there is <a href="https://github.com/busyloop/lolcat">lolcat</a> utility,
which is very easy to install on Linux and Mac OS.</p>

<p>Linux</p>
<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>sudo snap install lolcat
</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>Mac OS</p>
<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>brew install lolcat
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Having lolcat installed, you can set it up as a psql pager and get lovely rainbow
psql output!</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="err">\</span><span class="n">pset</span> <span class="n">pager</span> <span class="n">always</span>
</span><span class="line"><span class="err">\</span><span class="n">setenv</span> <span class="n">PAGER</span> <span class="s1">&#39;lolcat -f | less -iMSx4R -FX&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Jsonpath: ** Operator and Lax Mode Are't Meant to Be Together.]]></title>
    <link href="https://akorotkov.github.io/blog/2021/05/06/jsonpath-double-asterisk-lax/"/>
    <updated>2021-05-06T18:10:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2021/05/06/jsonpath-double-asterisk-lax/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p><img class="no-border 2x" src="https://akorotkov.github.io/images/double_asterisk_lax.png" width="529" height="221" /></p>

<p>PostgreSQL has an extension to jsonpath: <code>**</code> operator, which explores
arbitrary depth finding your values everywhere.  At the same time, there is
a <code>lax</code> mode, defined by the standard, providing a “relaxed” way for working
with json.  In the <code>lax</code> mode, accessors automatically unwrap arrays; missing
keys don’t trigger errors; etc.  In short, it appears that the <code>**</code> operator
and <code>lax</code> mode aren’t designed to be together :)</p>

<!--more-->

<p>The story started with <a href="https://www.postgresql.org/message-id/16828-2b0229babfad2d8c%40postgresql.org">the bug report</a>.
The simplified version is below.  Jsonpath query is intended to select the
value of key <code>"y"</code> everywhere.  But it appears to select these values twice.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">jsonb_path_query</span><span class="p">(</span><span class="s1">&#39;[{&quot;x&quot;: &quot;a&quot;, &quot;y&quot;: [{&quot;x&quot;:&quot;b&quot;}]}]&#39;</span><span class="p">::</span><span class="n">jsonb</span><span class="p">,</span>
</span><span class="line">                                 <span class="s1">&#39;$.**.x&#39;</span><span class="p">);</span>
</span><span class="line"> <span class="n">jsonb_path_query</span>
</span><span class="line"><span class="c1">------------------</span>
</span><span class="line"> <span class="ss">&quot;a&quot;</span>
</span><span class="line"> <span class="ss">&quot;a&quot;</span>
</span><span class="line"> <span class="ss">&quot;b&quot;</span>
</span><span class="line"> <span class="ss">&quot;b&quot;</span>
</span><span class="line"><span class="p">(</span><span class="mi">4</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>This case looks like a bug. But is it? Let’s dig into details. Let’s split
the jsonpath query into two parts: one containing the <code>**</code> operator and another
having the key accessor.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">SELECT</span> <span class="n">var</span><span class="p">,</span>
</span><span class="line">         <span class="n">jsonb_path_query_array</span><span class="p">(</span><span class="n">var</span><span class="p">,</span> <span class="s1">&#39;$.x&#39;</span><span class="p">)</span> <span class="n">key_x</span>
</span><span class="line">  <span class="k">FROM</span> <span class="n">jsonb_path_query</span><span class="p">(</span><span class="s1">&#39;[{&quot;x&quot;: &quot;a&quot;, &quot;y&quot;: [{&quot;x&quot;:&quot;b&quot;}]}]&#39;</span><span class="p">::</span><span class="n">jsonb</span><span class="p">,</span>
</span><span class="line">                        <span class="s1">&#39;$.**&#39;</span><span class="p">)</span> <span class="n">var</span><span class="p">;</span>
</span><span class="line">               <span class="n">var</span>               <span class="o">|</span> <span class="n">key_x</span>
</span><span class="line"><span class="c1">---------------------------------+-------</span>
</span><span class="line"> <span class="p">[</span><span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;a&quot;</span><span class="p">,</span> <span class="ss">&quot;y&quot;</span><span class="p">:</span> <span class="p">[</span><span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;b&quot;</span><span class="err">}</span><span class="p">]</span><span class="err">}</span><span class="p">]</span> <span class="o">|</span> <span class="p">[</span><span class="ss">&quot;a&quot;</span><span class="p">]</span>
</span><span class="line"> <span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;a&quot;</span><span class="p">,</span> <span class="ss">&quot;y&quot;</span><span class="p">:</span> <span class="p">[</span><span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;b&quot;</span><span class="err">}</span><span class="p">]</span><span class="err">}</span>   <span class="o">|</span> <span class="p">[</span><span class="ss">&quot;a&quot;</span><span class="p">]</span>
</span><span class="line"> <span class="ss">&quot;a&quot;</span>                             <span class="o">|</span> <span class="p">[]</span>
</span><span class="line"> <span class="p">[</span><span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;b&quot;</span><span class="err">}</span><span class="p">]</span>                    <span class="o">|</span> <span class="p">[</span><span class="ss">&quot;b&quot;</span><span class="p">]</span>
</span><span class="line"> <span class="err">{</span><span class="ss">&quot;x&quot;</span><span class="p">:</span> <span class="ss">&quot;b&quot;</span><span class="err">}</span>                      <span class="o">|</span> <span class="p">[</span><span class="ss">&quot;b&quot;</span><span class="p">]</span>
</span><span class="line"> <span class="ss">&quot;b&quot;</span>                             <span class="o">|</span> <span class="p">[]</span>
</span><span class="line"><span class="p">(</span><span class="mi">6</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>As you can see, the <code>**</code> operator selects every child in the json document as
expected. The key accessor extracts corresponding values from both objects
themselves and their wrapping arrays. And that’s also expected in the <code>lax</code>
mode. So, it appears there is no bug; everything works as designed, although
it’s surprising for users.</p>

<p>Finally, I’ve <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=b41645460a">committed a paragraph</a> to the
<a href="https://www.postgresql.org/docs/devel/functions-json.html#STRICT-AND-LAX-MODES">docs</a>,
which explicitly clarifies this issue.
It seems that <code>lax</code> mode and <code>**</code> operator just aren’t designed to be
used together.  If you need <code>**</code> operator, you can use <code>strict</code> mode. and
everything is intuitively correct.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">jsonb_path_query</span><span class="p">(</span><span class="s1">&#39;[{&quot;x&quot;: &quot;a&quot;, &quot;y&quot;: [{&quot;x&quot;:&quot;b&quot;}]}]&#39;</span><span class="p">::</span><span class="n">jsonb</span><span class="p">,</span>
</span><span class="line">                                 <span class="s1">&#39;strict $.**.x&#39;</span><span class="p">);</span>
</span><span class="line"> <span class="n">jsonb_path_query</span>
</span><span class="line"><span class="c1">------------------</span>
</span><span class="line"> <span class="ss">&quot;a&quot;</span>
</span><span class="line"> <span class="ss">&quot;b&quot;</span>
</span><span class="line"><span class="p">(</span><span class="mi">2</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Dramatical Effect of LSE Instructions for PostgreSQL on Graviton2 Instances]]></title>
    <link href="https://akorotkov.github.io/blog/2021/04/30/arm/"/>
    <updated>2021-04-30T03:10:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2021/04/30/arm/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>The world changes. ARM architecture breaks into new areas of computing. An only
decade ago, only your mobile, router, or another specialized device could be
ARM-based, while your desktop and server were typically x86-based. Nowadays,
your new MacBook is ARM-based, and your EC2 instance could be ARM as well.</p>

<p>In the mid-2020, Amazon made graviton2 instances publically available. The
maximum number of CPU core there is 64. This number is where it becomes
interesting to check PostgreSQL scalability. It’s exciting to check because
ARM implements atomic operations using pair of load/store. So, in a sense,
ARM is just like Power, where
<a href="https://www.postgresql.org/message-id/CAPpHfdsKrh7c7P8-5eG-qW3VQobybbwqH%3DgL5Ck%2BdOES-gBbFg%40mail.gmail.com">I’ve previously seen</a>
a significant effect of platform-specific atomics optimizations.</p>

<p>But on the other hand, ARM 8.1 defines a set of LSE instructions, which,
in particular, provide the way to implement atomic operation in a single
instruction (just like x86). What would be better: special optimization,
which puts custom logic between load and store instructions, or just a simple
loop of LSE CAS instructions? I’ve tried them both.</p>

<p>You can see the results of read-only and read-write pgbench on the graphs
below (details on experiments are <a href="https://www.postgresql.org/message-id/CAPpHfdsGqVd6EJ4mr_RZVE5xSiCNBy4MuSvdTrKmTpM0eyWGpg%40mail.gmail.com">here</a>).
<code>pg14-devel-lwlock-ldrex-strex</code> is the patched PostgreSQL with special
load/store optimization for lwlock, <code>pg14-devel-lse</code> is PostgreSQL compiled
with LSE support enabled.</p>

<p><img class="no-border 2x" src="https://akorotkov.github.io/images/arm-ro.png" width="720" height="432" /></p>

<p><img class="no-border 2x" src="https://akorotkov.github.io/images/arm-rw.png" width="720" height="432" /></p>

<p>You can see that load/store optimization gives substantial positive effect, but
LSE rocks here!</p>

<p>So, if you’re running PostgreSQL on graviton2 instance, make sure you’ve
binaries compiled with LSE support (see <a href="https://github.com/aws/aws-graviton-getting-started/blob/master/c-c++.md">the instruction</a>)
because the effect is dramatic.</p>

<p>BTW, it appears that <a href="https://www.postgresql.org/message-id/1367116.1606802480%40sss.pgh.pa.us">none of these optimizations have a noticeable effect on the performance of Apple M1</a>.
Probably, M1 has a smart enough inner optimizer to recognize these different
implementations to be equivalent.  And it was surprising that LSE usage might
give <a href="https://www.postgresql.org/message-id/CAB10pyYgh%2BKM4rY6XYbj3NNHkUQVV9UNpqaVmb9_fLbsUW%2BVyg%40mail.gmail.com">a small negative effect on Kunpeng 920</a>.
It was discouraging for me to know an ARM processor, where single instruction
operation is slower than multiple instruction equivalent. Hopefully,
processor architects would fix this in new Kunpeng processors.</p>

<p>In general, we see that now different ARM embodiments have different
performance characteristics and different effects of optimizations. Hopefully,
this is a problem of growth, and it will be overcome soon.</p>

<p><strong>Update:</strong> As Krunal Bauskar pointer in the comments, LSE instructions are still
faster than the load/store option on Kunpeng 920. Different timings might cause
the regression. For instance, with LSE instructions, we could just faster reach
the regression caused by another bottleneck.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Full Text Search Done (Almost) Right in PostgreSQL 11]]></title>
    <link href="https://akorotkov.github.io/blog/2018/02/17/fulltext-search-made-almost-right/"/>
    <updated>2018-02-17T18:20:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2018/02/17/fulltext-search-made-almost-right/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Long story short, using PostgreSQL 11 with <a href="https://github.com/postgrespro/rum">RUM index</a>
you can do both TOP-N query and COUNT(*) for non-selective FTS queries without
fetching all the results from heap (that means much faster).  Are you bored yet?
If not, please read the detailed description below.</p>

<p>At November 1st 2017, Tome Lane committed a <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=7c70996e">patch</a>
enabling bitmap scans to behave like index-only scan when possible.
In particular, since PostgreSQL 11 COUNT(*) queries can be evaluated using
bitmap scans without accessing heap when corresponding bit in visibility map
is set.  This patch was written by Alexander Kuzmenkov and reviewed by
Alexey Chernyshov (sboth are my Postgres Pro colleagues), and it was heavily
revised by Tom Lane.</p>

<!--more-->

<p>This commit might seem to be just one of planner and executor optimizations,
nice but doesn’t deserve much attention.  However, under detailed consideration
this patch appears to be significant improvement on the way of making full text
search in PostgreSQL to be done the right way.</p>

<p>I’ve started working on FTS improvements in 2012.  That time I realized that GIN
index is good for selective FTS queries, when number of matching results is low.
See the example below: GIN did great work for us by returning just few dozens of
matching rows very fast.  The rest operations including relevance calculation
and sorting are also fast, because they are performed over very small row set.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">EXPLAIN</span> <span class="p">(</span><span class="k">ANALYZE</span><span class="p">,</span> <span class="n">BUFFERS</span><span class="p">)</span>
</span><span class="line"><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">pgmail</span>
</span><span class="line"><span class="k">WHERE</span> <span class="n">fts</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;exclusion constraint&#39;</span><span class="p">)</span>
</span><span class="line"><span class="k">ORDER</span> <span class="k">BY</span> <span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">fts</span><span class="p">,</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;exclusion constraint&#39;</span><span class="p">))</span> <span class="k">DESC</span>
</span><span class="line"><span class="k">LIMIT</span> <span class="mi">10</span><span class="p">;</span>
</span><span class="line">                                                               <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">----------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Limit</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">144</span><span class="p">.</span><span class="mi">26</span><span class="p">..</span><span class="mi">144</span><span class="p">.</span><span class="mi">28</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">320</span><span class="p">.</span><span class="mi">142</span><span class="p">..</span><span class="mi">320</span><span class="p">.</span><span class="mi">149</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">7138</span> <span class="k">read</span><span class="o">=</span><span class="mi">7794</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Sort</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">144</span><span class="p">.</span><span class="mi">26</span><span class="p">..</span><span class="mi">144</span><span class="p">.</span><span class="mi">32</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">320</span><span class="p">.</span><span class="mi">141</span><span class="p">..</span><span class="mi">320</span><span class="p">.</span><span class="mi">147</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="n">Sort</span> <span class="k">Key</span><span class="p">:</span> <span class="p">(</span><span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">fts</span><span class="p">,</span> <span class="s1">&#39;&#39;&#39;exclus&#39;&#39; &amp; &#39;&#39;constraint&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">))</span> <span class="k">DESC</span>
</span><span class="line">         <span class="n">Sort</span> <span class="k">Method</span><span class="p">:</span> <span class="n">top</span><span class="o">-</span><span class="n">N</span> <span class="n">heapsort</span>  <span class="n">Memory</span><span class="p">:</span> <span class="mi">38</span><span class="n">kB</span>
</span><span class="line">         <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">7138</span> <span class="k">read</span><span class="o">=</span><span class="mi">7794</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">44</span><span class="p">.</span><span class="mi">20</span><span class="p">..</span><span class="mi">143</span><span class="p">.</span><span class="mi">72</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">5</span><span class="p">.</span><span class="mi">232</span><span class="p">..</span><span class="mi">315</span><span class="p">.</span><span class="mi">302</span> <span class="k">rows</span><span class="o">=</span><span class="mi">3357</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;exclus&#39;&#39; &amp; &#39;&#39;constraint&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">               <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">2903</span>
</span><span class="line">               <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">7138</span> <span class="k">read</span><span class="o">=</span><span class="mi">7794</span>
</span><span class="line">               <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail_fts_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">44</span><span class="p">.</span><span class="mi">19</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">3</span><span class="p">.</span><span class="mi">689</span><span class="p">..</span><span class="mi">3</span><span class="p">.</span><span class="mi">689</span> <span class="k">rows</span><span class="o">=</span><span class="mi">3357</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">                     <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;exclus&#39;&#39; &amp; &#39;&#39;constraint&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">                     <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">11</span> <span class="k">read</span><span class="o">=</span><span class="mi">23</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">176</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">320</span><span class="p">.</span><span class="mi">213</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">15</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>But situation is different if FTS query is not selective and number of matching
rows is high.  Then we have fetch all those rows from heap, calculate relevance
for each of them and sort them.  And despite we only need TOP-10 rows, this
query takes a lot of time.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">EXPLAIN</span> <span class="p">(</span><span class="k">ANALYZE</span><span class="p">,</span> <span class="n">BUFFERS</span><span class="p">)</span>
</span><span class="line"><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">pgmail</span>
</span><span class="line"><span class="k">WHERE</span> <span class="n">fts</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">)</span>
</span><span class="line"><span class="k">ORDER</span> <span class="k">BY</span> <span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">fts</span><span class="p">,</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">))</span> <span class="k">DESC</span>
</span><span class="line"><span class="k">LIMIT</span> <span class="mi">10</span><span class="p">;</span>
</span><span class="line">                                                                 <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">--------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Limit</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">144</span><span class="p">.</span><span class="mi">26</span><span class="p">..</span><span class="mi">144</span><span class="p">.</span><span class="mi">28</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">18110</span><span class="p">.</span><span class="mi">231</span><span class="p">..</span><span class="mi">18110</span><span class="p">.</span><span class="mi">236</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">1358323</span> <span class="k">read</span><span class="o">=</span><span class="mi">399077</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Sort</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">144</span><span class="p">.</span><span class="mi">26</span><span class="p">..</span><span class="mi">144</span><span class="p">.</span><span class="mi">32</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">18110</span><span class="p">.</span><span class="mi">229</span><span class="p">..</span><span class="mi">18110</span><span class="p">.</span><span class="mi">231</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="n">Sort</span> <span class="k">Key</span><span class="p">:</span> <span class="p">(</span><span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">fts</span><span class="p">,</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">))</span> <span class="k">DESC</span>
</span><span class="line">         <span class="n">Sort</span> <span class="k">Method</span><span class="p">:</span> <span class="n">top</span><span class="o">-</span><span class="n">N</span> <span class="n">heapsort</span>  <span class="n">Memory</span><span class="p">:</span> <span class="mi">44</span><span class="n">kB</span>
</span><span class="line">         <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">1358323</span> <span class="k">read</span><span class="o">=</span><span class="mi">399077</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">44</span><span class="p">.</span><span class="mi">20</span><span class="p">..</span><span class="mi">143</span><span class="p">.</span><span class="mi">72</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">784</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">70</span><span class="p">.</span><span class="mi">267</span><span class="p">..</span><span class="mi">17895</span><span class="p">.</span><span class="mi">628</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">               <span class="k">Rows</span> <span class="n">Removed</span> <span class="k">by</span> <span class="k">Index</span> <span class="k">Recheck</span><span class="p">:</span> <span class="mi">266782</span>
</span><span class="line">               <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">39841</span> <span class="n">lossy</span><span class="o">=</span><span class="mi">79307</span>
</span><span class="line">               <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">1358323</span> <span class="k">read</span><span class="o">=</span><span class="mi">399077</span>
</span><span class="line">               <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail_fts_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">44</span><span class="p">.</span><span class="mi">19</span> <span class="k">rows</span><span class="o">=</span><span class="mi">25</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">63</span><span class="p">.</span><span class="mi">914</span><span class="p">..</span><span class="mi">63</span><span class="p">.</span><span class="mi">914</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">                     <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">                     <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">41</span> <span class="k">read</span><span class="o">=</span><span class="mi">102</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">131</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">18110</span><span class="p">.</span><span class="mi">376</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">16</span> <span class="k">rows</span><span class="p">)(</span><span class="mi">15</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>How can we improve this situation?  If we would get results from index
pre-ordered by relevance, then we would be able to evaluate TOP-N query
without fetching the whole set of matching rows from heap.  Unfortunately,
that appears to be impossible for GIN index which stores only facts of occurence
of specifix terms in document.  But if we have additional infromation
about terms positions in the index, then it might work.  That information
would be enough to calculate relevance only basing on index information.</p>

<p><img class="no-border center 2x" src="https://akorotkov.github.io/images/gin2rum.png" width="614" height="134" /></p>

<p>Thus, I’ve proposed <a href="https://www.postgresql.org/message-id/CAPpHfdtSt47PpRQBK6OawHePLJk8PF-wNhswaUpre7_%2Bcc_kmA%40mail.gmail.com">proposed</a>
a set of patches to GIN index.  Some improvements were committed including
<a href="http://www.sai.msu.su/~megera/postgres/talks/329_PGCon2014-GIN.pdf">index compression and index search optimization</a>.  However, additional information storage for GIN
index wasn’t committed, because it alters GIN index structure too much.</p>

<p>Fortunately, we have
<a href="blog/2016/04/06/extensible-access-methods/">extensible index access methods</a>
in PostgreSQL 9.6.  And that enables us to implement things, which wasn’t
committed to GIN and more, as a separate index access method
<a href="https://github.com/postgrespro/rum">RUM</a>.  Using RUM, one can execute TOP-N
FTS query much faster without fetching all the matching rows from heap.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">EXPLAIN</span> <span class="p">(</span><span class="k">ANALYZE</span><span class="p">,</span> <span class="n">BUFFERS</span><span class="p">)</span>
</span><span class="line"><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">pgmail</span>
</span><span class="line"><span class="k">WHERE</span> <span class="n">fts</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">)</span>
</span><span class="line"><span class="k">ORDER</span> <span class="k">BY</span> <span class="n">fts</span> <span class="o">&lt;=&gt;</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">)</span>
</span><span class="line"><span class="k">LIMIT</span> <span class="mi">10</span><span class="p">;</span>
</span><span class="line">                                                                <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">-------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Limit</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">48</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">83</span><span class="p">.</span><span class="mi">25</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">width</span><span class="o">=</span><span class="mi">1523</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">242</span><span class="p">.</span><span class="mi">974</span><span class="p">..</span><span class="mi">248</span><span class="p">.</span><span class="mi">366</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">809</span> <span class="k">read</span><span class="o">=</span><span class="mi">25</span><span class="p">,</span> <span class="n">temp</span> <span class="k">read</span><span class="o">=</span><span class="mi">187</span> <span class="n">written</span><span class="o">=</span><span class="mi">552</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">pgmail_idx</span> <span class="k">on</span> <span class="n">pgmail</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">48</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">193885</span><span class="p">.</span><span class="mi">14</span> <span class="k">rows</span><span class="o">=</span><span class="mi">54984</span> <span class="n">width</span><span class="o">=</span><span class="mi">1523</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">242</span><span class="p">.</span><span class="mi">972</span><span class="p">..</span><span class="mi">248</span><span class="p">.</span><span class="mi">358</span> <span class="k">rows</span><span class="o">=</span><span class="mi">10</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">         <span class="k">Order</span> <span class="k">By</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">&lt;=&gt;</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">         <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">809</span> <span class="k">read</span><span class="o">=</span><span class="mi">25</span><span class="p">,</span> <span class="n">temp</span> <span class="k">read</span><span class="o">=</span><span class="mi">187</span> <span class="n">written</span><span class="o">=</span><span class="mi">552</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">14</span><span class="p">.</span><span class="mi">709</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">312</span><span class="p">.</span><span class="mi">794</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">8</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>However, the problem persisted if you need to get total count of matching rows.
Then PostgreSQL executor still have to fetch all the matching rows from the
heap in order to check their visibility.  So, if you need total number of
resulting rows for pagination, then it’s still might be very slow.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">EXPLAIN</span> <span class="p">(</span><span class="k">ANALYZE</span><span class="p">,</span> <span class="n">BUFFERS</span><span class="p">)</span>
</span><span class="line"><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">pgmail</span>
</span><span class="line"><span class="k">WHERE</span> <span class="n">fts</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">);</span>
</span><span class="line">                                                              <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">--------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Aggregate</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">118931</span><span class="p">.</span><span class="mi">46</span><span class="p">..</span><span class="mi">118931</span><span class="p">.</span><span class="mi">47</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">36263</span><span class="p">.</span><span class="mi">708</span><span class="p">..</span><span class="mi">36263</span><span class="p">.</span><span class="mi">709</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">800692</span> <span class="k">read</span><span class="o">=</span><span class="mi">348338</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">530</span><span class="p">.</span><span class="mi">19</span><span class="p">..</span><span class="mi">118799</span><span class="p">.</span><span class="mi">14</span> <span class="k">rows</span><span class="o">=</span><span class="mi">52928</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">74</span><span class="p">.</span><span class="mi">724</span><span class="p">..</span><span class="mi">36195</span><span class="p">.</span><span class="mi">946</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">         <span class="k">Rows</span> <span class="n">Removed</span> <span class="k">by</span> <span class="k">Index</span> <span class="k">Recheck</span><span class="p">:</span> <span class="mi">266782</span>
</span><span class="line">         <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">39841</span> <span class="n">lossy</span><span class="o">=</span><span class="mi">79307</span>
</span><span class="line">         <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">800692</span> <span class="k">read</span><span class="o">=</span><span class="mi">348338</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail_fts_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">516</span><span class="p">.</span><span class="mi">96</span> <span class="k">rows</span><span class="o">=</span><span class="mi">52928</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">67</span><span class="p">.</span><span class="mi">467</span><span class="p">..</span><span class="mi">67</span><span class="p">.</span><span class="mi">467</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">               <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">41</span> <span class="k">read</span><span class="o">=</span><span class="mi">102</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">210</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">36263</span><span class="p">.</span><span class="mi">790</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">12</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>For sure, some modern UIs use techniques like continuous scrolling which doesn’t
require to show full number of results to user.  Also, one can use planner
estimation for number of resulting rows which is typically matching the order
of magnitude to actual number of resulting rows.  But nevertheless, slow counting
of total results number was a problem for many of RUM users.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">EXPLAIN</span> <span class="p">(</span><span class="k">ANALYZE</span><span class="p">,</span> <span class="n">BUFFERS</span><span class="p">)</span>
</span><span class="line"><span class="k">SELECT</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">pgmail</span>
</span><span class="line"><span class="k">WHERE</span> <span class="n">fts</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;english&#39;</span><span class="p">,</span> <span class="s1">&#39;Tom Lane&#39;</span><span class="p">);</span>
</span><span class="line">                                                              <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">--------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Aggregate</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">121794</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">121794</span><span class="p">.</span><span class="mi">29</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">132</span><span class="p">.</span><span class="mi">336</span><span class="p">..</span><span class="mi">132</span><span class="p">.</span><span class="mi">336</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">404</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">558</span><span class="p">.</span><span class="mi">13</span><span class="p">..</span><span class="mi">121656</span><span class="p">.</span><span class="mi">82</span> <span class="k">rows</span><span class="o">=</span><span class="mi">54984</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">83</span><span class="p">.</span><span class="mi">676</span><span class="p">..</span><span class="mi">116</span><span class="p">.</span><span class="mi">889</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">         <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">119148</span>
</span><span class="line">         <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">404</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">pgmail_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">544</span><span class="p">.</span><span class="mi">38</span> <span class="k">rows</span><span class="o">=</span><span class="mi">54984</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">61</span><span class="p">.</span><span class="mi">459</span><span class="p">..</span><span class="mi">61</span><span class="p">.</span><span class="mi">459</span> <span class="k">rows</span><span class="o">=</span><span class="mi">224568</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">fts</span> <span class="o">@@</span> <span class="s1">&#39;&#39;&#39;tom&#39;&#39; &amp; &#39;&#39;lane&#39;&#39;&#39;</span><span class="p">::</span><span class="n">tsquery</span><span class="p">)</span>
</span><span class="line">               <span class="n">Buffers</span><span class="p">:</span> <span class="n">shared</span> <span class="n">hit</span><span class="o">=</span><span class="mi">398</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">0</span><span class="p">.</span><span class="mi">183</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">133</span><span class="p">.</span><span class="mi">885</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">11</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[ALTER INDEX ... SET STATISTICS ...???]]></title>
    <link href="https://akorotkov.github.io/blog/2017/05/31/alter-index-weird/"/>
    <updated>2017-05-31T18:20:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2017/05/31/alter-index-weird/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>It’s not very widely known, but PostgreSQL is gathering statistics for indexed expressions.  See following example.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test</span> <span class="k">AS</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">random</span><span class="p">()</span> <span class="n">x</span><span class="p">,</span> <span class="n">random</span><span class="p">()</span> <span class="n">y</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1000000</span><span class="p">));</span>
</span><span class="line"><span class="k">ANALYZE</span> <span class="n">test</span><span class="p">;</span>
</span><span class="line">
</span><span class="line"><span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">test</span> <span class="k">WHERE</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">.</span><span class="mi">01</span><span class="p">;</span>
</span><span class="line">                                                <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">-----------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">20406</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">333333</span> <span class="n">width</span><span class="o">=</span><span class="mi">16</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">1</span><span class="p">.</span><span class="mi">671</span><span class="p">..</span><span class="mi">113</span><span class="p">.</span><span class="mi">693</span> <span class="k">rows</span><span class="o">=</span><span class="mi">56</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Filter</span><span class="p">:</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="s1">&#39;0.01&#39;</span><span class="p">::</span><span class="n">double</span> <span class="k">precision</span><span class="p">)</span>
</span><span class="line">   <span class="k">Rows</span> <span class="n">Removed</span> <span class="k">by</span> <span class="n">Filter</span><span class="p">:</span> <span class="mi">999944</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We created table with two columns <code>x</code> and <code>y</code> whose values are independently and uniformly distributed from 0 to 1.  Despite we analyze that table, PostgreSQL optimizer estimates selectivity of <code>x + y &lt; 0.01</code> qual as 1/3.  You can see that this estimation is not even close to reality: we actually selected 56 rows instead of 333333 rows estimated.  This estimation comes from a rough assumption that <code>&lt;</code> operator selects 1/3 of rows unless something more precise is known.  Of course, it could be possible for planner to do something better in this case.  For instance, it could try to calculate histogram for <code>x + y</code> from the separate histograms for <code>x</code> and <code>y</code>.  However, PostgreSQL optimizer doesn’t perform such costly and complex computations for now.</p>

<p>Situation changes once we define an index on <code>x + y</code>.</p>

<!--more-->

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">test_idx</span> <span class="k">ON</span> <span class="n">test</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">));</span>
</span><span class="line"><span class="k">ANALYZE</span> <span class="n">test</span><span class="p">;</span>
</span><span class="line">
</span><span class="line"><span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">test</span> <span class="k">WHERE</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">.</span><span class="mi">01</span><span class="p">;</span>
</span><span class="line">                                                     <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">---------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">13</span><span class="p">.</span><span class="mi">39</span><span class="p">..</span><span class="mi">1838</span><span class="p">.</span><span class="mi">32</span> <span class="k">rows</span><span class="o">=</span><span class="mi">641</span> <span class="n">width</span><span class="o">=</span><span class="mi">16</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">040</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">107</span> <span class="k">rows</span><span class="o">=</span><span class="mi">56</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="s1">&#39;0.01&#39;</span><span class="p">::</span><span class="n">double</span> <span class="k">precision</span><span class="p">)</span>
</span><span class="line">   <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">56</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">13</span><span class="p">.</span><span class="mi">23</span> <span class="k">rows</span><span class="o">=</span><span class="mi">641</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">028</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">028</span> <span class="k">rows</span><span class="o">=</span><span class="mi">56</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="s1">&#39;0.01&#39;</span><span class="p">::</span><span class="n">double</span> <span class="k">precision</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Besides index get used for this query, there is way more accurate estimate for the number of rows selected by <code>x + y &lt; 0.01</code>.  Estimation is improved because PostgreSQL is now gathering separate statistics for <code>x + y</code> expression.  You can check that by querying a system catalog.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">pg_stats</span> <span class="k">WHERE</span> <span class="n">tablename</span> <span class="o">=</span> <span class="s1">&#39;test_idx&#39;</span><span class="p">;</span>
</span><span class="line"><span class="o">-</span><span class="p">[</span> <span class="n">RECORD</span> <span class="mi">1</span> <span class="p">]</span><span class="c1">----------+--------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"><span class="n">schemaname</span>             <span class="o">|</span> <span class="k">public</span>
</span><span class="line"><span class="n">tablename</span>              <span class="o">|</span> <span class="n">test_idx</span>
</span><span class="line"><span class="n">attname</span>                <span class="o">|</span> <span class="n">expr</span>
</span><span class="line"><span class="n">inherited</span>              <span class="o">|</span> <span class="n">f</span>
</span><span class="line"><span class="n">null_frac</span>              <span class="o">|</span> <span class="mi">0</span>
</span><span class="line"><span class="n">avg_width</span>              <span class="o">|</span> <span class="mi">8</span>
</span><span class="line"><span class="n">n_distinct</span>             <span class="o">|</span> <span class="o">-</span><span class="mi">0</span><span class="p">.</span><span class="mi">999863</span>
</span><span class="line"><span class="n">most_common_vals</span>       <span class="o">|</span> <span class="err">{</span><span class="mi">0</span><span class="p">.</span><span class="mi">262215601745993</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">319712610449642</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">3959802063182</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">404356196057051</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">40578526025638</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">437070866115391</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">462984828744084</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">4651908758096</span>
</span><span class="line"><span class="n">most_common_freqs</span>      <span class="o">|</span> <span class="err">{</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span><span class="mi">2</span><span class="n">e</span><span class="o">-</span><span class="mi">06</span><span class="p">,</span>
</span><span class="line"><span class="n">histogram_bounds</span>       <span class="o">|</span> <span class="err">{</span><span class="mi">0</span><span class="p">.</span><span class="mi">00104234321042895</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0141074191778898</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0200657406821847</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0247588600032032</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0284962640143931</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0315022920258343</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">0346860070712864</span><span class="p">,</span><span class="mi">0</span><span class="p">.</span><span class="mi">03</span>
</span><span class="line"><span class="n">correlation</span>            <span class="o">|</span> <span class="o">-</span><span class="mi">0</span><span class="p">.</span><span class="mi">00176553</span>
</span><span class="line"><span class="n">most_common_elems</span>      <span class="o">|</span> <span class="k">NULL</span>
</span><span class="line"><span class="n">most_common_elem_freqs</span> <span class="o">|</span> <span class="k">NULL</span>
</span><span class="line"><span class="n">elem_count_histogram</span>   <span class="o">|</span> <span class="k">NULL</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>So, there are histogram, most common values and etc for <code>x + y</code> expression, and that leads to more accurate selectivity estimation for <code>x + y &lt; 0.01</code>.  However, there is still and 1 order of degree error (641 rows estimated instead of 56).  Could we improve that?  Yes, PostgreSQL have statistics-gathering target parameter which is tunable per column using <a href="https://www.postgresql.org/docs/current/static/sql-altertable.html">ALTER TABLE … SET STATISTICS …</a> command.  Using this command, you may tune size of statistics arrays.</p>

<p>But, uhhhh, in our case we have no column, we have an indexed expression.  That appears to be a problem since there is no documented way to tune statistic target for that…</p>

<p>Nevertheless, it appears to be possible.  There is a gotcha which allows advanced DBAs to do that.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">ALTER</span> <span class="k">INDEX</span> <span class="n">test_idx</span> <span class="k">ALTER</span> <span class="k">COLUMN</span> <span class="n">expr</span> <span class="k">SET</span> <span class="k">STATISTICS</span> <span class="mi">10000</span><span class="p">;</span>
</span><span class="line"><span class="k">ANALYZE</span> <span class="n">test</span><span class="p">;</span>
</span><span class="line">
</span><span class="line"><span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">test</span> <span class="k">WHERE</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">.</span><span class="mi">01</span><span class="p">;</span>
</span><span class="line">                                                    <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">-------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">4</span><span class="p">.</span><span class="mi">96</span><span class="p">..</span><span class="mi">258</span><span class="p">.</span><span class="mi">61</span> <span class="k">rows</span><span class="o">=</span><span class="mi">69</span> <span class="n">width</span><span class="o">=</span><span class="mi">16</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">022</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">074</span> <span class="k">rows</span><span class="o">=</span><span class="mi">56</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="s1">&#39;0.01&#39;</span><span class="p">::</span><span class="n">double</span> <span class="k">precision</span><span class="p">)</span>
</span><span class="line">   <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mi">56</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">4</span><span class="p">.</span><span class="mi">94</span> <span class="k">rows</span><span class="o">=</span><span class="mi">69</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">014</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">014</span> <span class="k">rows</span><span class="o">=</span><span class="mi">56</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">)</span> <span class="o">&lt;</span> <span class="s1">&#39;0.01&#39;</span><span class="p">::</span><span class="n">double</span> <span class="k">precision</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>That works.  When we collect statistic arrays of 10000 size, estimate becomes 69 rows.  It’s only 23% estimation error which is more than good enough for query planning.</p>

<p>But… What the hell is <code>ALTER INDEX ... SET STATISTICS ...</code>?!  There is nothing like this in PostgreSQL documentation!</p>

<p>Let’s understand this situation step by step.</p>

<ol>
  <li><code>ALTER INDEX</code> and <code>ALTER TABLE</code> share the same bison rule.</li>
  <li>Cases when <code>ALTER INDEX</code> is not applicable are filtered runtime.</li>
  <li><code>ALTER INDEX ... SET STATISTICS ...</code> is not forbidden and works the same way as <code>ALTER TABLE ... SET STATISTICS ...</code> does.</li>
  <li>Indexed expressions are internally named as attributes: <code>expr</code>, <code>expr1</code>, <code>expr2</code> …</li>
</ol>

<p>There was <a href="http://www.postgresql.org/message-id/flat/3677.1437057873%40sss.pgh.pa.us">some short discussion</a> about that in pgsql-hackers mailing lists.  The conclusion was that this should be documented, but it’s not yet done.  I also think that we should invent some better syntax for that instead of usage of internal column names.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA["Our Answer to Uber" Talk at United Dev Conf, Minsk]]></title>
    <link href="https://akorotkov.github.io/blog/2017/04/08/uber-answer/"/>
    <updated>2017-04-08T00:20:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2017/04/08/uber-answer/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Today I gave a talk “Our answer to Uber” at United Dev Conf, Minsk.  Slides could be found at <a href="https://www.slideshare.net/AlexanderKorotkov/our-answer-to-uber/">slideshare</a>.  In my talk I attempted to make a review of <a href="https://eng.uber.com/mysql-migration/">Uber’s notes</a> and summarize community efforts to overcome highlighted shortcomings.</p>

<p>United Dev Conf is quite big IT conference with more than 700 attendees.  I’d like to notice that interest in PostgreSQL is quire high.  The room was almost full during my talk.  Also, after the talk I was continuously giving answers to surroundings in about 1 hour.</p>

<p>I think that Minsk is very attractive place for IT events.  There are everything required for it: lovely places for events, good and not expensive hotels, developed infrastructure.  Additionally Belarus introduces 5 days visa-free travel for <a href="http://www.belarus.by/en/press-center/news/belarus-introduces-visa-free-travel-for-80-countries_i_0000052902.html">80 countries</a>, and that made conference attendance much easier for many people.  It would be nice to have PGDay.Minsk one day.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Faceted Search in the Single PostgreSQL Query]]></title>
    <link href="https://akorotkov.github.io/blog/2016/06/17/faceted-search/"/>
    <updated>2016-06-17T14:20:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/06/17/faceted-search/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Faceted search is very popular buzzword nowadays.  In short, faceted search
specialty is that its results are organized per category.  Popular search
engines are receiving special support of faceted search.</p>

<p>Let’s see what PostgreSQL can do in this field.  At first, let’s formalize our
task.  For each category which have matching documents we want to obtain:</p>

<ul>
  <li>Total number of matching documents;</li>
  <li>TOP N matching documents.</li>
</ul>

<p>For sure, it’s possible to query such data using multiple per category SQL
queries.  But we’ll make it in a single SQL query.  That also would be faster
in majority of cases.  The query below implements faceted search over
PostgreSQL mailing lists archives using window functions and CTE.  Usage
of window function is essential while CTE was used for better query readability.</p>

<!--more-->

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>Faceted search SQL query</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
<span class="line-number">35</span>
</pre></td><td class="code"><pre><code class="psql"><span class="line"><span class="cm">/*</span>
</span><span class="line"><span class="cm"> * Select all matching messages, calculate rank within list and total count</span>
</span><span class="line"><span class="cm"> * within list using window functions.</span>
</span><span class="line"><span class="cm"> */</span>
</span><span class="line"><span class="k">WITH</span> <span class="n">msg</span> <span class="k">AS</span> <span class="p">(</span>
</span><span class="line">    <span class="k">SELECT</span>
</span><span class="line">        <span class="n">message_id</span><span class="p">,</span>
</span><span class="line">        <span class="n">subject</span><span class="p">,</span>
</span><span class="line">        <span class="n">list</span><span class="p">,</span>
</span><span class="line">        <span class="n">RANK</span><span class="p">()</span> <span class="k">OVER</span> <span class="p">(</span>
</span><span class="line">            <span class="k">PARTITION</span> <span class="k">BY</span> <span class="n">list</span>
</span><span class="line">            <span class="k">ORDER</span> <span class="k">BY</span> <span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">body_tsvector</span><span class="p">,</span>  <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;index bloat&#39;</span><span class="p">)),</span> <span class="n">id</span>
</span><span class="line">        <span class="p">)</span> <span class="n">rank</span><span class="p">,</span>
</span><span class="line">        <span class="n">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">OVER</span> <span class="p">(</span><span class="k">PARTITION</span> <span class="k">BY</span> <span class="n">list</span><span class="p">)</span> <span class="n">cnt</span>
</span><span class="line">    <span class="k">FROM</span> <span class="n">messages</span>
</span><span class="line">    <span class="k">WHERE</span> <span class="n">body_tsvector</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;index bloat&#39;</span><span class="p">)</span>
</span><span class="line"><span class="p">),</span>
</span><span class="line"><span class="cm">/* Aggregate messages and count per list into json. */</span>
</span><span class="line"><span class="n">lst</span> <span class="k">AS</span> <span class="p">(</span>
</span><span class="line">    <span class="k">SELECT</span>
</span><span class="line">        <span class="n">list</span><span class="p">,</span>
</span><span class="line">        <span class="n">jsonb_build_object</span><span class="p">(</span>
</span><span class="line">            <span class="s1">&#39;count&#39;</span><span class="p">,</span> <span class="n">cnt</span><span class="p">,</span>
</span><span class="line">            <span class="s1">&#39;results&#39;</span><span class="p">,</span> <span class="n">jsonb_agg</span><span class="p">(</span>
</span><span class="line">                <span class="n">jsonb_build_object</span><span class="p">(</span>
</span><span class="line">                    <span class="s1">&#39;message_id&#39;</span><span class="p">,</span> <span class="n">message_id</span><span class="p">,</span>
</span><span class="line">                    <span class="s1">&#39;subject&#39;</span><span class="p">,</span> <span class="n">subject</span>
</span><span class="line">        <span class="p">)))</span> <span class="k">AS</span> <span class="k">data</span>
</span><span class="line">    <span class="k">FROM</span> <span class="n">msg</span>
</span><span class="line">    <span class="k">WHERE</span> <span class="n">rank</span> <span class="o">&lt;=</span> <span class="mf">5</span>
</span><span class="line">    <span class="k">GROUP</span> <span class="k">by</span> <span class="n">list</span><span class="p">,</span> <span class="n">cnt</span>
</span><span class="line"><span class="p">)</span>
</span><span class="line"><span class="cm">/* Aggregate per list data into single json */</span>
</span><span class="line"><span class="k">SELECT</span>  <span class="n">jsonb_object_agg</span><span class="p">(</span><span class="n">list</span><span class="p">,</span> <span class="k">data</span><span class="p">)</span>
</span><span class="line"><span class="k">FROM</span>    <span class="n">lst</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The resulting JSON document contains total count of matching mailing list
messages and TOP 5 relevant messages for each list.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>Faceted search JSON result</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
</pre></td><td class="code"><pre><code class="js"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="s2">&quot;pgsql-admin&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class="line">    <span class="s2">&quot;count&quot;</span><span class="o">:</span> <span class="mi">263</span><span class="p">,</span>
</span><span class="line">    <span class="s2">&quot;results&quot;</span><span class="o">:</span> <span class="p">[</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;CACjxUsMUWkY1Z2K2A6yVdF88GT3xcFw5ofWTR6r1zqLUYu0WzA@mail.gmail.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: Slow planning time&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;dcc563d11001041749w561874f7y6574fb42ab49f850@mail.gmail.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: Finetuning Autovacuum&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;AANLkTikWabMzCRCSWuNLuPizSSQX3YILgJrNZuzgp3yM@mail.gmail.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: blocking automatic vacuum&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;dcc563d10904011631l4058aabew12f3fe4895a072f3@mail.gmail.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: Vacuum Full&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;FE44E0D7EAD2ED4BB2165071DB8E328C03062D9D@egcrc-ex01.egcrc.org&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: postgres bogged down beyond tolerance&quot;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">    <span class="p">]</span>
</span><span class="line">  <span class="p">},</span>
</span><span class="line"><span class="cm">/*................................................................................*/</span>
</span><span class="line">  <span class="s2">&quot;pgsql-advocacy&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class="line">    <span class="s2">&quot;count&quot;</span><span class="o">:</span> <span class="mi">8</span><span class="p">,</span>
</span><span class="line">    <span class="s2">&quot;results&quot;</span><span class="o">:</span> <span class="p">[</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;Pine.LNX.4.33.0310291602220.22178-100000@css120.ihs.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: Press Release&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;20050502203626.GA29791@dcc.uchile.cl&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: [HACKERS] Increased company involvement&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;5d94f7afb26f56652e06ba0657573ef2@biglumber.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Search and archives still out of sync&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;Pine.GSO.4.64.0708010705100.13114@westnet.com&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: postgresql publication&quot;</span><span class="p">},</span>
</span><span class="line">      <span class="p">{</span><span class="s2">&quot;message_id&quot;</span><span class="o">:</span> <span class="s2">&quot;20070801151739.GF6165@alvh.no-ip.org&quot;</span><span class="p">,</span> <span class="s2">&quot;subject&quot;</span><span class="o">:</span> <span class="s2">&quot;Re: postgresql publication&quot;</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">    <span class="p">]</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>In the plan of this query we can see that <code>message_body_idx</code> GIN index is
scanned only once, and this is great.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>Plan of faceted search SQL query</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
</pre></td><td class="code"><pre><code class="psql"><span class="line">                                                                   <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">---------------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="k">Aggregate</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">2369.50..2369.51</span> <span class="k">rows</span><span class="o">=</span><span class="mf">1</span> <span class="n">width</span><span class="o">=</span><span class="mf">114</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">34.232..34.232</span> <span class="k">rows</span><span class="o">=</span><span class="mf">1</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">CTE</span> <span class="n">msg</span>
</span><span class="line"> <span class="o">-&gt;</span>  <span class="n">WindowAgg</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">2087.93..2354.30</span> <span class="k">rows</span><span class="o">=</span><span class="mf">491</span> <span class="n">width</span><span class="o">=</span><span class="mf">336</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">30.925..33.087</span> <span class="k">rows</span><span class="o">=</span><span class="mf">2486</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">       <span class="o">-&gt;</span>  <span class="n">WindowAgg</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">2087.93..2222.96</span> <span class="k">rows</span><span class="o">=</span><span class="mf">491</span> <span class="n">width</span><span class="o">=</span><span class="mf">336</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">30.716..32.020</span> <span class="k">rows</span><span class="o">=</span><span class="mf">2486</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">             <span class="o">-&gt;</span>  <span class="n">Sort</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">2087.93..2089.16</span> <span class="k">rows</span><span class="o">=</span><span class="mf">491</span> <span class="n">width</span><span class="o">=</span><span class="mf">336</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">30.711..30.838</span> <span class="k">rows</span><span class="o">=</span><span class="mf">2486</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">                   <span class="n">Sort</span> <span class="k">Key</span><span class="p">:</span> <span class="n">messages</span><span class="mf">.</span><span class="n">list</span><span class="p">,</span> <span class="p">(</span><span class="n">ts_rank_cd</span><span class="p">(</span><span class="n">messages</span><span class="mf">.</span><span class="n">body_tsvector</span><span class="p">,</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;index bloat&#39;</span><span class="o">::</span><span class="nb">text</span><span class="p">))),</span> <span class="n">messages</span><span class="mf">.</span><span class="n">id</span>
</span><span class="line">                   <span class="n">Sort</span> <span class="n">Method</span><span class="p">:</span> <span class="n">quicksort</span>  <span class="n">Memory</span><span class="p">:</span> <span class="mf">582</span><span class="n">kB</span>
</span><span class="line">                   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">messages</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">48.05..2065.98</span> <span class="k">rows</span><span class="o">=</span><span class="mf">491</span> <span class="n">width</span><span class="o">=</span><span class="mf">336</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">3.037..24.345</span> <span class="k">rows</span><span class="o">=</span><span class="mf">2486</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">                         <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">body_tsvector</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;index bloat&#39;</span><span class="o">::</span><span class="nb">text</span><span class="p">))</span>
</span><span class="line">                         <span class="n">Heap</span> <span class="n">Blocks</span><span class="p">:</span> <span class="n">exact</span><span class="o">=</span><span class="mf">2044</span>
</span><span class="line">                         <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">message_body_idx</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">0.00..47.93</span> <span class="k">rows</span><span class="o">=</span><span class="mf">491</span> <span class="n">width</span><span class="o">=</span><span class="mf">0</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">2.723..2.723</span> <span class="k">rows</span><span class="o">=</span><span class="mf">2486</span> <span class="n">loo</span>
</span><span class="line">                               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">body_tsvector</span> <span class="o">@@</span> <span class="n">plainto_tsquery</span><span class="p">(</span><span class="s1">&#39;index bloat&#39;</span><span class="o">::</span><span class="nb">text</span><span class="p">))</span>
</span><span class="line">   <span class="n">CTE</span> <span class="n">lst</span>
</span><span class="line"> <span class="o">-&gt;</span>  <span class="n">HashAggregate</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">12.69..13.69</span> <span class="k">rows</span><span class="o">=</span><span class="mf">67</span> <span class="n">width</span><span class="o">=</span><span class="mf">540</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">34.090..34.133</span> <span class="k">rows</span><span class="o">=</span><span class="mf">14</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">       <span class="k">Group</span> <span class="k">Key</span><span class="p">:</span> <span class="n">msg</span><span class="mf">.</span><span class="n">list</span><span class="p">,</span> <span class="n">msg</span><span class="mf">.</span><span class="n">cnt</span>
</span><span class="line">       <span class="o">-&gt;</span>  <span class="n">CTE</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">msg</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">0.00..11.05</span> <span class="k">rows</span><span class="o">=</span><span class="mf">164</span> <span class="n">width</span><span class="o">=</span><span class="mf">540</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">30.928..33.879</span> <span class="k">rows</span><span class="o">=</span><span class="mf">68</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line">             <span class="n">Filter</span><span class="p">:</span> <span class="p">(</span><span class="n">rank</span> <span class="o">&lt;=</span> <span class="mf">5</span><span class="p">)</span>
</span><span class="line">             <span class="k">Rows</span> <span class="n">Removed</span> <span class="k">by</span> <span class="n">Filter</span><span class="p">:</span> <span class="mf">2418</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">CTE</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">lst</span>  <span class="p">(</span><span class="k">cost</span><span class="o">=</span><span class="mf">0.00..1.34</span> <span class="k">rows</span><span class="o">=</span><span class="mf">67</span> <span class="n">width</span><span class="o">=</span><span class="mf">114</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="nb">time</span><span class="o">=</span><span class="mf">34.092..34.140</span> <span class="k">rows</span><span class="o">=</span><span class="mf">14</span> <span class="n">loops</span><span class="o">=</span><span class="mf">1</span><span class="p">)</span>
</span><span class="line"> <span class="n">Planning</span> <span class="nb">time</span><span class="p">:</span> <span class="mf">0.380</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="nb">time</span><span class="p">:</span> <span class="mf">34.357</span> <span class="n">ms</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Thus, it appears that nothing prevents you from implementing trendy kinds of
searches using old good SQL and powerful features of PostgreSQL including:
fulltext search, JSON support, window functions etc.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[RuntimeAppend in Pg_pathman: Achievements and New Challenges]]></title>
    <link href="https://akorotkov.github.io/blog/2016/06/15/pg_pathman-runtime-append/"/>
    <updated>2016-06-15T15:00:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/06/15/pg_pathman-runtime-append/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Dealing with partitioned tables we can’t always select relevant partitions
during query planning.  Naturally, during query planning you can’t know values
which come from subquery or outer part of nested loop join.  Nevertheless, it
would be ridiculous to scan all the partitions in such cases.</p>

<p>This is why my Postgres Professional colleague Dmitry Ivanov developed a
new custom executor node for pg_pathman: RuntimeAppend.  This node behaves
like regular Append node: it contains set of children Nodes which should be
appended.  However, RuntimeAppend have one distinction: each run it selects
only relevant children to append basing on parameter values.</p>

<!--more-->

<p>Let’s consider example: join of <code>journal</code> table which contains row per each
30 seconds of year partitioned by day, and <code>q</code> table which refers 1000 random
rows of <code>journal</code> table.  Without RuntimeAppend optimizer selects Hash Join
plan.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>Regular Append: Hash Join</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">q</span> <span class="k">JOIN</span> <span class="n">journal</span> <span class="n">j</span> <span class="k">ON</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="n">j</span><span class="p">.</span><span class="n">dt</span><span class="p">;</span>
</span><span class="line">                                                          <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">-------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Hash</span> <span class="k">Join</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">27</span><span class="p">.</span><span class="mi">50</span><span class="p">..</span><span class="mi">25442</span><span class="p">.</span><span class="mi">51</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">56</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">479</span><span class="p">..</span><span class="mi">252</span><span class="p">.</span><span class="mi">506</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="n">Hash</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">j</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Append</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">21463</span><span class="p">.</span><span class="mi">01</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1051201</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">005</span><span class="p">..</span><span class="mi">152</span><span class="p">.</span><span class="mi">258</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1051201</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">journal_1</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">58</span><span class="p">.</span><span class="mi">80</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">004</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">247</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">journal_2</span> <span class="n">j_1</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">58</span><span class="p">.</span><span class="mi">80</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">208</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">journal_3</span> <span class="n">j_2</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">58</span><span class="p">.</span><span class="mi">80</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">197</span> <span class="k">rows</span><span class="o">=</span><span class="mi">2880</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line"><span class="p">...............................................................................................................................</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">journal_366</span> <span class="n">j_365</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">1</span><span class="p">.</span><span class="mi">01</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Hash</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">15</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">15</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">185</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">185</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">         <span class="n">Buckets</span><span class="p">:</span> <span class="mi">1024</span>  <span class="n">Batches</span><span class="p">:</span> <span class="mi">1</span>  <span class="n">Memory</span> <span class="k">Usage</span><span class="p">:</span> <span class="mi">48</span><span class="n">kB</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">q</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">15</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">074</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">29</span><span class="p">.</span><span class="mi">262</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">256</span><span class="p">.</span><span class="mi">337</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">374</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The Hash Join execution takes 256 milliseconds for execution and 29 milliseconds
for planning.  Relatively high planning time is expected because all the
partitions are present in plan.  It’s surprising that optimizer didn’t select
Nested Loop join.  Let’s force it to do so by <code>enable_hashjoin = off</code> and
<code>enable_mergejoin = off</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>Regular Append: Nested Loop</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">q</span> <span class="k">JOIN</span> <span class="n">journal</span> <span class="n">j</span> <span class="k">ON</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="n">j</span><span class="p">.</span><span class="n">dt</span><span class="p">;</span>
</span><span class="line">                                                                      <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">------------------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Nested</span> <span class="n">Loop</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">170817</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">56</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">1</span><span class="p">.</span><span class="mi">091</span><span class="p">..</span><span class="mi">452</span><span class="p">.</span><span class="mi">658</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">q</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">15</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">006</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">158</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Append</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">167</span><span class="p">.</span><span class="mi">14</span> <span class="k">rows</span><span class="o">=</span><span class="mi">366</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">218</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">438</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_1_dt_idx</span> <span class="k">on</span> <span class="n">journal_1</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_2_dt_idx</span> <span class="k">on</span> <span class="n">journal_2</span> <span class="n">j_1</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_3_dt_idx</span> <span class="k">on</span> <span class="n">journal_3</span> <span class="n">j_2</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line"><span class="p">......................................................................................................................................................</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_366_dt_idx</span> <span class="k">on</span> <span class="n">journal_366</span> <span class="n">j_365</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">12</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">15</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">29</span><span class="p">.</span><span class="mi">922</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">456</span><span class="p">.</span><span class="mi">140</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">737</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The Nested Loop join takes 456 milliseconds to execute.  This is even worse.
But this is understandable because we have to scan each partition of <code>journal</code>
for each row of <code>q</code>.</p>

<p>Finally, let’s enable RuntimeAppend.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>RuntimeAppend</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">EXPLAIN</span> <span class="k">ANALYZE</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">q</span> <span class="k">JOIN</span> <span class="n">journal</span> <span class="n">j</span> <span class="k">ON</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span> <span class="o">=</span> <span class="n">j</span><span class="p">.</span><span class="n">dt</span><span class="p">;</span>
</span><span class="line">                                                                   <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">------------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Nested</span> <span class="n">Loop</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">481</span><span class="p">.</span><span class="mi">67</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">56</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">041</span><span class="p">..</span><span class="mi">9</span><span class="p">.</span><span class="mi">911</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">q</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">15</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">width</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">005</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">079</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1000</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Custom</span> <span class="n">Scan</span> <span class="p">(</span><span class="n">RuntimeAppend</span><span class="p">)</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1000</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_330_dt_idx</span> <span class="k">on</span> <span class="n">journal_330</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_121_dt_idx</span> <span class="k">on</span> <span class="n">journal_121</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">004</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">004</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_37_dt_idx</span> <span class="k">on</span> <span class="n">journal_37</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line"><span class="p">................................................................................................................................................</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">journal_355_dt_idx</span> <span class="k">on</span> <span class="n">journal_355</span> <span class="n">j</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">28</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">49</span><span class="p">)</span> <span class="p">(</span><span class="n">actual</span> <span class="n">time</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">003</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">loops</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
</span><span class="line">               <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">dt</span> <span class="o">=</span> <span class="n">q</span><span class="p">.</span><span class="n">dt</span><span class="p">)</span>
</span><span class="line"> <span class="n">Planning</span> <span class="n">time</span><span class="p">:</span> <span class="mi">30</span><span class="p">.</span><span class="mi">775</span> <span class="n">ms</span>
</span><span class="line"> <span class="n">Execution</span> <span class="n">time</span><span class="p">:</span> <span class="mi">8</span><span class="p">.</span><span class="mi">615</span> <span class="n">ms</span>
</span><span class="line"><span class="p">(</span><span class="mi">687</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The Nested Loop join with RuntimeAppend takes only about 9 milliseconds
to execute!  Such fast execution is possible thanks to RuntimeAppend scans only
one relevant partition of <code>journal</code> for each row of <code>q</code>.</p>

<p>Nevertheless, all the partitions are present in plan and planning time is still
quite high.  This relatively high planning time could be not so significant
for prepared statements or long OLAP queries.</p>

<p>However, long planning time appears to be not the only problem.  We run a
benchmark when RuntimeAppend node returns just a few rows in prepared statement.
Despite high planning time doesn’t affect prepared statements, TPS was few
time slower than it was without partitioning.  After running perf, we got this
<a href="https://akorotkov.github.io/images/runtimeappend_flamegraph.svg">flamegraph</a>.  This flamegraph shows that
we spend very significant time for locking and unlocking every partition.
Naturally, locking 365 partitions isn’t using fast-path locking and appears to
be significant overhead.</p>

<p>Thus, we see how huge benefit could runtime partition selection have.  However,
in current design having all the partitions in plan cause high overhead.
Solution could be found in redesigning partition locking.  We are researching
this problem now.  It’s likely this problem can’t be solved in the boundaries
of extension and proper solution requires hacking of PostgreSQL core.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Drawing Graphs Directly in Psql]]></title>
    <link href="https://akorotkov.github.io/blog/2016/06/09/psql-graph/"/>
    <updated>2016-06-09T16:45:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/06/09/psql-graph/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>For people who are actively working with psql, it frequently happens that you
want to draw graph for the table you’re currently seeing.  Typically, it means a
cycle of actions including: exporting data, importing it into graph drawing tool
and drawing graph itself.  It appears that this process could be automated:
graph could be drawn by typing a single command directly in psql.  See an
example on the screenshot below.</p>

<p><img class="no-border center" src="https://akorotkov.github.io/images/screen-psql-iterm-graph.png" width="689" height="958" /></p>

<!--more-->

<p>It might seem like a magic, but actually there is absolutely no magic.  iTerm2
supports <a href="https://www.iterm2.com/documentation-images.html">image inlining</a>
since version 3 which is currently beta.  Thus, if we put image surrounded
with corresponding escape sequences it will appear in the terminal.  From psql
side we need to redirect output to the script which would do it.  We can define
a macro for simplifying this like in <a href="https://akorotkov.github.io/blog/2015/08/26/psql-gdb-attach/">one of my previous posts</a>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="err">\</span><span class="k">set</span> <span class="n">graph</span> <span class="s1">&#39;\\g |pg_graph&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>And finally we need a pg_graph script which parses psql output, draws graph and
puts it into stdout.  I <a href="https://gist.github.com/akorotkov/2c7011ac30de27b9c5631f1bf418f0a1">wrote one</a>
using Python and matplotlib.  It recognizes first column as series of X-values
and rest of columns as series of Y-values.  If first column contains only
decimal values it draws a plot chart, otherwise it draws a bar chart.</p>

<p>Thereby, it’s not hard to teach psql to do more things.  Also, we can consider
some improvements to psql including:</p>

<ul>
  <li>Add output format option for <code>\g</code> which would make it easier to parse psql
output from scripts;</li>
  <li>Provide elegant way to pass parameters into psql macro.</li>
</ul>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[PostgreSQL Scalability: Towards Millions TPS]]></title>
    <link href="https://akorotkov.github.io/blog/2016/05/09/scalability-towards-millions-tps/"/>
    <updated>2016-05-09T12:50:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/05/09/scalability-towards-millions-tps/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>PostgreSQL scalability on multicore and multisocket machines became a subject
of optimization long time ago once such machines became widely used.
<a href="http://suckit.blog.hu/2009/09/29/postgresql_history">This blog post</a> shows
brief history of vertical scalability improvements between versions 8.0 and 8.4.
PostgreSQL 9.2 had very noticeable scalability improvement.  Thanks to fast
path locking and other optimizations it becomes possible to achieve
<a href="http://rhaas.blogspot.ru/2012/04/did-i-say-32-cores-how-about-64.html">more than 350 000 TPS in select-only pgbench test</a>.  The latest stable release PostgreSQL 9.5 also
contain significant scalability advancements including LWLock improvement which
allows achieving <a href="http://amitkapila16.blogspot.ru/2015/01/read-scalability-in-postgresql-95.html">about 400 000 TPS in select-only pgbench test</a>.</p>

<p>Postgres Professional company also became involved into scalability
optimization.  In partnership with IBM we researched PostgreSQL scalability on
modern Power8 servers.  The results of this research was published in
<a href="https://habrahabr.ru/company/postgrespro/blog/270827/">popular Russian blog habrahabr</a>
(<a href="https://translate.google.com/translate?sl=ru&amp;tl=en&amp;js=y&amp;prev=_t&amp;hl=ru&amp;ie=UTF-8&amp;u=https%3A%2F%2Fhabrahabr.ru%2Fcompany%2Fpostgrespro%2Fblog%2F270827%2F&amp;edit-text=&amp;act=url">Google translated version</a>).
As brief result of this research we identify two ways to improve PostgreSQL
scalability on Power8:</p>

<ol>
  <li>Implement Pin/UnpinBuffer() using CAS operations instead of
buffer header spinlock;</li>
  <li>Optimize LWLockAttemptLock() in assembly to make fewer loops for changing
lwlock state.</li>
</ol>

<p>The optimization #1 appears to give huge benefit on big Intel servers as well,
while optimization #2 is Power-specific.  After long rounds of optimization,
cleaning and testing #1 was finally
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=48354581">committed by Andres Freund</a>.</p>

<p><img class="no-border center 2x" src="https://akorotkov.github.io/images/scalability.png" width="720" height="576" /></p>

<!--more-->

<p>On the graph above, following PostgreSQL versions were compared:</p>

<ol>
  <li>9.5.2 release – peak is 540 000 TPS with 60 clients,</li>
  <li>9.6 master (more precisely <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=59455018">59455018</a>) – peak is 1 064 000 TPS
with 110 clients,</li>
  <li>9.6 master where all PGXACTs were full cacheline aligned – peak is
1 722 000 TPS with 200 clients.</li>
</ol>

<p>Alignment issues worth some explanation.  Initially, I complained performance
regression introduced by commit <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=5364b357">5364b357</a> which increases number of
clog buffers.  That was strange by itself, because read-only benchmark shouldn’t
lookup to clog thanks to hint bits.  As expected it appears that clog buffers
don’t really affect read-only performance directly, <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=5364b357">5364b357</a> just
changed layout of shared memory structures.</p>

<p>It appears that read-only benchmark became very sensitive to layout of shared
memory structures. As result performance has significant variety depending on
shared_buffers, max_connections and other options which influence shared memory
distribution.  When I gave Andres access to that big machine, he very quickly
find a way to take care about performance irregularity:
<a href="http://www.postgresql.org/message-id/20160411214029.ce3fw6zxim5k6a2r@alap3.anarazel.de">make all PGXACTs full cacheline aligned</a>.
Without this patch SnapshotResetXmin() dirties processor cache containing
multiple PGXACTs.  With this patch SnapshotResetXmin() dirties cacheline with
only single PGXACT.  Thus, GetSnapshotData() have much less cache misses.
That was surprising and good lesson for me.  I knew that alignment influence
performance, but I didn’t expect this influence to be so huge. PGXACT cacheline
alignment issue was discovered after feature freeze for 9.6.  That
means it would be subject for 9.7 development.  Nevertheless, 9.6 have very
noticeable scalability improvement.</p>

<p>Therefore, PostgreSQL single instance delivers more than 1 million TPS and one
could say, that PostgreSQL opens a new era of millions TPS.</p>

<p>P.S. I’d like to thank:</p>

<ul>
  <li>Andres Freund, so-author and committer of patch;</li>
  <li>My PostgresPro colleagues: Dmitry Vasilyev who run a lot of benchmarks,
YUriy Zhuravlev who wrote original proof of concept of this patch;</li>
  <li>Dilip Kumar and Robert Haas who helped with testing.</li>
</ul>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Extensible Access Methods Are Committed to 9.6]]></title>
    <link href="https://akorotkov.github.io/blog/2016/04/06/extensible-access-methods/"/>
    <updated>2016-04-06T15:26:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/04/06/extensible-access-methods/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>PostgreSQL 9.6 receives suitable support of extensible index access methods.
And that’s good news because Postgres was initially designed to support it.</p>

<blockquote>
  <p>“It is imperative that a user be able to construct new access methods to 
provide efficient access to instances of nontraditional base types”</p>

  <p>Michael Stonebraker, Jeff Anton, Michael Hirohama.
Extendability in POSTGRES , IEEE Data Eng. Bull. 10 (2) pp.16-23, 1987</p>
</blockquote>

<p>That was a huge work which consists of multiple steps.</p>

<!--more-->

<ol>
  <li>Rework access method interface so that access method internals are hidden
from SQL level to C level.  Besides help for custom access methods support,
this refactoring is good by itself.<br />
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=65c5fcd3">Committed</a> by Tom Lane.</li>
  <li><code>CREATE ACCESS METHOD</code> command which provides legal way for insertion into
pg_am with support of dependencies and pg_dump/pg_restore.
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=473b9328">Committed</a> by Alvaro Herrera.</li>
  <li>Generic WAL interface which provides custom access methods the way to be
WAL-logged.  Each built-in access method has its own type of WAL records.
But custom access method shouldn’t because it could affect reliability.
Generic WAL records represent difference between pages in general way as
result of per-byte comparison of original and modified images of the page.
For sure, it is not as efficient as own type of WAL records, but there is
no choice under restrictions we have.
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=65578341">Committed</a> by Teodor Sigaev.</li>
  <li>Bloom contrib module which is example of custom index access method which
uses generic WAL interface.  This contrib is essential for testing
infrastructure described above.  Also, this access method could be useful by
itself.
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=9ee014fc">Committed</a> by Teodor Sigaev.</li>
</ol>

<p>I am very thankful for the efforts of committers and reviewers who make it
possible to include these features into PostgreSQL.</p>

<p>However, end users don’t really care about this infrastructure.  They do care
about features we can provide on the base of this infrastructure.  Actually,
we would be able to have index access methods which are:</p>

<ul>
  <li>Too hard to add to PostgreSQL core.  For instance, we presented
<a href="https://wiki.postgresql.org/images/2/25/Full-text_search_in_PostgreSQL_in_milliseconds-extended-version.pdf">fast FTS</a>
in 2012.  We have 2 of 4 GIN features committed to core.  And it seems to be
very long way to have rest of features in core.  But since 9.6 we would
provide it as an extension.</li>
  <li>Not patent free.  There are some interesting data structures which are
covered by patents (Fractal Tree index, for example).  This is why they
couldn’t be added to PostgreSQL core.  Since 9.6, they could be provided
without fork.</li>
</ul>

<p>Also, I consider this work as an approach (together with FDW) to pluggable
storage engines.  I will speak about this during my
<a href="http://www.pgcon.org/2016/schedule/events/920.en.html">talk at PGCon 2016</a>.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Monitoring Wait Events in PostgreSQL 9.6]]></title>
    <link href="https://akorotkov.github.io/blog/2016/03/25/wait_monitoring_9_6/"/>
    <updated>2016-03-25T18:00:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/03/25/wait_monitoring_9_6/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Recently Robert Haas has <a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=53be0b1a">committed</a> a patch which allows
seeing some more detailed information about current wait event of the process.
In particular, user will be able to see if process is waiting for heavyweight
lock, lightweight lock (either individual or tranche) or buffer pin.  The full
list of wait events is available in the
<a href="http://www.postgresql.org/docs/devel/static/monitoring-stats.html#WAIT-EVENT-TABLE">documentation</a>.
Hopefully, it will be more wait events in further releases.</p>

<p>It’s nice to see current wait event of the process, but just one snapshot is
not very descriptive and definitely not enough to do any conclusion.  But
we can use sampling for collecting suitable statistics.  This is why I’d like
to present <a href="https://github.com/postgrespro/pg_wait_sampling">pg_wait_sampling</a>
which automates gathering sampling statistics of wait events.  pg_wait_sampling
enables you to gather statistics for graphs like the one below.</p>

<p><img class="no-border" src="https://akorotkov.github.io/images/wait_monitoring.png" width="629" height="638" /></p>

<!--more-->

<p>Let me explain you how did I draw this graph. pg_wait_sampling samples wait
events into two destinations: history and profile.  History is an in-memory
ring buffer and profile is an in-memory hash table with accumulated statistics.
We’re going to use the second one to see insensitivity of wait events over time
periods.</p>

<p>At first, let’s create table for accumulated statistics.  I’m doing these
experiments on my laptop, and for the simplicity this table will live in the
instance under monitoring.  But note, that such table could live on the another
server.  I’d even say it’s preferable to place such data to another server.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">profile_log</span> <span class="p">(</span>
</span><span class="line">    <span class="n">ts</span>         <span class="k">timestamp</span><span class="p">,</span>
</span><span class="line">    <span class="n">event_type</span> <span class="nb">text</span><span class="p">,</span>
</span><span class="line">    <span class="n">event</span>      <span class="nb">text</span><span class="p">,</span>
</span><span class="line">    <span class="k">count</span>      <span class="nb">int8</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Secondly, I wrote a function to copy data from pg_wait_sampling_profile view to
profile_log table and clean profile data.  This function returns number of
rows inserted into profile_log table.  Also, this function discards pid number
and groups data by wait event.  And this is not necessary needed to be so.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">OR</span> <span class="k">REPLACE</span> <span class="k">FUNCTION</span> <span class="n">write_profile_log</span><span class="p">()</span> <span class="k">RETURNS</span> <span class="nb">integer</span> <span class="k">AS</span> <span class="err">$$</span>
</span><span class="line"><span class="k">DECLARE</span>
</span><span class="line">    <span class="k">result</span> <span class="nb">integer</span><span class="p">;</span>
</span><span class="line"><span class="k">BEGIN</span>
</span><span class="line">    <span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">profile_log</span>
</span><span class="line">        <span class="k">SELECT</span> <span class="k">current_timestamp</span><span class="p">,</span> <span class="n">event_type</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="k">SUM</span><span class="p">(</span><span class="k">count</span><span class="p">)</span>
</span><span class="line">        <span class="k">FROM</span> <span class="n">pg_wait_sampling_profile</span>
</span><span class="line">        <span class="k">WHERE</span> <span class="n">event</span> <span class="k">IS</span> <span class="k">NOT</span> <span class="k">NULL</span>
</span><span class="line">        <span class="k">GROUP</span> <span class="k">BY</span> <span class="n">event_type</span><span class="p">,</span> <span class="n">event</span><span class="p">;</span>
</span><span class="line">    <span class="k">GET</span> <span class="k">DIAGNOSTICS</span> <span class="k">result</span> <span class="o">=</span> <span class="k">ROW_COUNT</span><span class="p">;</span>
</span><span class="line">    <span class="n">PERFORM</span> <span class="n">pg_wait_sampling_reset_profile</span><span class="p">();</span>
</span><span class="line">    <span class="k">RETURN</span> <span class="k">result</span><span class="p">;</span>
</span><span class="line"><span class="k">END</span>
</span><span class="line"><span class="err">$$</span>
</span><span class="line"><span class="k">LANGUAGE</span> <span class="s1">&#39;plpgsql&#39;</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>And then I run psql session where setup watch of this function.  Monitoring of
our system is started.  For real usage it’s better to schedule this command
using cron or something.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="n">smagen</span><span class="o">@</span><span class="n">postgres</span><span class="o">=#</span> <span class="k">SELECT</span> <span class="n">write_profile_log</span><span class="p">();</span>
</span><span class="line"> <span class="n">write_profile_log</span>
</span><span class="line"><span class="c1">-------------------</span>
</span><span class="line">                 <span class="mi">0</span>
</span><span class="line"><span class="p">(</span><span class="mi">1</span> <span class="k">row</span><span class="p">)</span>
</span><span class="line">
</span><span class="line"><span class="n">smagen</span><span class="o">@</span><span class="n">postgres</span><span class="o">=#</span> <span class="err">\</span><span class="n">watch</span> <span class="mi">10</span>
</span><span class="line"><span class="n">Fri</span> <span class="n">Mar</span> <span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">09</span> <span class="mi">2016</span> <span class="p">(</span><span class="k">every</span> <span class="mi">10</span><span class="n">s</span><span class="p">)</span>
</span><span class="line">
</span><span class="line"> <span class="n">write_profile_log</span>
</span><span class="line"><span class="c1">-------------------</span>
</span><span class="line">                 <span class="mi">0</span>
</span><span class="line"><span class="p">(</span><span class="mi">1</span> <span class="k">row</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We can see that write_profile_log returns 0.  That means we didn’t insert
anything to profile_log.  And this is right because system is not under load
now.  Let us create some load using pgbench.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="nv">$ </span>pgbench -i -s <span class="m">10</span> postgres
</span><span class="line"><span class="nv">$ </span>pgbench -j <span class="m">10</span> -c <span class="m">10</span> -M prepared -T <span class="m">60</span> postgres
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>In the parallel session we can see that write_profile_log starts to insert some
data to profile_log table.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="n">Fri</span> <span class="n">Mar</span> <span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">04</span><span class="p">:</span><span class="mi">19</span> <span class="mi">2016</span> <span class="p">(</span><span class="k">every</span> <span class="mi">10</span><span class="n">s</span><span class="p">)</span>
</span><span class="line"> <span class="n">write_profile_log</span>
</span><span class="line"><span class="c1">-------------------</span>
</span><span class="line">                 <span class="mi">9</span>
</span><span class="line"><span class="p">(</span><span class="mi">1</span> <span class="k">row</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Finally, let’s examine the profile_log table.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">profile_log</span><span class="p">;</span>
</span><span class="line">             <span class="n">ts</span>             <span class="o">|</span>  <span class="n">event_type</span>   <span class="o">|</span>       <span class="n">event</span>       <span class="o">|</span> <span class="k">count</span>
</span><span class="line"><span class="c1">----------------------------+---------------+-------------------+-------</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="k">Lock</span>          <span class="o">|</span> <span class="n">tuple</span>             <span class="o">|</span>    <span class="mi">41</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockTranche</span> <span class="o">|</span> <span class="n">lock_manager</span>      <span class="o">|</span>     <span class="mi">1</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockTranche</span> <span class="o">|</span> <span class="n">buffer_content</span>    <span class="o">|</span>    <span class="mi">68</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockTranche</span> <span class="o">|</span> <span class="n">wal_insert</span>        <span class="o">|</span>     <span class="mi">3</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockNamed</span>   <span class="o">|</span> <span class="n">WALWriteLock</span>      <span class="o">|</span>    <span class="mi">68</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="k">Lock</span>          <span class="o">|</span> <span class="n">transactionid</span>     <span class="o">|</span>   <span class="mi">331</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockNamed</span>   <span class="o">|</span> <span class="n">ProcArrayLock</span>     <span class="o">|</span>     <span class="mi">8</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockNamed</span>   <span class="o">|</span> <span class="n">WALBufMappingLock</span> <span class="o">|</span>     <span class="mi">5</span>
</span><span class="line"> <span class="mi">2016</span><span class="o">-</span><span class="mi">03</span><span class="o">-</span><span class="mi">25</span> <span class="mi">14</span><span class="p">:</span><span class="mi">03</span><span class="p">:</span><span class="mi">19</span><span class="p">.</span><span class="mi">286394</span> <span class="o">|</span> <span class="n">LWLockNamed</span>   <span class="o">|</span> <span class="n">CLogControlLock</span>   <span class="o">|</span>     <span class="mi">1</span>
</span><span class="line"><span class="p">........................................................................</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>How to interpret these data?  In the first row we can see that count for tuple
lock for 14:03:19 is 41.  The pg_wait_sampling collector samples wait event
every 10 ms while write_profile_log function writes snapshot of profile every
10 s.  Thus, it was 1000 samples during this period.  Taking into account that
it was 10 backends serving pgbench, we can read the first row as “from 14:03:09
to 14:03:19 backends spend about 0.41% of time in waiting for tuple lock”.</p>

<p>That’s it.  This blog post shows how you can setup a wait event monitoring
of your database using
<a href="https://github.com/postgrespro/pg_wait_sampling">pg_wait_sampling</a>
extension with PostgreSQL 9.6.  This example was given just for introduction and
it is simplified in many ways.  But experienced DBAs would easily adopt it for
their setups.</p>

<p>P.S. Every monitoring has some overhead.  Overhead of wait monitoring was
subject of hot debates in mailing lists.  This is why features like exposing
wait events parameters and measuring each wait event individually are not yet
in 9.6.  But sampling also has overhead.  I hope pg_wait_sampling would be a
start point to show on comparison that other approaches are not that bad, and
finally we would have something way more advanced for 9.7.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Pg_pathman UPDATE and DELETE Support and Benchmark]]></title>
    <link href="https://akorotkov.github.io/blog/2016/03/18/pg_pathman-update-delete-benchmark/"/>
    <updated>2016-03-18T12:20:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/03/18/pg_pathman-update-delete-benchmark/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>Recently <a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a> receives
support of UPDATE and DELETE queries.  Because of some specialties of PostgreSQL
query planner hooks, UPDATE and DELETE planning is accelerated only when only
one partition is touched by query.  Other way, regular slow inheritance query 
planning is used.  However, case when UPDATE and DELETE touches only one
partition seems to be most common and most needing optimization.</p>

<p>Also, I’d like to share some benchmark. This benchmark consists of operations
on journal table with about 1 M records for year partitioned by day.  For sure,
this is kind of toy example, because nobody will split so small amount of data
into so many partitions.  But it is still good to see partitioning overhead.
Performance of following operations was compared:</p>

<ul>
  <li>Select single row by its timestamp,</li>
  <li>Select data for whole day (whole one partition),</li>
  <li>Insert one row with random timestamp,</li>
  <li>Update one row with random timestamp.</li>
</ul>

<p>The following partitioning methods were compared:</p>

<ul>
  <li>Single table, no partitioning,</li>
  <li>pg_partman extension,</li>
  <li>pg_pathman extension.</li>
</ul>

<p>Benchmarks were done on 2 x Intel Xeon CPU X5675 @ 3.07GHz, 24 GB of memory
server with fsync = off in 10 threads. See the results below.</p>

<p><img class="no-border" src="https://akorotkov.github.io/images/pg_pathman_benchmarks.png" width="676" height="445" /></p>

<table>
  <thead>
    <tr>
      <th>Test name</th>
      <th style="text-align: right">single table, TPS</th>
      <th style="text-align: right">pg_partman, TPS</th>
      <th style="text-align: right">pg_pathman, TPS</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Select one row</td>
      <td style="text-align: right">47973</td>
      <td style="text-align: right">1084</td>
      <td style="text-align: right">41775</td>
    </tr>
    <tr>
      <td>Select whole one partition</td>
      <td style="text-align: right">2302</td>
      <td style="text-align: right">704</td>
      <td style="text-align: right">2556</td>
    </tr>
    <tr>
      <td>Insert one row</td>
      <td style="text-align: right">34401</td>
      <td style="text-align: right">7969</td>
      <td style="text-align: right">25859</td>
    </tr>
    <tr>
      <td>Update one row</td>
      <td style="text-align: right">32769</td>
      <td style="text-align: right">202</td>
      <td style="text-align: right">29289</td>
    </tr>
  </tbody>
</table>

<!--more-->

<p>I can make following highlights for these results.</p>

<ul>
  <li>pg_pathman is dramatically faster than pg_partman, because pg_pathman uses
planner hooks for faster query planning while pg_partman uses built-in
inheritance mechanism.</li>
  <li>When selecting or updating a single row, pg_pathman is almost as fast as plain
table.  The difference for insertion of single row is slightly bigger because
trigger is used for that.</li>
  <li>The difference between pg_partman and pg_pathman when selecting the whole
partition is not as dramatic as when selecting the one row.  This is why
planning time becomes less substantial in comparison with execution time.</li>
  <li>Inserting random rows with pg_pathman is still much faster than with
pg_partman while both of them use trigger on parent relation.  However,
pg_pathman uses fast C-function for partition selection.</li>
  <li>Selecting the whole partition when table is partitioned by pg_pathman is
slightly faster than selecting same rows from plain table.  This is because
sequential scan was used for selecting whole partition while index scan
was used for selecting part of plain table.  When among of data is big and
doesn’t fit cache this difference is expected to be much more.</li>
</ul>

<p>See <a href="https://gist.github.com/akorotkov/0d558a6e1b5ea176813a">this gist</a> for
SQL-scripts used for benchmarking.</p>

<ul>
  <li>create_*.sql creates journal table using various partitioning methods.</li>
  <li>select_one.sql, select_day.sql, insert.sql and update.sql are pg_bench
scripts.</li>
</ul>

<p>P.S. This post is not a criticism of pg_partman.  It was developed long time
before extendability mechanisms which pg_pathman use were created.  And it is
a great extension which has served many years.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[How Does Pg_pathman Handle Filter Conditions?]]></title>
    <link href="https://akorotkov.github.io/blog/2016/03/14/pg_pathman-condition-processing/"/>
    <updated>2016-03-14T11:10:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/03/14/pg_pathman-condition-processing/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p><img class="no-border right" src="https://akorotkov.github.io/images/filter_data_icon.jpg" width="256" height="256" /></p>

<p>In my <a href="https://akorotkov.github.io/blog/2016/03/04/pg_pathman-beta-release/">previous post</a> I’ve
introduced <a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a> as an
extension which accelerate query planning over partitioned tables.  In this post
I would like to covert another aspect of pg_pathman: it not only produce  plans
faster, but also produce better plans.  Thanks to it query execution with
pg_pathman becomes much faster in some cases.</p>

<p>When you search partitioned table with some filter conditions, pg_pathman adopts
this filter to each individual partition.  Therefore, each partition receives
the only filter conditions which are useful to check against it.</p>

<p>Let me illustrate this on the example.  At first, let’s see what’s happening
with filter conditions while dealing with PostgreSQL inheritance mechanism.</p>

<!--more-->

<p>Let us make some partitioned table using inheritance.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test</span> <span class="p">(</span><span class="n">ts</span> <span class="k">timestamp</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="n">title</span> <span class="nb">text</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">test_ts_idx</span> <span class="k">ON</span> <span class="n">test</span> <span class="p">(</span><span class="n">ts</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_1</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-01-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-02-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_2</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_3</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-03-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-04-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_4</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-04-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-05-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_5</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-06-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test_6</span> <span class="p">(</span><span class="k">LIKE</span> <span class="n">test</span> <span class="k">INCLUDING</span> <span class="n">INDEXES</span><span class="p">,</span> <span class="k">CHECK</span> <span class="p">(</span> <span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-06-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01&#39;</span> <span class="p">))</span> <span class="k">INHERITS</span> <span class="p">(</span><span class="n">test</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>And them fill it with test data.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_1</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-01-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">31</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_2</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-02-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">28</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_3</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-03-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">31</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_4</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-04-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">30</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_5</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-05-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">31</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test_6</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-06-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">30</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Then let’s try to select rows from two time intervals.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">EXPLAIN</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">test</span> <span class="k">WHERE</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15&#39;</span><span class="p">)</span> <span class="k">OR</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01&#39;</span><span class="p">);</span>
</span><span class="line">                                                                                                                                    <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Append</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">5028</span><span class="p">.</span><span class="mi">22</span> <span class="k">rows</span><span class="o">=</span><span class="mi">128059</span> <span class="n">width</span><span class="o">=</span><span class="mi">41</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">40</span><span class="p">)</span>
</span><span class="line">         <span class="n">Filter</span><span class="p">:</span> <span class="p">(((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span> <span class="k">OR</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)))</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_2</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">1183</span><span class="p">.</span><span class="mi">40</span> <span class="k">rows</span><span class="o">=</span><span class="mi">40320</span> <span class="n">width</span><span class="o">=</span><span class="mi">41</span><span class="p">)</span>
</span><span class="line">         <span class="n">Filter</span><span class="p">:</span> <span class="p">(((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span> <span class="k">OR</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)))</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="n">Heap</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_3</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">444</span><span class="p">.</span><span class="mi">46</span><span class="p">..</span><span class="mi">1266</span><span class="p">.</span><span class="mi">02</span> <span class="k">rows</span><span class="o">=</span><span class="mi">20178</span> <span class="n">width</span><span class="o">=</span><span class="mi">41</span><span class="p">)</span>
</span><span class="line">         <span class="k">Recheck</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span> <span class="k">OR</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)))</span>
</span><span class="line">         <span class="o">-&gt;</span>  <span class="n">BitmapOr</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">444</span><span class="p">.</span><span class="mi">46</span><span class="p">..</span><span class="mi">444</span><span class="p">.</span><span class="mi">46</span> <span class="k">rows</span><span class="o">=</span><span class="mi">20178</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">               <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_3_ts_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">430</span><span class="p">.</span><span class="mi">07</span> <span class="k">rows</span><span class="o">=</span><span class="mi">20178</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">                     <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span>
</span><span class="line">               <span class="o">-&gt;</span>  <span class="n">Bitmap</span> <span class="k">Index</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_3_ts_idx</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">4</span><span class="p">.</span><span class="mi">30</span> <span class="k">rows</span><span class="o">=</span><span class="mi">1</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">                     <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_5</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">1310</span><span class="p">.</span><span class="mi">80</span> <span class="k">rows</span><span class="o">=</span><span class="mi">24360</span> <span class="n">width</span><span class="o">=</span><span class="mi">41</span><span class="p">)</span>
</span><span class="line">         <span class="n">Filter</span><span class="p">:</span> <span class="p">(((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span> <span class="k">OR</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)))</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_6</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">1268</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">43200</span> <span class="n">width</span><span class="o">=</span><span class="mi">41</span><span class="p">)</span>
</span><span class="line">         <span class="n">Filter</span><span class="p">:</span> <span class="p">(((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">))</span> <span class="k">OR</span> <span class="p">((</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span> <span class="k">AND</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)))</span>
</span><span class="line"><span class="p">(</span><span class="mi">16</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We can see that filter condition was passed to each partition as is.  But
actually it could be simplified a lot.  For instance, table test_2 could be scan
without filter condition at all because all its rows are matching.  Filter
condition to test_3 could be simplified to <code>ts &lt; '2015-03-15'</code>, therefore
BitmapOr is not necessary.</p>

<p>Let’s try the same example with
<a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a>.  Firstly create test
table and its partitions.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">test</span> <span class="p">(</span><span class="n">ts</span> <span class="k">timestamp</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span> <span class="n">title</span> <span class="nb">text</span><span class="p">);</span>
</span><span class="line"><span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">test_ts_idx</span> <span class="k">ON</span> <span class="n">test</span> <span class="p">(</span><span class="n">ts</span><span class="p">);</span>
</span><span class="line"><span class="k">SELECT</span> <span class="n">create_range_partitions</span><span class="p">(</span><span class="s1">&#39;test&#39;</span><span class="p">,</span> <span class="s1">&#39;ts&#39;</span><span class="p">,</span> <span class="s1">&#39;2015-01-01&#39;</span><span class="p">::</span><span class="k">timestamp</span><span class="p">,</span> <span class="s1">&#39;1 month&#39;</span><span class="p">::</span><span class="nb">interval</span><span class="p">,</span> <span class="mi">6</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Then insert test data into table.  pg_pathman automatically creates trigger
which distribute data between partitions. Just like pg_partman does.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">test</span> <span class="p">(</span><span class="k">SELECT</span> <span class="s1">&#39;2015-01-01&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="o">+</span> <span class="n">i</span> <span class="o">*</span> <span class="nb">interval</span> <span class="s1">&#39;1 minute&#39;</span><span class="p">,</span> <span class="n">md5</span><span class="p">(</span><span class="n">i</span><span class="p">::</span><span class="nb">text</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">generate_series</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1440</span> <span class="o">*</span> <span class="mi">181</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="n">i</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>And finally try the same query with pg_pathman.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">#</span> <span class="k">EXPLAIN</span> <span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">test</span> <span class="k">WHERE</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-02-01&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15&#39;</span><span class="p">)</span> <span class="k">OR</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15&#39;</span> <span class="k">AND</span> <span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-07-01&#39;</span><span class="p">);</span>
</span><span class="line">                                     <span class="n">QUERY</span> <span class="n">PLAN</span>
</span><span class="line"><span class="c1">------------------------------------------------------------------------------------</span>
</span><span class="line"> <span class="n">Append</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">3248</span><span class="p">.</span><span class="mi">59</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_2</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">780</span><span class="p">.</span><span class="mi">20</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="k">Index</span> <span class="n">Scan</span> <span class="k">using</span> <span class="n">test_3_ts_idx</span> <span class="k">on</span> <span class="n">test_3</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">29</span><span class="p">..</span><span class="mi">767</span><span class="p">.</span><span class="mi">99</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">         <span class="k">Index</span> <span class="n">Cond</span><span class="p">:</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&lt;</span> <span class="s1">&#39;2015-03-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_5</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">864</span><span class="p">.</span><span class="mi">40</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line">         <span class="n">Filter</span><span class="p">:</span> <span class="p">(</span><span class="n">ts</span> <span class="o">&gt;=</span> <span class="s1">&#39;2015-05-15 00:00:00&#39;</span><span class="p">::</span><span class="k">timestamp</span> <span class="k">without</span> <span class="n">time</span> <span class="k">zone</span><span class="p">)</span>
</span><span class="line">   <span class="o">-&gt;</span>  <span class="n">Seq</span> <span class="n">Scan</span> <span class="k">on</span> <span class="n">test_6</span>  <span class="p">(</span><span class="n">cost</span><span class="o">=</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="p">..</span><span class="mi">836</span><span class="p">.</span><span class="mi">00</span> <span class="k">rows</span><span class="o">=</span><span class="mi">0</span> <span class="n">width</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
</span><span class="line"><span class="p">(</span><span class="mi">7</span> <span class="k">rows</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>We can see that pg_pathman selects the same partitions, but query plan becomes
way simpler.  Now, test_2 is scanned without useless filter condition.  test_3
is scanned using just <code>ts &lt; '2015-03-15'</code> filter condition.  Thanks to it, plain
Index Scan is used instead of BitmapOr.  And similar advances was applied to
rest of partitions.</p>

<p>How was this simplification possible?  The common fear here is that such
simplification could be computational expensive in general case.  But since
pg_pathman is intended to decrease query planning time, it’s very important to
keep all transformations cheap and simple.  And this cheap and simple algorithm
of transformation really exists.</p>

<p>Let’s see how it works on simple example. The filter condition <code>(ts &gt;=
'2015-02-01' AND ts &lt; '2015-03-15') OR (ts &gt;= '2015-05-15' AND ts &lt;
'2015-07-01')</code> have following tree representation.</p>

<p><img class="no-border center" src="https://akorotkov.github.io/images/pg_pathman_condition_1.png" width="483" height="123" /></p>

<p>Leaf nodes of tree are simple conditions.  Non-leaf nodes are logical operators
which forms complex conditions.  For particular partition each filter condition
(either simple or complex) could be treated into one of three classes.</p>

<ol>
  <li>
    <p>Filter condition is always true for rows of this partition (t).  For
instance, condition <code>ts &gt;= '2015-04-15'</code> is always true for partition <code>ts &gt;=
2015-05-01 AND ts &lt; 2015-06-01</code>.</p>
  </li>
  <li>
    <p>Filter condition could be either true or false for rows of this partition
(m). For instance, condition <code>ts &gt;= '2015-03-15'</code> could be either true or
false for partition <code>ts &gt;= 2015-03-01 AND ts &lt; 2015-03-01</code>.</p>
  </li>
  <li>
    <p>Filter condition is always false for rows of this partition (f).  For
instance, condition <code>ts &lt;= '2015-02-01'</code> is always false for partition <code>ts &gt;=
2015-04-01 AND ts &lt; 2015-04-01</code>.</p>
  </li>
</ol>

<p>We can mark each tree node with vector of classes which corresponding condition
is treated against each partition.  These vectors could be filled upwards: for
leaf nodes first, and then for non-leaf nodes using tri-state logic.</p>

<p><img class="no-border center" src="https://akorotkov.github.io/images/pg_pathman_condition_2.png" width="543" height="144" /></p>

<p>It’s evident that only conditions which could be either true or false (m) are
useful for filtering.  Conditions which are always true or always false
shouldn’t be presented in the partitions filter.  Using produced three we can
now produce filter conditions for each partition.</p>

<ol>
  <li>
    <p>For <code>ts &gt;= 2015-01-01 AND ts &lt; 2015-02-01</code> partition, whole filter condition
is false. So, skip it.</p>
  </li>
  <li>
    <p>For <code>ts &gt;= 2015-02-01 AND ts &lt; 2015-03-01</code> partition, whole filter condition
is true. So, scan it without filter.</p>
  </li>
  <li>
    <p>For <code>ts &gt;= 2015-03-01 AND ts &lt; 2015-04-01</code> partition, filter condition tree
would be reduced into following tree.</p>

    <p><img class="no-border center" src="https://akorotkov.github.io/images/pg_pathman_condition_3.png" width="123" height="103" /></p>

    <p>Therefore, this partition will be scan with <code>ts &lt; '2015-03-15'</code> filter.</p>
  </li>
  <li>
    <p>For <code>ts &gt;= 2015-04-01 AND ts &lt; 2015-05-01</code> partition, whole filter condition
is false. So, skip it.</p>
  </li>
  <li>
    <p>For <code>ts &gt;= 2015-05-01 AND ts &lt; 2015-06-01</code> partition, filter condition tree
would be reduced into following tree.</p>

    <p><img class="no-border center" src="https://akorotkov.github.io/images/pg_pathman_condition_4.png" width="123" height="103" /></p>

    <p>Therefore, this partition will be scan with <code>ts &gt;= '2015-05-15'</code> filter.</p>
  </li>
  <li>
    <p>For <code>ts &gt;= 2015-06-01 AND ts &lt; 2015-07-01</code> partition, whole filter condition
is true. So, scan it without filter.</p>
  </li>
</ol>

<p>This is how filter conditions processing works in
<a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a>.  The explanation could
be a bit exhausting for reading, but I hope you feel enlighten by getting how it
works.  I remember that pg_pathman is open source extension for PostgreSQL 9.5
in beta-release stage.  I appeal to everyone interested for trying it and
sharing a feedback.</p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Pg_pathman Beta Release]]></title>
    <link href="https://akorotkov.github.io/blog/2016/03/04/pg_pathman-beta-release/"/>
    <updated>2016-03-04T17:10:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2016/03/04/pg_pathman-beta-release/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p><img class="no-border" src="https://akorotkov.github.io/images/partitiontable.jpg" width="640" height="373" /></p>

<p>Partitioning in PostgreSQL is traditionally implemented using <a href="http://www.postgresql.org/docs/9.5/static/ddl-partitioning.html">table
inheritance</a>.
Table inheritance allow planner to include into plan only those child tables
(partitions) which are compatible with query.  Simultaneously a lot of work on
partitions management remains on users: create inherited tables, writing trigger
which selects appropriate partition for row inserting etc.  In order to automate
this work pg_partman extension was written.  Also, there is upcoming work on
<a href="http://www.postgresql.org/message-id/55D3093C.5010800@lab.ntt.co.jp">declarative partitioning</a>
by Amit Langote for PostgreSQL core.</p>

<p>In Postgres Professional we notice performance problem of inheritance based
partitioning.  The problem is that planner selects children tables compatible
with query by linear scan.  Thus, for query which selects just one row from one
partition it would be much slower to plan than to execute.  This fact
discourages many users and this is why we’re working on new PostgreSQL
extension: <a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a>.</p>

<p><a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a> caches partitions
meta-information and uses set_rel_pathlist hook in order to replace mechanism of
child tables selection by its own mechanism.  Thanks to this binary search
algorithm over sorted array is used for range partitioning and hash table
lookup for hash partitioning.  Therefore, time spent to partition selection
appears to be negligible in comparison with forming of result plan nodes.  See
<a href="http://www.postgrespro.com/blog/pgsql/pg_pathman">postgrespro blog post</a> for
performance benchmarks.</p>

<p><a href="https://github.com/postgrespro/pg_pathman">pg_pathman</a> now in beta-release
status and we encourage all interested users to try it and give us a feedback.
pg_pathman is compatible with PostgreSQL 9.5 and distributed under PostgreSQL
license.  In the future we’re planning to enhance functionality of pg_pathman
by following features.</p>

<ul>
  <li>Execute time selection of partitions using custom nodes (useful for nested
loops and prepared statements);</li>
  <li>Optimization of ordering output from partitioned tables (useful for merge
join and order by);</li>
  <li>Optimization of hash join when both tables are partitioned by join key;</li>
  <li>LIST-partitioning;</li>
  <li>HASH-partitioning by attributes of any hashable type.</li>
</ul>

<p>Despite we have pg_pathman useful here and now, we want this functionality to
eventually become part of PostgreSQL core.  This is why we are going to join
work on declarative partitioning by Amit Langote which have excellent DDL
infrastructure and fulfill it with effective internal algorithms.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Thoughts About Jsonb Statistics]]></title>
    <link href="https://akorotkov.github.io/blog/2015/09/07/jsonb_statistics/"/>
    <updated>2015-09-07T11:30:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2015/09/07/jsonb_statistics/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>Users of jsonb datatype frequently complaint that it lucks of statistics.
Naturally, today jsonb statistics is just default scalar statistics, which is
suitable for <code>&lt;</code>, <code>&lt;=</code>, <code>=</code>, <code>&gt;=</code>, <code>&gt;</code> operators selectivity estimation. But
people search jsonb documents using <code>@&gt;</code> operator, expressions with <code>-&gt;</code>
operator, jsquery etc. This is why selectivity estimation, which people
typically get in their queries, is just a stub. This could lead wrong query plans
and bad performance. And it made us introduce hints in jsquery extension.</p>

<p>Thus, problem is clear. But the right solution is still unclear, at least for
me. Let me discuss evident approaches to jsonb statistics and their limitations.</p>

<!--more-->

<h2 id="collect-just-frequent-paths">Collect just frequent paths</h2>

<p>First candidate for good selectivity estimation is <code>@&gt;</code> operator. Really,
<code>@&gt;</code> is builtin operator with GIN index support. First idea that comes into
mind is to collect most frequent paths and their frequencies as jsonb
statistics. In order to understand idea of paths better let’s consider how GIN
jsonb_path_ops works. jsonb_path_ops is builtin GIN operator class, which is
most suitable for jsonb <code>@&gt;</code> operator.</p>

<p>Path is a sequence of key names, array indexes and referenced value.
For instance, the document 
<code>{"a": [{"b": "xyz", "c": true}, 10], "d": {"e": [7, false]}}</code>
would be decomposed into following set of paths.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class=""><span class="line">"a".#."b"."xyz"
</span><span class="line">"a".#."c".true
</span><span class="line">"a".#.10
</span><span class="line">"d"."e".#.7
</span><span class="line">"d"."e".#.false</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>In this representation of paths array indexes are replaced with <code>#</code>. That
allows our search to be agnostic to them like <code>@&gt;</code> operator does. Thus, when we
have such decomposition we can say that if <code>a @&gt; b</code> then <code>a</code> paths are superset
of <code>b</code> paths. If we intersect posting list of search argument paths then we can
get list of candidates for search result. This is how jsonb_path_ops works.</p>

<p>The same idea could be applied to jsonb statistics. We could decompose each
jsonb document into set of paths and then collect frequencies of most common
individual paths. Such statistics perfectly fits current PostgreSQL system
catalog and looks very similar to statistics of tsvectors and arrays, which are
decomposed into lexemes and elements correspondingly. Such statistics of most
common paths could look like following table.</p>

<table>
  <thead>
    <tr>
      <th>Path</th>
      <th style="text-align: right">Frequency</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“a”.#.”b”.”xyz”</td>
      <td style="text-align: right">0.55</td>
    </tr>
    <tr>
      <td>“d”.”e”.#.77</td>
      <td style="text-align: right">0.43</td>
    </tr>
    <tr>
      <td>“a”.#.”b”.”def”</td>
      <td style="text-align: right">0.35</td>
    </tr>
    <tr>
      <td>“d”.”e”.#.100</td>
      <td style="text-align: right">0.22</td>
    </tr>
    <tr>
      <td>“d”.”f”.true</td>
      <td style="text-align: right">0.1</td>
    </tr>
  </tbody>
</table>

<p>Having such statistics we can estimate selectivity of <code>@&gt;</code> operator as product
of frequencies of search argument paths. For paths, which are not in most
common list, we can use some default “rare frequency”. Also, we use quite rough
assumption that paths appearance is independent. Let’s be honest: this
assumption is just wrong. However, this is typical assumption we have to use
during query planning. Finally, we don’t need absolutely accurate cost. Match of
magnitude order can be considered as a quite good result.</p>

<p>There is also another source or inaccuracy I’d like to mention. Let’s consider
some example.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class=""><span class="line">a = [{"x": [1]}, {"x": [2]}]
</span><span class="line">b = [{"x": [1,2]}]</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>Both <code>a</code> and <code>b</code> are decomposed into the same set of paths.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class=""><span class="line">#."x".1
</span><span class="line">#."x".2</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>However, neither <code>a @&gt; b</code> neither ‘b @&gt; a’. Since we ignored array indexes in
paths we also ignore whether values beholds to same array element or not. This
leads also to false positives in GIN and overestimations by statistics.</p>

<p>This approach is not only limited by <code>@&gt;</code> operator. We can produce estimation
for queries with complex logic. Example in jsquery could be <code>"(abc" = 1 OR
"xyz".# = "hij") AND NOT "def" = false</code>.</p>

<p>However, such statistics hardly can estimate selectivity of <code>&lt;</code>, <code>&lt;=</code>,
<code>&gt;=</code>, <code>&gt;</code> operators over jsonb values. For instance, in order to estimate
jsquery <code>"x" &gt; 1</code> we can only count most common paths, which match this
condition. But we’re lacking of histograms. It is a serious obstacle in getting
accurate estimates and it lets us search for better solution.</p>

<h2 id="collect-scalar-statistics-for-each-key-path">Collect scalar statistics for each key path</h2>

<p>Another idea of jsonb statistics we can get from assumption that almost every
“schemaless” dataset can be easily represented in the schema of tables. Assuming
this we would like our selectivity estimates for search in jsonb documents to be
as good as those for search in plain tables.</p>

<p>Let’s consider this on the example. The following json document could represent
the information about order in e-commerce.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;contact&quot;</span><span class="p">:</span> <span class="s2">&quot;John Smith&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;phone&quot;</span><span class="p">:</span> <span class="s2">&quot;212 555-1234&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;address&quot;</span><span class="p">:</span> <span class="s2">&quot;10021-3100, 21 2nd Street, New York&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;products&quot;</span><span class="p">:</span>
</span><span class="line">  <span class="p">[</span>
</span><span class="line">    <span class="p">{</span>
</span><span class="line">      <span class="nt">&quot;article&quot;</span><span class="p">:</span> <span class="s2">&quot;XF56120&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Sunglasses&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;price&quot;</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;quantity&quot;</span><span class="p">:</span> <span class="mi">1</span>
</span><span class="line">    <span class="p">},</span>
</span><span class="line">    <span class="p">{</span>
</span><span class="line">      <span class="nt">&quot;article&quot;</span><span class="p">:</span> <span class="s2">&quot;AT10789&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;T-Shirt&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;price&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;quantity&quot;</span><span class="p">:</span> <span class="mi">2</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">]</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>The same information could be represented in the following couple of tables.</p>

<table>
  <thead>
    <tr>
      <th>id</th>
      <th>contact</th>
      <th>phone</th>
      <th>address</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>John Smith</td>
      <td>212 555-1234</td>
      <td>10021-3100, 21 2nd Street, New York</td>
    </tr>
  </tbody>
</table>

<table>
  <thead>
    <tr>
      <th>order_id</th>
      <th>article</th>
      <th>name</th>
      <th style="text-align: right">price</th>
      <th style="text-align: right">quantity</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>XF56120</td>
      <td>Sunglasses</td>
      <td style="text-align: right">500</td>
      <td style="text-align: right">1</td>
    </tr>
    <tr>
      <td>1</td>
      <td>AT10789</td>
      <td>T-Shirt</td>
      <td style="text-align: right">100</td>
      <td style="text-align: right">2</td>
    </tr>
  </tbody>
</table>

<p>What kind of statictis would be collected by PostgreSQL in the second case? It
would be most common values and histogram for each attribute. Most common values
(MCVs) are values, which occur in the column most frequently. Frequencies of those
values are collected and stored as well. Histogram is described by array of
bounds. Each bound is assumed to contain equal number of column values
excluding MCVs (so called equi-depth histogram).</p>

<p>With some simplification such statistics could be represented in the following
table.</p>

<table>
  <thead>
    <tr>
      <th>Table</th>
      <th>Attribute</th>
      <th>Most common values</th>
      <th>Histogram</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>order</td>
      <td>contact</td>
      <td>{“John Smith”: 0.05, “James Johnson”: 0.01}</td>
      <td>[“Anthony Anderson”, “Lisa Baker”, “Sandra Phillips”]</td>
    </tr>
    <tr>
      <td>product</td>
      <td>price</td>
      <td>{“100”: 0.1, “10”: 0.08, “50”: 0.05, “150”: 0.03}</td>
      <td>[0, 12.5, 45.5, 250, 1000]</td>
    </tr>
    <tr>
      <td>product</td>
      <td>quantity</td>
      <td>{“1”: 0.5, “2”: 0.2, “3”: 0.05, “5”: 0.01}</td>
      <td>[0, 4, 7, 9, 10]</td>
    </tr>
    <tr>
      <td>…….</td>
      <td>………</td>
      <td>………………………………………….</td>
      <td>……………………………………………..</td>
    </tr>
  </tbody>
</table>

<p>What if we replace table and attribute with path of keys where corresponding
value could be found in json document?</p>

<table>
  <thead>
    <tr>
      <th>Key path</th>
      <th>Most common values</th>
      <th>Histogram</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>contact</td>
      <td>{“John Smith”: 0.05, “James Johnson”: 0.01}</td>
      <td>[“Anthony Anderson”, “Lisa Baker”, “Sandra Phillips”]</td>
    </tr>
    <tr>
      <td>products.#.price</td>
      <td>{“100”: 0.1, “10”: 0.08, “50”: 0.05, “150”: 0.03}</td>
      <td>[0, 12.5, 45.5, 250, 1000]</td>
    </tr>
    <tr>
      <td>products.#.quantity</td>
      <td>{“1”: 0.5, “2”: 0.2, “3”: 0.05, “5”: 0.01}</td>
      <td>[0, 4, 7, 9, 10]</td>
    </tr>
    <tr>
      <td>……………….</td>
      <td>………………………………………….</td>
      <td>……………………………………………..</td>
    </tr>
  </tbody>
</table>

<p>This kind of statistics seems to be comprehensive enough. It could produce fine
estimations for queries like <code>products.#.price &gt; 100</code>.</p>

<p>However, there are still bunch of open problems here.</p>

<ul>
  <li>
    <p>Typical json documents we can meet in applications are really well structured
as an example above. However, there are some cases when they are not. At
first, someone could easily put values into keys. Let me illustrate this on
the following example: <code>products</code> becomes an object where article is used as
a key.</p>

    <p>In this case we can find that cardinality of key paths are very high. Thus,
we would be unable to collect suitable statistics for each key path.
However, we could consider such situation as user mistake. Then we should
advise users to restructure their documents.</p>

    <p>There are still kind of documents, which don’t fit this model not because of
user mistake but because of their nature. Imagine json formatted query plans
stored in the table. Plans could have unlimited levels of nesting and
correspondingly cardinality of key paths could be very high.</p>
  </li>
</ul>

<div class="bogus-wrapper"><notextile><figure class="code"><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
</pre></td><td class="code"><pre><code class="json"><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="nt">&quot;id&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;contact&quot;</span><span class="p">:</span> <span class="s2">&quot;John Smith&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;phone&quot;</span><span class="p">:</span> <span class="s2">&quot;212 555-1234&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;address&quot;</span><span class="p">:</span> <span class="s2">&quot;10021-3100, 21 2nd Street, New York&quot;</span><span class="p">,</span>
</span><span class="line">  <span class="nt">&quot;products&quot;</span><span class="p">:</span>
</span><span class="line">  <span class="p">{</span>
</span><span class="line">    <span class="nt">&quot;XF56120&quot;</span><span class="p">:</span>
</span><span class="line">    <span class="p">{</span>
</span><span class="line">      <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;Sunglasses&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;price&quot;</span><span class="p">:</span> <span class="mi">500</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;quantity&quot;</span><span class="p">:</span> <span class="mi">1</span>
</span><span class="line">    <span class="p">},</span>
</span><span class="line">    <span class="nt">&quot;AT10789&quot;</span><span class="p">:</span>
</span><span class="line">    <span class="p">{</span>
</span><span class="line">      <span class="nt">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;T-Shirt&quot;</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;price&quot;</span><span class="p">:</span> <span class="mi">100</span><span class="p">,</span>
</span><span class="line">      <span class="nt">&quot;quantity&quot;</span><span class="p">:</span> <span class="mi">2</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<ul>
  <li>
    <p>Some objects stored inside jsonb documents could require special statistics.
For instance, point coordinates could be represented in json as
<code>{"x": 11.3, "y": 27.0}</code>. But statistics we will need in this case is not
separate statistics for <code>x</code> and <code>y</code>. We would need something special for
geometrical objects like 2D-histograms.</p>
  </li>
  <li>
    <p>Another problem is fitting this model into PostgreSQL system catalog.
<code>pg_statistic</code> assumes that statistics of attribute is represented by few
arrays. However, in this model we have to store few arrays per each key
path. For sure, we do a trick by storing array of jsonb or something like
this, but that would be a kluge. It would be nice to store each key path in
the separate row of <code>pg_statistic</code>. This would require significant changes
in statistics handling though.</p>
  </li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>This was just my current thoughts about jsonb statistics. Probably, someone come
with much better ideas. But I’m not sure we can find ideal solution, which
would fit everyone needs. We can see that current developments in
multivariate  statistics use pluggable approach: user can turn on specific
method on specific set of column. We could end up with something similar for
jsonb: simple basic statistics + various kinds of pluggable statistics for
specific needs.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Psql Command to Attach Gdb to Backend]]></title>
    <link href="https://akorotkov.github.io/blog/2015/08/26/psql-gdb-attach/"/>
    <updated>2015-08-26T18:00:00+03:00</updated>
    <id>https://akorotkov.github.io/blog/2015/08/26/psql-gdb-attach/?utm_medium=social&amp;utm_source=rss</id>
    <content type="html"><![CDATA[<p>While hacking PostgreSQL it’s very useful to know pid of the backend you are
working with. You need to know pid of the process to attach debugger, profiler
etc. Luckily, .psqlrc provides us an elegant way to define the shortcuts for
psql. Using config line below one can find out backend pid just by typing <code>:pid</code>.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>.psqlrc</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="err">\</span><span class="k">set</span> <span class="n">pid</span> <span class="s1">&#39;SELECT pg_backend_pid();&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="o">=#</span> <span class="p">:</span><span class="n">pid</span>
</span><span class="line"> <span class="n">pg_backend_pid</span>
</span><span class="line"><span class="c1">----------------</span>
</span><span class="line">          <span class="mi">99038</span>
</span><span class="line"><span class="p">(</span><span class="mi">1</span> <span class="k">row</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>In 9.6 it becomes possible to even
<a href="http://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=275f05c9">include backend pid into psql prompt</a>.</p>

<p>However, it’s possible to automate more complex actions in psql. I’ve configured
my psql to run gdb attached to current backend in new tab of iTerm2 just by
typing <code>:gdb</code>.</p>

<p><img class="no-border" src="https://akorotkov.github.io/images/screen-psql-iterm-gdb.png" width="494" height="411" /></p>

<!--more-->

<p>The <code>:gdb</code> command selects pid of current backend and puts it to the input of
pg_debug script.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>.psqlrc</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
</pre></td><td class="code"><pre><code class="sql"><span class="line"><span class="err">\</span><span class="k">set</span> <span class="n">gdb</span> <span class="s1">&#39;SELECT pg_backend_pid() \\g |pg_debug&#39;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>pg_debug extracts pid from its input and then runs OSA script which runs gdb
in the new tab of iTerm2.</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span>pg_debug</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
</pre></td><td class="code"><pre><code class="bash"><span class="line"><span class="c">#!/bin/bash</span>
</span><span class="line">
</span><span class="line"><span class="nv">IFS</span><span class="o">=</span><span class="s1">&#39;&#39;</span>
</span><span class="line">
</span><span class="line"><span class="k">while</span> <span class="nb">read </span>line
</span><span class="line"><span class="k">do</span>
</span><span class="line">	<span class="c"># Extended display off</span>
</span><span class="line">	<span class="k">if</span> <span class="o">[[</span> <span class="nv">$line</span> <span class="o">=</span>~ ^<span class="se">\ </span>+<span class="o">([</span>0-9<span class="o">]</span>+<span class="o">)</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span><span class="line">		<span class="nv">PID</span><span class="o">=</span><span class="k">${</span><span class="nv">BASH_REMATCH</span><span class="p">[1]</span><span class="k">}</span>
</span><span class="line">		<span class="nb">break</span>
</span><span class="line"><span class="nb">	</span><span class="k">fi</span>
</span><span class="line">	<span class="c"># Extended display on</span>
</span><span class="line">	<span class="k">if</span> <span class="o">[[</span> <span class="nv">$line</span> <span class="o">=</span>~ ^pg_backend_pid.*<span class="se">\ </span><span class="o">([</span>0-9<span class="o">]</span>+<span class="o">)</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span><span class="line">		<span class="nv">PID</span><span class="o">=</span><span class="k">${</span><span class="nv">BASH_REMATCH</span><span class="p">[1]</span><span class="k">}</span>
</span><span class="line">		<span class="nb">break</span>
</span><span class="line"><span class="nb">	</span><span class="k">fi</span>
</span><span class="line"><span class="k">done</span>
</span><span class="line">
</span><span class="line"><span class="c"># Open gdb session</span>
</span><span class="line">osascript -e <span class="s2">&quot;</span>
</span><span class="line"><span class="s2">tell application \&quot;iTerm\&quot;</span>
</span><span class="line"><span class="s2">	activate</span>
</span><span class="line"><span class="s2">	tell the current terminal</span>
</span><span class="line"><span class="s2">		set mysession to (the current session)</span>
</span><span class="line"><span class="s2">		launch session \&quot;Default Session\&quot;</span>
</span><span class="line"><span class="s2">		tell the last session</span>
</span><span class="line"><span class="s2">			write text \&quot;gdb --pid=$PID -x &lt;(echo continue)\&quot;</span>
</span><span class="line"><span class="s2">		end tell</span>
</span><span class="line"><span class="s2">		select mysession</span>
</span><span class="line"><span class="s2">	end tell</span>
</span><span class="line"><span class="s2">end tell&quot;</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>This script works for Mac OS X and iTerm2, but the same approach should work
for other platforms and terminal emulators.</p>
]]></content>
  </entry>
  
</feed>
