<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nishchith.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nishchith.com/" rel="alternate" type="text/html" /><updated>2026-01-01T04:01:15+00:00</updated><id>https://nishchith.com/feed.xml</id><title type="html">nishchith.com</title><subtitle>nishchith&apos;s space on the internet</subtitle><author><name>nishchith shetty</name></author><entry><title type="html">Understanding SQL Parsers</title><link href="https://nishchith.com/sql-parsers/" rel="alternate" type="text/html" title="Understanding SQL Parsers" /><published>2025-12-31T00:00:00+00:00</published><updated>2025-12-31T00:00:00+00:00</updated><id>https://nishchith.com/sql-parsers</id><content type="html" xml:base="https://nishchith.com/sql-parsers/"><![CDATA[<p>It’s rare to talk about heuristic systems when there’s so much hype around probabilistic ones. This post is boring, but stay with me.</p>

<p>My work at Atlan has touched SQL parsing since the beginning. I made early contributions to the query engine with policy-based authorization powering <a href="https://www.youtube.com/watch?v=Aaa1DZYmVgw">Insights</a>, and we generate SQL lineage by parsing queries to power <a href="https://atlan.com/p/data-lineage">column-level lineage</a>. Along the way, my colleagues and I have evaluated a lot of SQL parsers, both open source and commercial: SQLGlot, sqlparser-rs, sqloxide, Apache Calcite, Gudusoft GSP, JSqlParser, and others. Each with different tradeoffs.</p>

<p>This post is my attempt to distill what I’ve learned. I’m not an expert, just someone curious enough to ask: why do so many SQL parsers exist? And what are they actually doing under the hood?</p>

<p>The idea for this writeup came during a drive back from SFO after dropping a friend off at the airport. That turned into about 10 hours of conversation with Claude to pull it all together.</p>

<hr />

<h2 id="table-of-contents">Table of Contents</h2>

<ul>
  <li><a href="#what-is-a-sql-parser">What is a SQL Parser?</a></li>
  <li><a href="#the-full-pipeline">The Full Pipeline</a></li>
  <li><a href="#lexical-analysis-the-lexer">Lexical Analysis (The Lexer)</a></li>
  <li><a href="#syntactic-analysis-the-parser">Syntactic Analysis (The Parser)</a></li>
  <li><a href="#the-abstract-syntax-tree">The Abstract Syntax Tree</a></li>
  <li><a href="#syntax-vs-semantics">Syntax vs Semantics</a></li>
  <li><a href="#column-level-lineage">Column-Level Lineage</a></li>
  <li><a href="#sql-dialects">SQL Dialects</a></li>
  <li><a href="#parsers-vs-query-engines">Parsers vs Query Engines</a></li>
  <li><a href="#comparing-sql-parsers">Comparing SQL Parsers</a></li>
  <li><a href="#further-reading">Further Reading</a></li>
</ul>

<hr />

<h2 id="what-is-a-sql-parser">What is a SQL Parser?</h2>

<p>A SQL parser reads SQL text and converts it into a structured representation, usually a tree, that computers can work with. It’s the “understanding” step.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>INPUT:  "CREATE TABLE active_users AS SELECT id, name, email FROM users WHERE status = 'active'"
                                            ↓
                                       [SQL PARSER]
                                            ↓
OUTPUT: A tree structure representing the query's meaning
</code></pre></div></div>

<p>Think of it like how your brain parses a sentence to extract meaning. The parser does the same for SQL.</p>

<hr />

<h2 id="the-full-pipeline">The Full Pipeline</h2>

<p>Every SQL parser follows the same fundamental pipeline. This isn’t a design choice. It’s a consequence of how language processing works.</p>

<iframe src="/assets/sql-pipeline.html" width="100%" height="520" frameborder="0" style="border: none; border-radius: 8px;"></iframe>

<p><strong>Lexer</strong>: Breaks the SQL string into tokens. Keywords, identifiers, operators, literals. Like recognizing words in a sentence.</p>

<p><strong>Parser</strong>: Takes tokens and builds a tree based on grammar rules. Like understanding “subject-verb-object” structure.</p>

<p><strong>AST</strong>: The Abstract Syntax Tree. A clean, navigable representation of the query’s structure.</p>

<p><strong>Semantic Analysis</strong>: Adds meaning. Does this table exist? What type is this column? This is where you need schema information.</p>

<hr />

<h2 id="lexical-analysis-the-lexer">Lexical Analysis (The Lexer)</h2>

<p>The lexer converts a stream of characters into meaningful chunks called tokens.</p>

<p><strong>Input</strong>:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">active_users</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">email</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'active'</span>
</code></pre></div></div>

<p><strong>Output</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>Token(CREATE)
Token(TABLE)
Token(IDENTIFIER, "active_users")
Token(AS)
Token(SELECT)
Token(IDENTIFIER, "id")
Token(COMMA)
Token(IDENTIFIER, "name")
Token(COMMA)
Token(IDENTIFIER, "email")
Token(FROM)
Token(IDENTIFIER, "users")
Token(WHERE)
Token(IDENTIFIER, "status")
Token(EQUALS)
Token(STRING, "active")
</code></pre></div></div>

<p>The lexer handles dialect-specific decisions early:</p>

<table>
  <thead>
    <tr>
      <th>Decision</th>
      <th>Standard SQL</th>
      <th>MySQL</th>
      <th>SQL Server</th>
      <th>PostgreSQL</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Identifier quote</td>
      <td><code class="language-plaintext highlighter-rouge">"name"</code></td>
      <td><code class="language-plaintext highlighter-rouge">`name`</code></td>
      <td><code class="language-plaintext highlighter-rouge">[name]</code></td>
      <td><code class="language-plaintext highlighter-rouge">"name"</code></td>
    </tr>
    <tr>
      <td>String quote</td>
      <td><code class="language-plaintext highlighter-rouge">'text'</code></td>
      <td><code class="language-plaintext highlighter-rouge">'text'</code> or <code class="language-plaintext highlighter-rouge">"text"</code></td>
      <td><code class="language-plaintext highlighter-rouge">'text'</code></td>
      <td><code class="language-plaintext highlighter-rouge">'text'</code></td>
    </tr>
    <tr>
      <td>Line comment</td>
      <td><code class="language-plaintext highlighter-rouge">--</code></td>
      <td><code class="language-plaintext highlighter-rouge">--</code> or <code class="language-plaintext highlighter-rouge">#</code></td>
      <td><code class="language-plaintext highlighter-rouge">--</code></td>
      <td><code class="language-plaintext highlighter-rouge">--</code></td>
    </tr>
    <tr>
      <td>Case sensitivity</td>
      <td>Insensitive</td>
      <td>Insensitive</td>
      <td>Insensitive</td>
      <td>Insensitive (keywords)</td>
    </tr>
  </tbody>
</table>

<p>Lexers are simple. They look at one character (or a few) at a time, don’t need to understand nesting or structure, and can be implemented with state machines or regex. This is why SQLGlot’s optional Rust tokenizer gives ~30% speedup<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Tokenization is pure CPU-bound character scanning.</p>

<hr />

<h2 id="syntactic-analysis-the-parser">Syntactic Analysis (The Parser)</h2>

<p>The parser reads tokens and builds a tree structure based on grammar rules.</p>

<p><strong>Input tokens</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>[CREATE, TABLE, active_users, AS, SELECT, id, COMMA, name, COMMA, email, FROM, users, WHERE, status, =, 'active']
</code></pre></div></div>

<p><strong>Grammar rule</strong>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>ctas_stmt    := CREATE TABLE table_name AS select_stmt
select_stmt  := SELECT column_list FROM table_name [WHERE condition]
</code></pre></div></div>

<p><strong>Parser thinks</strong>:</p>
<ul>
  <li>“I see CREATE TABLE… this must be a ctas_stmt”</li>
  <li>“Next I need a table_name… I see ‘active_users’”</li>
  <li>“Next I need AS… got it”</li>
  <li>“Now I need a select_stmt…”</li>
  <li>“I see SELECT… parsing the inner query”</li>
  <li>“Column list: id, name, email”</li>
  <li>“FROM users”</li>
  <li>“WHERE status = ‘active’”</li>
</ul>

<p>The parser is essentially a state machine following grammar rules, consuming tokens and building tree nodes.</p>

<h3 id="why-parsing-is-harder-than-lexing">Why Parsing is Harder Than Lexing</h3>

<p>Lexing is pattern matching. Parsing is about structure.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="p">(</span><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="p">(</span><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">t</span><span class="p">))</span>
</code></pre></div></div>

<p>A lexer sees: <code class="language-plaintext highlighter-rouge">(</code>, <code class="language-plaintext highlighter-rouge">(</code>, <code class="language-plaintext highlighter-rouge">(</code>, <code class="language-plaintext highlighter-rouge">)</code>, <code class="language-plaintext highlighter-rouge">)</code>, <code class="language-plaintext highlighter-rouge">)</code></p>

<p>A parser must match each <code class="language-plaintext highlighter-rouge">(</code> with its corresponding <code class="language-plaintext highlighter-rouge">)</code>.</p>

<p>This is why regex can’t parse SQL. Regex can’t count balanced parentheses. It’s mathematically proven (regular languages vs context-free languages).</p>

<hr />

<h2 id="the-abstract-syntax-tree">The Abstract Syntax Tree</h2>

<p>The AST represents the query’s structure in a clean, navigable tree.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">active_users</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">email</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'active'</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>CreateTableAsSelect
├── table_name: "active_users"
└── query:
    └── SelectStatement
        ├── columns:
        │   ├── Column { name: "id" }
        │   ├── Column { name: "name" }
        │   └── Column { name: "email" }
        ├── from:
        │   └── Table { name: "users" }
        └── where:
            └── BinaryOp
                ├── left: Column { name: "status" }
                ├── op: Equals
                └── right: Literal { value: "active" }
</code></pre></div></div>

<p>The AST is the <strong>central data structure</strong>. Everything downstream (analysis, transformation, code generation) operates on it.</p>

<h3 id="what-you-can-do-with-an-ast">What You Can Do With an AST</h3>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Traversal</strong></td>
      <td>Walk the tree, collect information</td>
    </tr>
    <tr>
      <td><strong>Transform</strong></td>
      <td>Rewrite nodes (add filters, change structure)</td>
    </tr>
    <tr>
      <td><strong>Generate</strong></td>
      <td>Convert back to SQL string (round-trip)</td>
    </tr>
    <tr>
      <td><strong>Transpile</strong></td>
      <td>Generate SQL for a different dialect</td>
    </tr>
    <tr>
      <td><strong>Lineage</strong></td>
      <td>Trace where data comes from</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="syntax-vs-semantics">Syntax vs Semantics</h2>

<p>This is where things get interesting.</p>

<p><strong>Syntactic analysis</strong> (parsing): Is this valid SQL grammar?</p>

<p><strong>Semantic analysis</strong>: Does this SQL make sense given the database schema?</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">active_users</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">email</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'active'</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Syntactic (Parser answers)</th>
      <th>Semantic (Analyzer answers)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>✓ Valid CTAS structure</td>
      <td>Does ‘users’ table exist?</td>
    </tr>
    <tr>
      <td>✓ Valid SELECT clause</td>
      <td>Does ‘users’ have ‘id’, ‘name’, ‘email’ columns?</td>
    </tr>
    <tr>
      <td>✓ Valid WHERE condition</td>
      <td>Is ‘status’ a valid column?</td>
    </tr>
    <tr>
      <td>✓ Correct keyword order</td>
      <td>Is comparing status to string valid?</td>
    </tr>
  </tbody>
</table>

<p><strong>Key insight</strong>: A parser with no schema information can only do syntactic analysis. Semantic analysis requires external knowledge about the database.</p>

<h3 id="what-syntactic-analysis-catches">What Syntactic Analysis Catches</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">SELECT</span> <span class="k">FROM</span> <span class="n">users</span>        <span class="c1">-- Missing column list</span>
<span class="k">FROM</span> <span class="n">users</span> <span class="k">SELECT</span> <span class="o">*</span>      <span class="c1">-- Wrong keyword order</span>
<span class="k">SELECT</span> <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span>            <span class="c1">-- Unbalanced parentheses</span>
</code></pre></div></div>

<h3 id="what-syntactic-analysis-cannot-catch">What Syntactic Analysis Cannot Catch</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">nonexistent</span>    <span class="c1">-- Parser doesn't know if table exists</span>
<span class="k">SELECT</span> <span class="n">foo</span> <span class="k">FROM</span> <span class="n">users</span>        <span class="c1">-- Parser doesn't know columns</span>
<span class="k">WHERE</span> <span class="n">name</span> <span class="o">&gt;</span> <span class="mi">5</span>               <span class="c1">-- Parser doesn't know types</span>
</code></pre></div></div>

<hr />

<h2 id="column-level-lineage">Column-Level Lineage</h2>

<p>Lineage traces where data comes from and where it goes.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">active_users</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">email</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'active'</span>
</code></pre></div></div>

<p><strong>Table-level lineage</strong> (easy, no schema needed):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>READ:  [users]
WRITE: [active_users]
</code></pre></div></div>

<p><strong>Column-level lineage</strong> (needs schema):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>active_users.id    ← users.id (direct)
active_users.name  ← users.name (direct)
active_users.email ← users.email (direct)
</code></pre></div></div>

<h3 id="data-flow-vs-control-flow">Data Flow vs Control Flow</h3>

<p>This is where lineage gets nuanced.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">active_users</span> <span class="k">AS</span> <span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">email</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">status</span> <span class="o">=</span> <span class="s1">'active'</span>
</code></pre></div></div>

<p><strong>Question</strong>: Does <code class="language-plaintext highlighter-rouge">status</code> contribute to the lineage of <code class="language-plaintext highlighter-rouge">active_users</code>?</p>

<table>
  <thead>
    <tr>
      <th>Perspective</th>
      <th><code class="language-plaintext highlighter-rouge">status</code> in lineage?</th>
      <th>Reasoning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Data Flow</strong></td>
      <td>❌ No</td>
      <td><code class="language-plaintext highlighter-rouge">status</code> doesn’t appear in output columns</td>
    </tr>
    <tr>
      <td><strong>Control Flow</strong></td>
      <td>✅ Yes</td>
      <td><code class="language-plaintext highlighter-rouge">status</code> affects which rows are included</td>
    </tr>
  </tbody>
</table>

<p>Most “lineage” discussions mean data flow. But impact analysis needs both. Changing <code class="language-plaintext highlighter-rouge">status</code> column could break this query even though it’s not in the output.</p>

<hr />

<h2 id="sql-dialects">SQL Dialects</h2>

<p>SQL is a “standard” that nobody fully implements.</p>

<h3 id="the-dialect-landscape">The Dialect Landscape</h3>

<p>Every database vendor implements a subset of the SQL standard, adds proprietary extensions, and has different syntax for the same operations.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>                         ┌─────────────────┐
                         │  SQL Standard   │
                         │  (SQL-92, 99,   │
                         │   2003, 2016)   │
                         └────────┬────────┘
                                  │
         ┌──────────┬─────────┬───┴───┬─────────┬──────────┐
         ▼          ▼         ▼       ▼         ▼          ▼
   ┌──────────┐┌──────────┐┌──────┐┌──────┐┌──────────┐┌──────────┐
   │PostgreSQL││  MySQL   ││Oracle││SQL   ││Snowflake ││ BigQuery │
   │          ││          ││      ││Server││          ││          │
   │ +ARRAY   ││ +LIMIT   ││+ROWNUM│ +TOP ││ +FLATTEN ││ +STRUCT  │
   │ +JSONB   ││ +BACKTICK││+DUAL ││ +[]  ││ +VARIANT ││ +UNNEST  │
   │ +::cast  ││ +AUTO_INC││+PLSQL││+T-SQL││ +$$      ││ +SAFE_   │
   └──────────┘└──────────┘└──────┘└──────┘└──────────┘└──────────┘

   Each dialect: ~80% common SQL + ~20% proprietary extensions
</code></pre></div></div>

<p>This fragmentation is why so many SQL parsers exist. A parser built for PostgreSQL won’t understand MySQL’s backtick identifiers. A parser built for standard SQL won’t handle Snowflake’s <code class="language-plaintext highlighter-rouge">FLATTEN</code> function.</p>

<h3 id="identifier-quoting">Identifier Quoting</h3>

<table>
  <thead>
    <tr>
      <th>Dialect</th>
      <th>Quote Style</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Standard SQL</td>
      <td><code class="language-plaintext highlighter-rouge">"double quotes"</code></td>
      <td><code class="language-plaintext highlighter-rouge">SELECT "Column" FROM "Table"</code></td>
    </tr>
    <tr>
      <td>MySQL</td>
      <td><code class="language-plaintext highlighter-rouge">`backticks`</code></td>
      <td><code class="language-plaintext highlighter-rouge">SELECT `Column` FROM `Table`</code></td>
    </tr>
    <tr>
      <td>SQL Server</td>
      <td><code class="language-plaintext highlighter-rouge">[brackets]</code></td>
      <td><code class="language-plaintext highlighter-rouge">SELECT [Column] FROM [Table]</code></td>
    </tr>
    <tr>
      <td>BigQuery</td>
      <td><code class="language-plaintext highlighter-rouge">`backticks`</code></td>
      <td><code class="language-plaintext highlighter-rouge">SELECT `Column` FROM `Table`</code></td>
    </tr>
  </tbody>
</table>

<h3 id="limit--pagination">LIMIT / Pagination</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c1">-- MySQL, PostgreSQL, SQLite</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">LIMIT</span> <span class="mi">10</span> <span class="k">OFFSET</span> <span class="mi">5</span>

<span class="c1">-- SQL Server</span>
<span class="k">SELECT</span> <span class="n">TOP</span> <span class="mi">10</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">users</span>
<span class="c1">-- Or (SQL Server 2012+)</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">ORDER</span> <span class="k">BY</span> <span class="n">id</span> <span class="k">OFFSET</span> <span class="mi">5</span> <span class="k">ROWS</span> <span class="k">FETCH</span> <span class="k">NEXT</span> <span class="mi">10</span> <span class="k">ROWS</span> <span class="k">ONLY</span>

<span class="c1">-- Oracle (traditional)</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">WHERE</span> <span class="n">ROWNUM</span> <span class="o">&lt;=</span> <span class="mi">10</span>
<span class="c1">-- Oracle 12c+</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">users</span> <span class="k">FETCH</span> <span class="k">FIRST</span> <span class="mi">10</span> <span class="k">ROWS</span> <span class="k">ONLY</span>
</code></pre></div></div>

<h3 id="type-casting">Type Casting</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="syntax"><code><span class="c1">-- Standard SQL</span>
<span class="k">CAST</span><span class="p">(</span><span class="n">x</span> <span class="k">AS</span> <span class="nb">INTEGER</span><span class="p">)</span>

<span class="c1">-- PostgreSQL</span>
<span class="n">x</span><span class="p">::</span><span class="nb">INTEGER</span>

<span class="c1">-- MySQL</span>
<span class="k">CAST</span><span class="p">(</span><span class="n">x</span> <span class="k">AS</span> <span class="nb">SIGNED</span><span class="p">)</span>  <span class="c1">-- No INTEGER, use SIGNED/UNSIGNED</span>

<span class="c1">-- BigQuery</span>
<span class="k">CAST</span><span class="p">(</span><span class="n">x</span> <span class="k">AS</span> <span class="n">INT64</span><span class="p">)</span>
<span class="n">SAFE_CAST</span><span class="p">(</span><span class="n">x</span> <span class="k">AS</span> <span class="n">INT64</span><span class="p">)</span>  <span class="c1">-- Returns NULL instead of error</span>
</code></pre></div></div>

<h3 id="function-name-differences">Function Name Differences</h3>

<p>The same operation, different names across dialects:</p>

<table>
  <thead>
    <tr>
      <th>Operation</th>
      <th>PostgreSQL</th>
      <th>MySQL</th>
      <th>SQL Server</th>
      <th>Snowflake</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Current timestamp</td>
      <td><code class="language-plaintext highlighter-rouge">NOW()</code></td>
      <td><code class="language-plaintext highlighter-rouge">NOW()</code></td>
      <td><code class="language-plaintext highlighter-rouge">GETDATE()</code></td>
      <td><code class="language-plaintext highlighter-rouge">CURRENT_TIMESTAMP()</code></td>
    </tr>
    <tr>
      <td>String length</td>
      <td><code class="language-plaintext highlighter-rouge">LENGTH()</code></td>
      <td><code class="language-plaintext highlighter-rouge">LENGTH()</code></td>
      <td><code class="language-plaintext highlighter-rouge">LEN()</code></td>
      <td><code class="language-plaintext highlighter-rouge">LENGTH()</code></td>
    </tr>
    <tr>
      <td>If null</td>
      <td><code class="language-plaintext highlighter-rouge">COALESCE()</code></td>
      <td><code class="language-plaintext highlighter-rouge">IFNULL()</code></td>
      <td><code class="language-plaintext highlighter-rouge">ISNULL()</code></td>
      <td><code class="language-plaintext highlighter-rouge">NVL()</code></td>
    </tr>
    <tr>
      <td>Date add</td>
      <td><code class="language-plaintext highlighter-rouge">+ INTERVAL '1 day'</code></td>
      <td><code class="language-plaintext highlighter-rouge">DATE_ADD()</code></td>
      <td><code class="language-plaintext highlighter-rouge">DATEADD()</code></td>
      <td><code class="language-plaintext highlighter-rouge">DATEADD()</code></td>
    </tr>
  </tbody>
</table>

<h3 id="how-parsers-handle-dialects">How Parsers Handle Dialects</h3>

<p><strong>Dialect flags</strong> (sqlparser-rs): Single parser with ~50 boolean flags like <code class="language-plaintext highlighter-rouge">supports_filter_during_aggregation</code>, <code class="language-plaintext highlighter-rouge">supports_group_by_expr</code>. Simple but can’t handle major syntax differences.</p>

<p><strong>Parameterized grammar</strong> (SQLGlot): Base parser with overridable methods per dialect. Each dialect class inherits and overrides specific parsing methods. Flexible but has complex inheritance.</p>

<p><strong>Separate grammars</strong> (Gudusoft): One complete grammar file per database. Complete accuracy but high maintenance burden. When a database releases a new version, you update that grammar file.</p>

<hr />

<h2 id="parsers-vs-query-engines">Parsers vs Query Engines</h2>

<p>This distinction trips people up. A parser and a query engine are not the same thing.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>┌─────────────────────────────┐    ┌─────────────────────────────────┐
│         PARSER              │    │         QUERY ENGINE            │
│                             │    │                                 │
│  • Lexical analysis         │    │  • Query planning               │
│  • Syntactic analysis       │    │  • Query optimization           │
│  • AST construction         │    │  • Physical execution           │
│  • (Optional) Semantic      │    │  • Data access                  │
│    analysis                 │    │  • Join algorithms              │
│                             │    │  • Aggregation                  │
│  INPUT: SQL string          │    │  • Sorting                      │
│  OUTPUT: AST or errors      │    │  • Result materialization       │
│                             │    │                                 │
│  NO data access             │    │  READS/WRITES data              │
│  NO execution               │    │  EXECUTES query                 │
└─────────────────────────────┘    └─────────────────────────────────┘

Examples:                          Examples:
• SQLGlot                          • PostgreSQL
• sqlparser-rs                     • DuckDB
• JSqlParser                       • Apache Spark
                                   • Presto/Trino
</code></pre></div></div>

<h3 id="the-full-query-processing-pipeline">The Full Query Processing Pipeline</h3>

<p>When you run a query in a database, it goes through many stages. Parsing is just the first.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="syntax"><code>┌─────────────────┐
│    SQL String   │
└─────────────────┘
         │
         ▼
┌─────────────────┐
│     PARSER      │  ◄── SQLGlot, sqlparser-rs stop here
└────────┬────────┘
         │ AST
         ▼
┌─────────────────┐
│    ANALYZER     │  ◄── Semantic analysis (name resolution, types)
└────────┬────────┘
         │ Analyzed AST
         ▼
┌─────────────────┐
│    PLANNER      │  ◄── Logical plan (relational algebra)
└────────┬────────┘
         │ Logical Plan
         ▼
┌─────────────────┐
│   OPTIMIZER     │  ◄── Rule-based and cost-based optimization
└────────┬────────┘
         │ Optimized Plan
         ▼
┌─────────────────┐
│   EXECUTOR      │  ◄── Physical operators, data access
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    RESULTS      │
└─────────────────┘
</code></pre></div></div>

<h3 id="what-parsers-do">What Parsers Do</h3>

<table>
  <thead>
    <tr>
      <th>Capability</th>
      <th>Parser Does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Syntax validation</td>
      <td>✅ Detect <code class="language-plaintext highlighter-rouge">SELECT FROM</code> (missing columns)</td>
    </tr>
    <tr>
      <td>AST construction</td>
      <td>✅ Build tree structure</td>
    </tr>
    <tr>
      <td>Transpilation</td>
      <td>✅ Convert MySQL → PostgreSQL</td>
    </tr>
    <tr>
      <td>Lineage extraction</td>
      <td>✅ Find table/column dependencies</td>
    </tr>
    <tr>
      <td>Query formatting</td>
      <td>✅ Pretty-print SQL</td>
    </tr>
  </tbody>
</table>

<h3 id="what-parsers-dont-do">What Parsers Don’t Do</h3>

<table>
  <thead>
    <tr>
      <th>Capability</th>
      <th>Parser</th>
      <th>Engine</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Execute query</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Return results</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Optimize execution</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Choose join order</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Manage transactions</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
  </tbody>
</table>

<h3 id="why-this-matters">Why This Matters</h3>

<p>If you’re building a data catalog and need lineage, you need a parser. You don’t need a query engine.</p>

<p>If you’re building an IDE with autocomplete, you need a parser. You don’t need to execute anything.</p>

<p>If you’re building a transpiler to migrate queries from Snowflake to Databricks, you need a parser with good dialect support. You don’t need to run those queries.</p>

<p>The tools are different because the problems are different.</p>

<hr />

<h3 id="at-a-glance">At a Glance</h3>

<table>
  <thead>
    <tr>
      <th>Parser</th>
      <th>Language</th>
      <th>License</th>
      <th>Dialects</th>
      <th>One-liner</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>SQLGlot</strong></td>
      <td>Python</td>
      <td>MIT</td>
      <td>31</td>
      <td>Most feature-complete open-source</td>
    </tr>
    <tr>
      <td><strong>sqlparser-rs</strong></td>
      <td>Rust</td>
      <td>Apache 2.0</td>
      <td>~15</td>
      <td>Fast, minimal, foundation for Rust engines</td>
    </tr>
    <tr>
      <td><strong>Apache Calcite</strong></td>
      <td>Java</td>
      <td>Apache 2.0</td>
      <td>~10</td>
      <td>Full query planning framework</td>
    </tr>
    <tr>
      <td><strong>Gudusoft GSP</strong></td>
      <td>Java/C#</td>
      <td>Commercial</td>
      <td>25+</td>
      <td>Enterprise, stored procedure support</td>
    </tr>
    <tr>
      <td><strong>JSqlParser</strong></td>
      <td>Java</td>
      <td>Apache/LGPL</td>
      <td>~6</td>
      <td>Mature, simple Java parser</td>
    </tr>
  </tbody>
</table>

<h3 id="layer-support">Layer Support</h3>

<table>
  <thead>
    <tr>
      <th>Parser</th>
      <th>Lexer</th>
      <th>Parser</th>
      <th>AST</th>
      <th>Semantic</th>
      <th>Lineage</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>SQLGlot</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>sqlparser-rs</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>Calcite</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>⚠️</td>
    </tr>
    <tr>
      <td>Gudusoft GSP</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>JSqlParser</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>
  </tbody>
</table>

<h3 id="features">Features</h3>

<table>
  <thead>
    <tr>
      <th>Parser</th>
      <th>Transpile</th>
      <th>Format</th>
      <th>Lineage</th>
      <th>Schema</th>
      <th>Round-trip</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>SQLGlot</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>sqlparser-rs</td>
      <td>❌</td>
      <td>⚠️</td>
      <td>❌</td>
      <td>❌</td>
      <td>⚠️</td>
    </tr>
    <tr>
      <td>Calcite</td>
      <td>⚠️</td>
      <td>❌</td>
      <td>⚠️</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>Gudusoft GSP</td>
      <td>⚠️</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
    <tr>
      <td>JSqlParser</td>
      <td>❌</td>
      <td>⚠️</td>
      <td>❌</td>
      <td>❌</td>
      <td>✅</td>
    </tr>
  </tbody>
</table>

<h3 id="quick-decision-matrix">Quick Decision Matrix</h3>

<table>
  <thead>
    <tr>
      <th>Your Situation</th>
      <th>Recommended</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Python, need transpilation</td>
      <td>SQLGlot</td>
    </tr>
    <tr>
      <td>Python, need lineage</td>
      <td>SQLGlot</td>
    </tr>
    <tr>
      <td>Rust query engine</td>
      <td>sqlparser-rs</td>
    </tr>
    <tr>
      <td>Browser-based SQL tool</td>
      <td>sqlparser-rs (WASM)</td>
    </tr>
    <tr>
      <td>Java, basic parsing</td>
      <td>JSqlParser</td>
    </tr>
    <tr>
      <td>Java, query planning</td>
      <td>Apache Calcite</td>
    </tr>
    <tr>
      <td>Enterprise, stored procs</td>
      <td>Gudusoft GSP</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="further-reading">Further Reading</h2>

<p>Still Interested? There’s more to read. Hopefully I have piqued your interest. For any comments or feedback, please reach out to and I’ll address them.</p>

<h3 id="parsing-algorithms">Parsing Algorithms</h3>

<ul>
  <li><a href="https://en.wikipedia.org/wiki/Recursive_descent_parser">Recursive Descent</a> – Top-down, hand-written parsers. Most SQL parsers use this.</li>
  <li><a href="https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing">Pratt Parsing</a> – Elegant handling of operator precedence.</li>
  <li><a href="https://en.wikipedia.org/wiki/LR_parser">LR Parsing</a> – Bottom-up, table-driven. Used by parser generators like Bison.</li>
  <li><a href="https://en.wikipedia.org/wiki/Parser_combinator">Parser Combinators</a> – Functional composition of parsers.</li>
</ul>

<h3 id="parser-libraries">Parser Libraries</h3>

<ul>
  <li><a href="https://github.com/tobymao/sqlglot">SQLGlot</a> – Python, 31 dialects, transpilation, lineage</li>
  <li><a href="https://github.com/sqlparser-rs/sqlparser-rs">sqlparser-rs</a> – Rust, fast, WASM support</li>
  <li><a href="https://calcite.apache.org/">Apache Calcite</a> – Java, full query planning</li>
  <li><a href="https://github.com/JSQLParser/JSqlParser">JSqlParser</a> – Java, mature, simple</li>
  <li><a href="https://www.gudusoft.com/">Gudusoft GSP</a> – Commercial, enterprise features</li>
</ul>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p><a href="https://www.tobikodata.com/blog/sqlglot-jumps-on-the-rust-bandwagon#:~:text=pip%20install%20%22sqlglot%5Brs%5D,Looking%20for%20Feedback">SQLGlot Jumps on the Rust Bandwagon</a> <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>nishchith shetty</name></author><summary type="html"><![CDATA[It’s rare to talk about heuristic systems when there’s so much hype around probabilistic ones. This post is boring, but stay with me.]]></summary></entry><entry><title type="html">tiny notes</title><link href="https://nishchith.com/tiny-notes/" rel="alternate" type="text/html" title="tiny notes" /><published>2024-10-12T12:58:45+00:00</published><updated>2024-10-12T12:58:45+00:00</updated><id>https://nishchith.com/tiny-notes</id><content type="html" xml:base="https://nishchith.com/tiny-notes/"><![CDATA[<ul>
  <li>exercise everyday</li>
  <li>question everything, seek truth</li>
  <li>run tiny experiments</li>
  <li>inspiration is perishable</li>
  <li>close the loop</li>
  <li>read the labels</li>
  <li>people will scold you for trying to chase perfection, don’t listen.</li>
  <li>balance the chase to perfection through iteration</li>
  <li>avoid making boring mistakes</li>
  <li>talk less. do more. be decisive when the time comes.</li>
  <li>no adults in the room</li>
  <li>have a maniacal sense of urgency</li>
</ul>]]></content><author><name>nishchith shetty</name></author><summary type="html"><![CDATA[exercise everyday question everything, seek truth run tiny experiments inspiration is perishable close the loop read the labels people will scold you for trying to chase perfection, don’t listen. balance the chase to perfection through iteration avoid making boring mistakes talk less. do more. be decisive when the time comes. no adults in the room have a maniacal sense of urgency]]></summary></entry><entry><title type="html">Setup server alerts using webhooks</title><link href="https://nishchith.com/server-alerts-using-webhooks/" rel="alternate" type="text/html" title="Setup server alerts using webhooks" /><published>2020-12-20T04:54:32+00:00</published><updated>2020-12-20T04:54:32+00:00</updated><id>https://nishchith.com/server-alerts-using-webhooks</id><content type="html" xml:base="https://nishchith.com/server-alerts-using-webhooks/"><![CDATA[<p>If you’re using self-hosted servers, you might have run into <a href="https://feross.org/how-to-setup-your-linode/">this</a> (or similar) blog which covers most of the things you need to do on your first login to the server.</p>

<p>Over the past weeks, I’ve failed to setup mail alerts on ssh login, sudo, and other events due to the various cloud providers blocking the <code class="language-plaintext highlighter-rouge">SMTP</code> ports for <a href="https://www.linode.com/blog/linode/a-new-policy-to-help-fight-spam/">security reasons</a> and making it difficult to setup a Mail Transfer Agent (MTA) quickly<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>

<p>Slack alerts seemed to be the next logical step, and it takes considerably less time to setup<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. We’ll be leveraging Unix systems’ Pluggable Authentication Module (PAM) – which can be configured under <code class="language-plaintext highlighter-rouge">/etc/pam.d</code> – to setup slack alert on ssh login and logout events.</p>

<p>The following steps shall guide you to easily setup the same and maybe adapt the process to other services like discord, telegram, or what have you.</p>

<ul>
  <li>Setup incoming webhook in slack
    <ul>
      <li>Follow the instructions under the <code class="language-plaintext highlighter-rouge">Getting Started</code> section on <a href="https://api.slack.com/incoming-webhooks">slack’s webhook documentation</a> for creating an app and tieing it to a <code class="language-plaintext highlighter-rouge">#channel</code> under your desired workspace. This should land you with a <code class="language-plaintext highlighter-rouge">Webhook URL</code>.</li>
    </ul>
  </li>
  <li>We’ll use the following script which sends a <code class="language-plaintext highlighter-rouge">POST</code> request with the details (<code class="language-plaintext highlighter-rouge">IP ADDRESS</code>, <code class="language-plaintext highlighter-rouge">HOSTNAME</code>) on either <code class="language-plaintext highlighter-rouge">open_session</code> (login) or <code class="language-plaintext highlighter-rouge">close_session</code> (logout) event as payload to the <code class="language-plaintext highlighter-rouge">WEBHOOK URL</code>.</li>
</ul>

<figure class="highlight">
  <pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/bash</span>

<span class="nv">WEBHOOK_URL</span><span class="o">=</span><span class="s2">"&lt;WEBHOOK_URL&gt;"</span>
<span class="nv">CHANNEL</span><span class="o">=</span><span class="s2">"#&lt;CHANNEL_NAME&gt;"</span>
<span class="nv">HOST</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span><span class="nb">hostname</span><span class="si">)</span><span class="s2">"</span>

<span class="k">if</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$PAM_TYPE</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"open_session"</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$PAM_TYPE</span><span class="s2">"</span> <span class="o">==</span> <span class="s2">"close_session"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
    </span><span class="nv">content</span><span class="o">=</span><span class="s2">"</span><span class="se">\"</span><span class="s2">attachments</span><span class="se">\"</span><span class="s2">: [{ 
        </span><span class="se">\"</span><span class="s2">mrkdwn_in</span><span class="se">\"</span><span class="s2">: [</span><span class="se">\"</span><span class="s2">text</span><span class="se">\"</span><span class="s2">, </span><span class="se">\"</span><span class="s2">fallback</span><span class="se">\"</span><span class="s2">], 
        </span><span class="se">\"</span><span class="s2">fallback</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">Event : </span><span class="nv">$PAM_TYPE</span><span class="s2"> to </span><span class="se">\`</span><span class="nv">$HOST</span><span class="se">\`\"</span><span class="s2">, 
        </span><span class="se">\"</span><span class="s2">text</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">SSH: </span><span class="nv">$PAM_TYPE</span><span class="s2"> to </span><span class="se">\`</span><span class="nv">$HOST</span><span class="se">\`\"</span><span class="s2">, 
        </span><span class="se">\"</span><span class="s2">fields</span><span class="se">\"</span><span class="s2">: [ { 
                </span><span class="se">\"</span><span class="s2">title</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">User</span><span class="se">\"</span><span class="s2">, 
                </span><span class="se">\"</span><span class="s2">value</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="nv">$PAM_USER</span><span class="se">\"</span><span class="s2">, 
                </span><span class="se">\"</span><span class="s2">short</span><span class="se">\"</span><span class="s2">: true 
            }, { 
                </span><span class="se">\"</span><span class="s2">title</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">IP Address</span><span class="se">\"</span><span class="s2">, 
                </span><span class="se">\"</span><span class="s2">value</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="nv">$PAM_RHOST</span><span class="se">\"</span><span class="s2">, 
                </span><span class="se">\"</span><span class="s2">short</span><span class="se">\"</span><span class="s2">: true 
        } ],
        </span><span class="se">\"</span><span class="s2">color</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">#f30c00</span><span class="se">\"</span><span class="s2"> 
    }]"</span>
    curl <span class="nt">-X</span> POST <span class="nt">--data-urlencode</span> <span class="se">\</span>
        <span class="s2">"payload={
                </span><span class="se">\"</span><span class="s2">channel</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="nv">$CHANNEL</span><span class="se">\"</span><span class="s2">,
                </span><span class="se">\"</span><span class="s2">mrkdwn</span><span class="se">\"</span><span class="s2">: true, 
                </span><span class="se">\"</span><span class="s2">username</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">SSH Notifications</span><span class="se">\"</span><span class="s2">, 
                </span><span class="nv">$content</span><span class="s2">, 
                </span><span class="se">\"</span><span class="s2">icon_emoji</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">:warning:</span><span class="se">\"</span><span class="s2">}"</span> <span class="se">\</span>
        <span class="s2">"</span><span class="nv">$WEBHOOK_URL</span><span class="s2">"</span> &amp;
<span class="k">fi
</span><span class="nb">exit</span></code></pre>
</figure>

<ul>
  <li>
    <p>You can name the script anything you want and place it anywhere; For this example, I’ve placed it in <code class="language-plaintext highlighter-rouge">/usr/local/sbin/ssh-slack</code></p>
  </li>
  <li>
    <p>Make the script executable.</p>
  </li>
</ul>

<figure class="highlight">
  <pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">chmod</span> +x /usr/local/sbin/ssh-slack</code></pre>
</figure>

<ul>
  <li>One of the modules of PAM - <code class="language-plaintext highlighter-rouge">pam_exec.so</code> helps us trigger the scripts based on various authentication events<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>. We’ll add the path to our script under <code class="language-plaintext highlighter-rouge">/etc/pam.d/sshd</code> which will trigger our script on any ssh authentication-related events.</li>
</ul>

<figure class="highlight">
  <pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span><span class="nb">sudo echo</span> <span class="s2">"session   optional   pam_exec.so   /usr/local/sbin/ssh-slack"</span> <span class="o">&gt;&gt;</span> /etc/pam.d/sshd</code></pre>
</figure>

<ul>
  <li>That’s It! You should have the slack alerts working now.</li>
</ul>

<p><br /></p>

<p><strong><code class="language-plaintext highlighter-rouge">Note</code></strong></p>
<ul>
  <li>The process described isn’t limited to ssh authentication-related events; We can configure it to work with other deamons like fail2ban and others to setup alerts or send logs to services on critical events.</li>
</ul>

<p><br /></p>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Common alternatives include using services like SendGrid or Mailgun, but they often require additional configuration and API keys. Webhooks are simpler for this use case. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>The setup time is typically under 10 minutes compared to hours spent troubleshooting SMTP configurations and firewall rules. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>PAM modules are loaded dynamically and can be configured for various authentication, authorization, and session management tasks. The <code class="language-plaintext highlighter-rouge">pam_exec.so</code> module is particularly useful for running external scripts during authentication events. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>nishchith shetty</name></author><summary type="html"><![CDATA[If you’re using self-hosted servers, you might have run into this (or similar) blog which covers most of the things you need to do on your first login to the server.]]></summary></entry><entry><title type="html">Accepted for Google Summer of Code</title><link href="https://nishchith.com/acceptance-gsoc/" rel="alternate" type="text/html" title="Accepted for Google Summer of Code" /><published>2019-10-19T04:54:32+00:00</published><updated>2019-10-19T04:54:32+00:00</updated><id>https://nishchith.com/acceptance-gsoc</id><content type="html" xml:base="https://nishchith.com/acceptance-gsoc/"><![CDATA[<p>I got accepted for Google Summer of Code 2019 under <a href="https://github.com/chaoss">CHAOSS</a>: A linux foundation, for the project: “<a href="https://github.com/chaoss/grimoirelab/issues/182">Support of Source Code Related Metrics</a>”. I’ll be working towards adding <a href="https://github.com/chaoss/grimoirelab-graal/">Graal</a> to the GrimoireLab toolchain in order to produce source code related metrics.</p>

<p>My mentors for the project will be: <a href="https://github.com/valeriocos">@valeriocos</a> <a href="https://github.com/jgbarah">@jgbarah</a> <a href="https://github.com/aswanipranjal">@aswanipranjal</a></p>

<h2> <b># What would I be working on? </b> </h2>

<ul>
  <li>I will mainly be focusing on:
    <ul>
      <li>Adding support of source code related metrics to Grimoirelab with the help of analysis data produced by Graal.</li>
      <li>Adapting Grimoirelab toolchain to be able to execute Graal and process the data produced by it.</li>
      <li>Writing appropriate unit tests for additional backends, their corresponding supporting connectors, and methods.</li>
      <li>Producing analytics related to proposed and calculated metrics*.</li>
      <li>Adding documentation related to additional features and improvements in existing ones.</li>
    </ul>
  </li>
</ul>

<h2> <b># What this is all about? </b> </h2>

<ul>
  <li><strong>This</strong> is me learning to manage a project, keeping track of the incremental process of development and documenting the work, which would later on (in the process) help to understand and explain things in a better way to newcomer or a community member.</li>
  <li>Also there will be some of the things that i’ll be learning throughout the process for the first time, will be following some guidelines, some of the best practices, which i’ll try and share.</li>
</ul>

<h2> <b># Community Bonding Period: Meeting </b> </h2>

<p>We had our 1st meeting for the community bonding period on Monday, 13th May 2019 at 14:00 CEST or 17:30 IST ; which was intended to answer the following questions:</p>

<ul>
  <li>
    <h3> <b> How do we keep a track of what’s being worked on? </b> </h3>

    <ul>
      <li>One of my mentors (<a href="https://github.com/aswanipranjal">@aswanipranjal</a>) for the project had shared an extensive list of guidelines (which i found really helpful) which included ideas such as maintaining a project-tracker repository which can work as a lab for experiment and a log for all the things that happens related to the project during the period. I liked the idea and I made one 😛 <a href="https://github.com/inishchith/gsoc-graal">gsoc-graal</a></li>
    </ul>
  </li>
  <li>
    <h3> <b> How do we keep the community aware of the same? </b> </h3>

    <ul>
      <li>
        <p>We have planned to keep all the communication open so that everyone can sync and is free to participate and help us grow! If you have suggestions / comments about anything please do not hesitate to share them with us.</p>
      </li>
      <li>
        <p>We will be discussing about the progress of this project every week at the #grimoirelab channel on Freenode IRC.</p>
      </li>
      <li>
        <p>There will be a weekly report added to the project tracker, which i’ll make sure to also post on the <a href="https://lists.linuxfoundation.org/mailman/listinfo/oss-health-metrics">mailing lists</a> of CHAOSS.</p>
      </li>
    </ul>
  </li>
</ul>

<p><br /></p>

<h2 id="-update-"><b> <code class="language-plaintext highlighter-rouge">Update:</code> </b></h2>

<ul>
  <li>I successfully completed my Google Summer of Code project with CHAOSS on 22nd August 2019.
    <blockquote>
      <iframe width="90%" height="300" src="https://www.youtube.com/embed/RXZeuJt0UXM" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
    </blockquote>
  </li>
  <li>All the project related information can be found in the <a href="https://github.com/inishchith/gsoc-graal"> project tracker</a>.</li>
</ul>

<p><br /></p>]]></content><author><name>nishchith shetty</name></author><summary type="html"><![CDATA[I got accepted for Google Summer of Code 2019 under CHAOSS: A linux foundation, for the project: “Support of Source Code Related Metrics”. I’ll be working towards adding Graal to the GrimoireLab toolchain in order to produce source code related metrics.]]></summary></entry></feed>