<?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: JoongHyuk Shin</title>
    <description>The latest articles on DEV Community by JoongHyuk Shin (@joonghyuk_shin_28874f3573).</description>
    <link>https://dev.to/joonghyuk_shin_28874f3573</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%2F3911251%2F63b302ae-0e4c-4fa7-916b-72b2a119b393.png</url>
      <title>DEV Community: JoongHyuk Shin</title>
      <link>https://dev.to/joonghyuk_shin_28874f3573</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/joonghyuk_shin_28874f3573"/>
    <language>en</language>
    <item>
      <title>1.1.1 Life of a Query</title>
      <dc:creator>JoongHyuk Shin</dc:creator>
      <pubDate>Mon, 04 May 2026 10:13:22 +0000</pubDate>
      <link>https://dev.to/joonghyuk_shin_28874f3573/111-life-of-a-query-1phg</link>
      <guid>https://dev.to/joonghyuk_shin_28874f3573/111-life-of-a-query-1phg</guid>
      <description>&lt;p&gt;This section is the map for the rest of the book. The five stages introduced in the 1.1 chapter overview (parse, analyze/rewrite, plan, portal, execute) are traced here through the actual code: which functions implement each stage, and in what order they get called. The mechanics of each of the five stages are unpacked in later chapters. Here, only the skeleton matters: how a backend starts up, how it receives messages, and where the first fork in the road appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  One backend process owns one query
&lt;/h2&gt;

&lt;p&gt;Every time a client connects, PostgreSQL forks a &lt;strong&gt;backend process&lt;/strong&gt; for it (the parent is &lt;code&gt;postmaster&lt;/code&gt;). That process stays alive until the client disconnects, and it handles every query that client sends, by itself. Unlike the thread-pool model common in other RDBMSs, PG uses one OS process per connection. The reasons behind that decision are taken up in 6.1.1.&lt;/p&gt;

&lt;p&gt;The actual entry point of that backend is a function called &lt;code&gt;PostgresMain&lt;/code&gt;. The name is grand; what it does is unexpectedly simple. Two things, then off it goes.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;it installs signal handlers&lt;/strong&gt;. Signals are asynchronous notifications the OS delivers to a process (for example, &lt;code&gt;SIGTERM&lt;/code&gt; is a request to shut down, &lt;code&gt;SIGUSR1&lt;/code&gt; is for PG-internal communication). A backend has to react to signals from &lt;code&gt;postmaster&lt;/code&gt; and from other backends, so each signal is wired to a handler ahead of time. Signals and IPC in general are covered in 6.3.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;it initializes the transaction system&lt;/strong&gt;. Every SQL statement in PG, even without an explicit &lt;code&gt;BEGIN&lt;/code&gt;, runs inside some transaction. The transaction system is the core PG machinery that tracks &lt;code&gt;BEGIN&lt;/code&gt;/&lt;code&gt;COMMIT&lt;/code&gt; boundaries, MVCC visibility, XID assignment, and so on. Transactions and MVCC are the subject of all of chapter 3. For now, it's enough to know that this machinery is set up before the backend ever sees a SQL statement.&lt;/p&gt;

&lt;p&gt;Once those two preparations are done, the real work of the backend begins. An infinite loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&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="n"&gt;ReadyForQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;whereToSendOutput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="n"&gt;firstchar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReadCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input_message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstchar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PqMsg_Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="c1"&gt;// 'Q', simple query&lt;/span&gt;
            &lt;span class="n"&gt;exec_simple_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PqMsg_Parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="c1"&gt;// 'P', extended: parse&lt;/span&gt;
            &lt;span class="n"&gt;exec_parse_message&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PqMsg_Bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;// 'B', extended: bind&lt;/span&gt;
            &lt;span class="n"&gt;exec_bind_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;input_message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PqMsg_Execute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;// 'E', extended: execute&lt;/span&gt;
            &lt;span class="n"&gt;exec_execute_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;portal_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PqMsg_Sync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="c1"&gt;// 'S', end of an extended cycle&lt;/span&gt;
            &lt;span class="n"&gt;finish_xact_command&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;send_ready_for_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;break&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This loop is the entire life of a backend.&lt;/p&gt;

&lt;p&gt;"Announce that I'm ready, read one message, dispatch on its type." Repeat forever. When the client closes the connection, an &lt;code&gt;'X'&lt;/code&gt; (Terminate) message arrives, the loop exits, and the process dies.&lt;/p&gt;

&lt;p&gt;The first fork in the road is visible right here. There's the &lt;code&gt;'Q'&lt;/code&gt; path and the &lt;code&gt;'P' / 'B' / 'E'&lt;/code&gt; path. That split is the difference between the simple query protocol and the extended query protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple vs extended
&lt;/h2&gt;

&lt;p&gt;Simple is the case where a single message contains the SQL text in full. Type &lt;code&gt;SELECT 1;&lt;/code&gt; into &lt;code&gt;psql&lt;/code&gt; and hit enter, and that's what flies across the wire. The backend receives that one message and runs the full five-stage cycle (parse, analyze and rewrite, plan, portal, execute) before returning the result.&lt;/p&gt;

&lt;p&gt;Extended does the same job but splits it into four messages. The key concept around which it splits is the &lt;strong&gt;prepared statement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A prepared statement is a SQL template that has already been parsed and analyzed in advance. The places where values will be plugged in are left as placeholders such as &lt;code&gt;$1&lt;/code&gt; and &lt;code&gt;$2&lt;/code&gt;, and only the actual values get sent at execution time. For example, if you prepare &lt;code&gt;INSERT INTO users (id, name) VALUES ($1, $2)&lt;/code&gt;, you can run it later with just &lt;code&gt;(1, 'Alice')&lt;/code&gt;, &lt;code&gt;(2, 'Bob')&lt;/code&gt;, and so on. The full SQL text isn't reparsed each time. If you give the prepared statement a name, it becomes a &lt;strong&gt;named prepared statement&lt;/strong&gt;, and you can refer back to it by that name for the rest of the session.&lt;/p&gt;

&lt;p&gt;The four messages of the extended protocol are simply that flow split into wire-level pieces.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;'P'&lt;/code&gt; Parse&lt;/td&gt;
&lt;td&gt;Receives the SQL template, finishes parsing and analysis, stores the prepared statement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;'B'&lt;/code&gt; Bind&lt;/td&gt;
&lt;td&gt;Binds parameter values to the prepared statement and creates a portal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;'E'&lt;/code&gt; Execute&lt;/td&gt;
&lt;td&gt;Runs the portal and sends result rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;'S'&lt;/code&gt; Sync&lt;/td&gt;
&lt;td&gt;Ends the cycle and sends &lt;code&gt;ReadyForQuery&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What this means is that you can send &lt;code&gt;'B' + 'E'&lt;/code&gt; repeatedly with different parameters against the same prepared statement. Say you want to insert a thousand users.&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="c1"&gt;# Driver pseudocode: 1000 INSERTs via a prepared statement
&lt;/span&gt;&lt;span class="n"&gt;stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO users (id, name) VALUES ($1, $2)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;stmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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;conn.prepare(...)&lt;/code&gt; corresponds to one &lt;code&gt;'P'&lt;/code&gt; message. Parsing and planning happen there, just once. Each subsequent &lt;code&gt;stmt.execute(...)&lt;/code&gt; corresponds to a &lt;code&gt;'B' + 'E'&lt;/code&gt; pair. Parsing and planning are paid only once; the remaining 999 iterations cost only bind and execute. Under simple query, the same INSERT text would be reparsed and replanned every single time.&lt;/p&gt;

&lt;p&gt;A nice side effect is that SQL injection is shut out at the source. Parameter values never go through the SQL parser; they enter as plain data at bind time. Drivers like JDBC and psycopg2 expose &lt;code&gt;?&lt;/code&gt; or &lt;code&gt;$1&lt;/code&gt; placeholders precisely because they use this path internally.&lt;/p&gt;

&lt;p&gt;The semantic differences between simple and extended are unpacked further in section 1.1.2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizable vs utility
&lt;/h2&gt;

&lt;p&gt;Everything described so far assumes &lt;strong&gt;optimizable statements&lt;/strong&gt;: &lt;code&gt;SELECT/INSERT/UPDATE/DELETE&lt;/code&gt;. These have paths to optimize. The planner decides between sequential and index scan, hash join and nested loop, one join order or another.&lt;/p&gt;

&lt;p&gt;But statements like &lt;code&gt;CREATE TABLE&lt;/code&gt;, &lt;code&gt;VACUUM&lt;/code&gt;, &lt;code&gt;SET&lt;/code&gt;, and &lt;code&gt;BEGIN&lt;/code&gt; (the &lt;strong&gt;utility statements&lt;/strong&gt;) are different. There's nothing for a cost model to optimize. They're DDL or system commands, with no path to choose. In that case the planner produces only an empty shell of a plan and hands the actual work to a utility-statement handler. The executor never gets called on this path.&lt;/p&gt;

&lt;p&gt;The detailed branching is the subject of 1.1.3. The takeaway here is just one thing: not every query in PG goes through the planner.&lt;/p&gt;

&lt;h2&gt;
  
  
  The big picture
&lt;/h2&gt;

&lt;p&gt;We can now compress the journey of a SQL line into a single diagram.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client
   │
   │  'Q' (or 'P' + 'B' + 'E')
   ▼
PostgresMain main loop
   │
   ▼
exec_simple_query
   │
   ├─ pg_parse_query           → raw parse tree     (1.2.1, 1.2.3)
   │
   ├─ pg_analyze_and_rewrite   → list of Query nodes (1.2.2, 1.3)
   │
   ├─ pg_plan_queries          → execution plan      (1.4 cluster)
   │     └─ utility produces an empty shell          (1.1.3)
   │
   ├─ PortalStart + PortalRun  → tuple pulling       (1.5)
   │
   └─ PortalDrop + finish_xact_command
   │
   ▼
ReadyForQuery → back to the top of the loop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each box in this diagram corresponds to a chapter or cluster in the book. 1.2 is parser and analyzer, 1.3 is rewriter, 1.4 is the planner cluster (seven sections), 1.5 is the executor. All of part 1 is essentially one zoomed-in view of this diagram.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Working on another RDBMS engine, I once found this aspect of PG surprising. PG accepts a multi-statement query like &lt;code&gt;SELECT 1; SELECT 2;&lt;/code&gt; as a single simple-query message. What's even more surprising is the transaction handling. Without an explicit &lt;code&gt;BEGIN&lt;/code&gt;/&lt;code&gt;COMMIT&lt;/code&gt;, all those statements get bundled into a single implicit transaction block, and if even one of them fails, the whole batch rolls back.&lt;/p&gt;

&lt;p&gt;At first I assumed this was just standard behavior. Comparing the client protocols of other major databases made it clear this is a PG-specific decision. MySQL has &lt;code&gt;CLIENT_MULTI_STATEMENTS&lt;/code&gt; off by default, so multi-statement queries are simply rejected (you have to flip the flag explicitly because of SQL injection risk). Even with the flag on, statements are processed sequentially, and because autocommit is the default, each one commits as its own transaction. Oracle accepts only one statement per OCI call, so to bundle multiple statements you have to wrap them in an anonymous PL/SQL block (&lt;code&gt;BEGIN ... END;&lt;/code&gt;). SQL Server accepts multiple statements in a T-SQL batch, but atomic handling still requires an explicit &lt;code&gt;BEGIN TRANSACTION&lt;/code&gt;. None of the three does what PG does: bundle automatically as soon as the message arrives.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What this means in practice
&lt;/h2&gt;

&lt;p&gt;This five-stage skeleton turns out to be the foundation for three diagnostic tools you'll use in operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, you can see exactly where EXPLAIN's output comes from.&lt;/strong&gt; &lt;code&gt;EXPLAIN&lt;/code&gt; runs only as far as stage 4 (plan); it skips stage 5 (execute). &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; actually runs through stage 5 and measures it. That's why &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; produces real load and shouldn't be casually run in production: an &lt;code&gt;EXPLAIN ANALYZE UPDATE ...&lt;/code&gt; actually updates rows. The familiar &lt;code&gt;BEGIN; EXPLAIN ANALYZE UPDATE ...; ROLLBACK&lt;/code&gt; idiom exists for exactly this reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second, you can see where the value of prepared statements actually comes from.&lt;/strong&gt; Stages 1 through 4 (parse, analyze, rewrite, plan) produce the same result for the same SQL. So when the extended protocol's &lt;code&gt;'P'&lt;/code&gt; message handles 1 through 4 once, every subsequent &lt;code&gt;'B' + 'E'&lt;/code&gt; only repeats stage 5. If you fire a thousand same-shape INSERTs in a transaction, prepared statements save you 999 plannings. Whether your ORM actually uses prepared statements depends on the driver settings; the ratio of &lt;code&gt;calls&lt;/code&gt; to &lt;code&gt;plans&lt;/code&gt; in &lt;code&gt;pg_stat_statements&lt;/code&gt; will show whether reuse is happening.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third, the fact that one backend means one process means one query at a time explains why connection pooling matters so much.&lt;/strong&gt; A backend's main loop is essentially single-threaded. While one client runs a long query, that backend can't do anything else. Connection counts therefore drive memory and scheduling costs linearly, and a pooler like PgBouncer becomes effectively mandatory. The answer to "why are PostgreSQL connections so expensive?" lives inside this one-line main loop.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>internals</category>
      <category>backend</category>
    </item>
    <item>
      <title>1.1 Where Does a Query Go?</title>
      <dc:creator>JoongHyuk Shin</dc:creator>
      <pubDate>Mon, 04 May 2026 09:57:44 +0000</pubDate>
      <link>https://dev.to/joonghyuk_shin_28874f3573/11-where-does-a-query-go-1bka</link>
      <guid>https://dev.to/joonghyuk_shin_28874f3573/11-where-does-a-query-go-1bka</guid>
      <description>&lt;p&gt;Suppose a client sends &lt;code&gt;SELECT * FROM users WHERE id = 1&lt;/code&gt;. The path that single line travels before coming back as a result row is longer than you might expect. Inside the PostgreSQL backend, that SQL goes through a five-stage pipeline.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Backend entry and dispatch&lt;/strong&gt;. The backend receives the message from the client and decides which processing path it should follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parser and Analyzer&lt;/strong&gt;. The SQL text is parsed, and the catalog is consulted to give it meaning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rewriter&lt;/strong&gt;. The RULE system expands views, injects RLS policies, and otherwise transforms the query tree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planner&lt;/strong&gt;. Using a cost model, it explores possible execution paths and picks the best one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Executor&lt;/strong&gt;. It walks the chosen plan, pulling tuples up and sending them to the client.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chapter 1 of this book unfolds this pipeline one chapter at a time. Chapter 1.1, the first one, covers stage 1: the entry point. It has three sections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1.1.1 Life of a Query&lt;/strong&gt;: compresses all five stages into a single diagram. The map for the rest of the book.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1.1.2 Simple vs Extended&lt;/strong&gt;: looks at the semantic difference between the two protocols.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1.1.3 Optimizable vs Utility&lt;/strong&gt;: shows how &lt;code&gt;SELECT/INSERT/...&lt;/code&gt; and &lt;code&gt;CREATE/VACUUM/...&lt;/code&gt; take different paths.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this chapter, it should be clear how the backend's main loop receives a client message and dispatches it to the right function.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>internals</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
