<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jialei Jin</title>
    <description>The latest articles on DEV Community by Jialei Jin (@jialei_jin).</description>
    <link>https://dev.to/jialei_jin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3394475%2Ff89ba3b6-da52-4b15-8fc0-349d13c9af3e.jpg</url>
      <title>DEV Community: Jialei Jin</title>
      <link>https://dev.to/jialei_jin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jialei_jin"/>
    <language>en</language>
    <item>
      <title>How Lingodb do column pruning</title>
      <dc:creator>Jialei Jin</dc:creator>
      <pubDate>Mon, 28 Jul 2025 20:43:42 +0000</pubDate>
      <link>https://dev.to/jialei_jin/how-lingodb-do-column-pruning-1lo3</link>
      <guid>https://dev.to/jialei_jin/how-lingodb-do-column-pruning-1lo3</guid>
      <description>&lt;p&gt;This ariticle talk about lingodb's column pruning and how it differs from tranditional way because of MLIR making it easy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I am external contributor to Lingodb, not affiliated to TUM. For this ariticle's content, I am just a learner not contributor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Typical way of column pruning
&lt;/h2&gt;

&lt;p&gt;Column pruning is a very common optimization for databases. It helps TableScan only fetch data from only neccessary columns used by upstream operator like projection operator. This is one of the optimization can decrease disk IO.&lt;/p&gt;

&lt;p&gt;Let's walk through column pruning by a very simple example, say we have a very simple query &lt;code&gt;select abs(name) from student&lt;/code&gt;. The query plan structure is very simple, can be described from top to bottom like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;QUERY&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;Projection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;REQUIRED&lt;/span&gt; &lt;span class="n"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;ANALYZER&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Analyze&lt;/span&gt; &lt;span class="n"&gt;Projection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;collect&lt;/span&gt; &lt;span class="n"&gt;REQUIRED&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;Analyzer&lt;/span&gt; &lt;span class="n"&gt;reach&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;QUERY&lt;/span&gt; &lt;span class="n"&gt;PLAN&lt;/span&gt; &lt;span class="n"&gt;AFTER&lt;/span&gt; &lt;span class="n"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;PRUNNING&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;Projection&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TableScan&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;student&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more complex cases with join or where, &lt;code&gt;[REQUIRED COLUMN ANALYZER]&lt;/code&gt; will be apply to those operators for get the used/required columns before it reach TableScan.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Lingodb do column pruning
&lt;/h2&gt;

&lt;p&gt;By searching the code, you cannot find any content related to column pruning. Column pruning is not a standard optimization/pass of Lingodb. Lingodb just do it very handy inside a rewrite passes. I will just post the code to show how Lingodb do column pruning. It's very simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseTableLowering&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;OpConversionPattern&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BaseTableOp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nl"&gt;public:&lt;/span&gt;
   &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;OpConversionPattern&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BaseTableOp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;::&lt;/span&gt;&lt;span class="n"&gt;OpConversionPattern&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="n"&gt;LogicalResult&lt;/span&gt; &lt;span class="n"&gt;matchAndRewrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BaseTableOp&lt;/span&gt; &lt;span class="n"&gt;baseTableOp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OpAdaptor&lt;/span&gt; &lt;span class="n"&gt;adaptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConversionPatternRewriter&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rewriter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getRequired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseTableOp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;BaseTableLowering&lt;/code&gt; is the process of rewriting tablescan from relalg dialect to subop dialect. The total rewriting process is not what we care for this ariticle.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;code&gt;BaseTableOp&lt;/code&gt; is TableScan definition. &lt;code&gt;getRequired(baseTableOp)&lt;/code&gt; call get the required columns for the TableScan. This required column analysis is from bottom to top, from def to users, which is opposite to typical way of column pruning.&lt;/p&gt;

&lt;p&gt;The very crucial thing that Lingodb can achieve column pruning from def to users is MLIR provide def/use information by nature. Def/use info for a program is crucial in compiler optimization, like liveness analysis. And in terms of database compilation process, def/use info is perfect for column pruning. The  &lt;code&gt;[REQUIRED COLUMN ANALYZER]&lt;/code&gt;  is defined as follow, read comments along with code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ColumnSet&lt;/span&gt; &lt;span class="nf"&gt;getRequired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Operator&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// 1. calc available columns. For BaseTableOp like student, available could be [name, birth, gender, addr]&lt;/span&gt;
   &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getAvailableColumns&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ColumnSet&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="c1"&gt;// 2. iterate users. BaseTableOp.getUsers() for could be [projection, filter, join, ...]&lt;/span&gt;
   &lt;span class="c1"&gt;//    required columns is calcuated recursively. In case of required column is indirectly required,&lt;/span&gt;
   &lt;span class="c1"&gt;//    an example is a projection uses column of a join which joins two tables.&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getUsers&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;consumingOp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlir&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;dyn_cast_or_null&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Operator&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getRequired&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumingOp&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
         &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumingOp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getUsedColumns&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;materializeOp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mlir&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;dyn_cast_or_null&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MaterializeOp&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relalg&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ColumnSet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fromArrayAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;materializeOp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getCols&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c1"&gt;// 3. do intersect. required may contains used column not from current BaseTableOp.&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me show TableScan changes after &lt;code&gt;getRequired&lt;/code&gt; is called. Lingodb's relalg IR for a more concrete understanding. We use a more complex sql like following, the tables used are all tpch tables. You can also try it here: &lt;a href="https://www.lingo-db.com/interface/" rel="noopener noreferrer"&gt;https://www.lingo-db.com/interface/&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;n_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c_name&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="n"&gt;tb1&lt;/span&gt; &lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;nation&lt;/span&gt; 
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;n_regionkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r_regionkey&lt;/span&gt; 
  &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;r_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'EUROPE'&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tb2&lt;/span&gt;
&lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;
&lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;c_nationkey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n_nationkey&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[SUBOP TABLESCAN]&lt;/code&gt; is the TableScan after rewriting. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;region&lt;/code&gt;: The attributes &lt;code&gt;@scan_u_3::@ref({type = !subop.table_entry_ref&amp;lt;[r_name$0 : !db.char&amp;lt;25&amp;gt;, r_regionkey$0 : i32]&amp;gt;})&lt;/code&gt; shows there only two columns need to be scanned. &lt;code&gt;r_name&lt;/code&gt; is required by predicate and &lt;code&gt;r_regionkey&lt;/code&gt; is required by inner join. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nation&lt;/code&gt;: The attributes &lt;code&gt;@scan_u_2::@ref({type = !subop.table_entry_ref&amp;lt;[n_name$1 : !db.char&amp;lt;25&amp;gt;, n_nationkey$0 : i32, n_regionkey$0 : i32]&amp;gt;}) {parallel}&lt;/code&gt; shows there only three columns need to be scanned. &lt;code&gt;n_name&lt;/code&gt; is required by outer projection, n_nationkey  is required by outer join, n_regionkey is required by inner join.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;customer&lt;/code&gt;: The attributes &lt;code&gt;@scan_u_1::@ref({type = !subop.table_entry_ref&amp;lt;[c_name$1 : !db.string, c_nationkey$0 : i32]&amp;gt;})&lt;/code&gt; shows there only two columns need to be scanned. &lt;code&gt;c_name&lt;/code&gt; is required by outer projection and &lt;code&gt;c_nationkey&lt;/code&gt; is required by outer join.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;RELALG&lt;/span&gt; &lt;span class="err"&gt;TABLESCAN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;%1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;relalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;basetable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5.000000e+00&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="err"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;table_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nl"&gt;columns:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;r_comment&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@tb1_&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@r_comment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;r_name&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@tb1_&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@r_name&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}),&lt;/span&gt; &lt;span class="err"&gt;r_regionkey&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@tb1_&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@r_regionkey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;
&lt;span class="nv"&gt;%2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;relalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;basetable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2.500000e+01&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="err"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;table_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nation"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nl"&gt;columns:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;n_comment&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@nation&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@n_comment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;n_name&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@nation&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@n_name&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}),&lt;/span&gt; &lt;span class="err"&gt;n_nationkey&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@nation&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@n_nationkey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;n_regionkey&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@nation&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@n_regionkey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nv"&gt;%5&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;relalg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;basetable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1.500000e+05&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="err"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;table_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nl"&gt;columns:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;c_acctbal&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_acctbal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.decimal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_address&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_address&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_comment&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_comment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_custkey&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_custkey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_mktsegment&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_mktsegment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_name&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_name&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_nationkey&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_nationkey&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="err"&gt;c_phone&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@customer&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@c_phone&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;})}&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;SUBOP&lt;/span&gt; &lt;span class="err"&gt;TABLESCAN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;%15&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;subop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;scan_refs&lt;/span&gt; &lt;span class="nv"&gt;%arg0&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;!subop.table&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;r_comment$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;r_name$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;r_regionkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@scan_u_3&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@ref&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!subop.table_entry_ref&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;r_name$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;r_regionkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&amp;gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nv"&gt;%15&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;subop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;scan_refs&lt;/span&gt; &lt;span class="nv"&gt;%arg0&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;!subop.table&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;n_comment$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;n_name$1 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;n_nationkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;n_regionkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@scan_u_2&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@ref&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!subop.table_entry_ref&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;n_name$1 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;25&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;n_nationkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;n_regionkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&amp;gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nv"&gt;%15&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="err"&gt;subop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;scan_refs&lt;/span&gt; &lt;span class="nv"&gt;%arg0&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;!subop.table&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;c_acctbal$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.decimal&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_address$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_comment$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_custkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_mktsegment$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_name$1 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_nationkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_phone$0 :&lt;/span&gt; &lt;span class="nv"&gt;!db.char&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;]&amp;gt;&lt;/span&gt; &lt;span class="vg"&gt;@scan_u_1&lt;/span&gt;&lt;span class="err"&gt;::&lt;/span&gt;&lt;span class="vg"&gt;@ref&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;!subop.table_entry_ref&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;[&lt;/span&gt;&lt;span class="nl"&gt;c_name$1 :&lt;/span&gt; &lt;span class="nv"&gt;!db.string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;c_nationkey$0 :&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;]&amp;gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope I show how different and handy that Lingodb handle query optimization comparing to other databases. The MLIR provides a compilation infrastructure that can make compilation task easy to do. I avoid the more delicate part Lingodb like how to implement &lt;code&gt;getAvailableColumns&lt;/code&gt; inside &lt;code&gt;getRequired&lt;/code&gt;. Upon MLIR infrastructure, Lingodb still need to do a lot of great design to make it work. But I would like to keep it simple for this article just to show the general idea to how column pruning is handled in Lingodb.&lt;/p&gt;

&lt;p&gt;Another thing you may consider is this &lt;code&gt;getRequired&lt;/code&gt; implementation may not be fastest. Like for join case, &lt;code&gt;getRequired&lt;/code&gt; will visit all users for each TableScan which may cause multiple visits for one operator, but typical way of column pruning still visit all operators only once. However, Lingodb is not chasing for extreme compilation performance right now. The main objective of Lingodb can be seen from their website &lt;a href="https://www.lingo-db.com/" rel="noopener noreferrer"&gt;https://www.lingo-db.com/&lt;/a&gt;. Current design is totally acceptable.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
