SlideShare a Scribd company logo
Writeable CTEs,
 A Revolution in SQL
PGCon
May 20, 2011
Copyright © 2011
David Fetter dfetter@vmware.com
All Rights Reserved
Current CTEs
WITH [RECURSIVE] t1 [(column type,…)] AS
(
    [SELECT | VALUES]
[UNION [ALL]
    [SELECT]
),
t2 AS…tn AS…
SELECT…
Writeable ct es_pgcon_may_2011
Travelling Salesman Problem
Given a number of cities and the costs of travelling
from any city to any other city, what is the least-
cost round-trip route that visits each city exactly
once and then returns to the starting city?
OBTW

With CTE and Windowing, SQL is Turing Complete.
What Didn't the
Old Syntax Do?
WRITE!
WITH [RECURSIVE] t1 [(column type,…)] AS
(
    [SELECT | VALUES |
   (INSERT | UPDATE | DELETE) [RETURNING]]
[UNION [ALL]
    [SELECT | VALUES |
   (INSERT     | UPDATE | DELETE) [RETURNING]]
)
(SELECT |   INSERT | UPDATE | DELETE)      …
For 9.1:
Simple Partition Management

 CREATE TABLE log (
     ts TIMESTAMPTZ NOT NULL,
     msg TEXT NOT NULL
 );
For 9.1:
Simple Partition Management
 CREATE TABLE log_201101 ()
 INHERITS(log);

 ALTER TABLE log_201101 ADD
 CONSTRAINT right_month CHECK(
     ts >= '2011-01-01' AND
     ts < '2011-02-01');
For 9.1:
    Simple Partition Management

berliner@pgday_eu_2010:54321# WITH t1 AS (
    DELETE FROM ONLY log
    WHERE ts >='2011-01-01' AND ts < '2011-02-01'
    RETURNING *
)
INSERT INTO log_201101 SELECT * FROM t1;
INSERT 0 45270
Query Clustering:
             I/O Minimization

CREATE TABLE person (
    id SERIAL PRIMARY KEY,
    first_name TEXT,
    last_name TEXT,
    CHECK (CASE WHEN first_name IS NULL THEN 0 ELSE 1 END +
           CASE WHEN last_name IS NULL THEN 0 ELSE 1 END >= 1),
    birthdate DATE NOT NULL,
    gender TEXT
);
Query Clustering:
         I/O Minimization

CREATE TABLE im (
    id SERIAL PRIMARY KEY,
    provider TEXT NOT NULL, /* should be fk */
    handle TEXT NOT NULL
);
Query Clustering:
      I/O Minimization
CREATE TABLE phone (
    id SERIAL PRIMARY KEY,
    country_code TEXT NOT NULL,
    phone_number TEXT NOT NULL,
    extension TEXT
);
Query Clustering:
 I/O Minimization
CREATE TABLE street (
    id SERIAL PRIMARY KEY,
    street1 TEXT NOT NULL,
    street2 TEXT,
    street3 TEXT,
    city TEXT NOT NULL,
    state TEXT,
    country TEXT NOT NULL,
    post_code TEXT
);
Query Clustering:
   I/O Minimization
CREATE TABLE person_im (
    person_id INTEGER NOT NULL REFERENCES person (id),
    im_id INTEGER NOT NULL REFERENCES im (id),
    UNIQUE (person_id, im_id)
);

CREATE TABLE person_phone (
    person_id INTEGER NOT NULL REFERENCES person (id),
    phone_id INTEGER NOT NULL REFERENCES phone (id),
    UNIQUE (person_id, phone_id)
);

CREATE TABLE person_street (
    person_id INTEGER NOT NULL REFERENCES person (id),
    street_id INTEGER NOT NULL REFERENCES street (id),
    UNIQUE (person_id, street_id)
);
Query Clustering:
         I/O Minimization

WITH t_person AS (
    INSERT INTO person (first_name, last_name)
    VALUES ('David', 'Fetter')
    RETURNING id
),
Query Clustering:
        I/O Minimization
t_im AS (
    INSERT INTO im (provider, handle)
    VALUES
        ('Yahoo!', 'dfetter'),
        ('AIM', 'dfetter666'),
        ('XMPP', 'davidfetter@postgresql.org')
    RETURNING id
),
t_person_im AS (
    INSERT INTO person_im
    SELECT * FROM t_person CROSS JOIN t_im
),
Query Clustering:
        I/O Minimization
t_phone (phone_id) AS (
    INSERT INTO phone (country_code, phone_number)
    VALUES
        ('+1','415 235 3778'),
        ('+1','650 427 3751')
    RETURNING id
),
t_person_phone AS (
    INSERT INTO person_phone
    SELECT * FROM t_person CROSS JOIN t_phone
),
Query Clustering:
                     I/O Minimization

t_street AS (
    INSERT INTO street (street1, city, state, country, post_code)
    VALUES
        ('438 W. Grand Ave., Apartment 727','Oakland','California','USA','94612-2341'),
        ('3421 Hillview Avenue', 'Palo Alto','California','USA','94304')
),
t_person_street AS (
    INSERT INTO person_street
    SELECT * FROM t_person CROSS JOIN t_street
)
Query Clustering:
  I/O Minimization


VALUES(true);
Query Clustering:
     Transaction Management
CREATE TABLE foo (
    id SERIAL PRIMARY KEY,
    bar_id INTEGER NOT NULL
);

CREATE TABLE bar (
    id SERIAL PRIMARY KEY,
    foo_id INTEGER NOT NULL REFERENCES foo(id)
                           ON DELETE CASCADE
                           INITIALLY DEFERRED
);

ALTER TABLE foo ADD FOREIGN KEY (bar_id) REFERENCES bar(id)
                                   ON DELETE CASCADE
                                   INITIALLY DEFERRED;
Query Clustering:
    Transaction Management
WITH t AS
(
! INSERT INTO foo(id, bar_id)
! VALUES(
! ! DEFAULT,
! ! nextval(pg_get_serial_sequence('bar', 'id'))
  )
! RETURNING id AS foo_id, bar_id
)
INSERT INTO bar(id,foo_id)
SELECT bar_id, foo_id FROM t RETURNING *;
Materialized Views
 WITH t AS (
   DELETE FROM foo
   RETURNING a, b
 )
 INSERT INTO mv_foo
 SELECT a, SUM(b)
 FROM t GROUP BY a
Just Cool Stuff™
WITH t AS (
  INSERT INTO foo
  VALUES (...),...,(...)
  RETURNING a
)
SELECT bar.a, bar.b
FROM bar JOIN t USING(a)
Head-Scratcher:
 SNAPSHOTS
WITH t AS (DELETE FROM foo)
SELECT * FROM foo;

(0 rows)?
(100123 rows)?
How'd He Do That?!?



First try: David digs into the grammar and gets cut
a few times.
How'd He Do That?!?


First try: Marko reworks the planner. It needs to
know when it creates a ModifyTable node. These used
to have another name.
How'd He Do That?!?



First try: Marko reworks the executor.   It needs new
nodes. Mmmm...nodes.
How'd He Do That?!?



Marko reworks the executor, Part II:
Copy & Paste. Now it's getting ugly...
How'd He Do That?!?


Jaime Casanova, Tom Lane, and Robert Haas look at
the reworked executor.

D'oh!
How'd He Do That?!?


       FAIL!
Way too much code copying from top
level to the new nodes.
How'd He Do That?!?



Planner changes for ModifyTable node (a few)
How'd He Do That?!?



Executor changes: ONE new node called ModifyTable
How'd He Do That?!?


Marko Tiikkaja restructures the whole code base for the
ModifyTable node. "The usual stuff," (he said casually) for
                      new nodes.
How'd He Do That?!?




 WIN!
Next Steps
INSERT, UPDATE and DELETE on the top level.
Finish the patch
   EXPLAIN ANALYZE
   SPI
   SQL functions
Bug fixes, cleanup, testing
RECURSIVE
Optimization
Future?

DCL (GRANT/REVOKE)
DDL (CREATE/ALTER/DROP)
Where Is It?!?

http://www.postgresql.org/developer/beta
WITH t AS (
    VALUES
        ('Questions'),
        ('Comments'),
        ('Rotten Tomatoes')
)
INSERT INTO davidfetter
SELECT * FROM t
RETURNING...
Thanks, eh!
 Copyright © 2011
 David Fetter dfetter@vmware.com
 All Rights Reserved

More Related Content

PPTX
Lecture 4 sql {basics keys and constraints}
PPTX
Lecture 3 sql {basics ddl commands}
PPTX
Ppt INFORMATIVE PRACTICES for class 11th chapter 14
PPT
Select To Order By
DOCX
Assignment 2 lab 3 python gpa calculator
DOCX
TXT
Bouncingballs sh
PDF
Learn plsql
Lecture 4 sql {basics keys and constraints}
Lecture 3 sql {basics ddl commands}
Ppt INFORMATIVE PRACTICES for class 11th chapter 14
Select To Order By
Assignment 2 lab 3 python gpa calculator
Bouncingballs sh
Learn plsql

What's hot (19)

PPTX
Reading the .explain() Output
PDF
Sql commands
DOCX
Bibashsql
PDF
[1062BPY12001] Data analysis with R / week 4
DOCX
Tallerpractica
PPTX
Sql
PPTX
PDF
Computer practicals(part) Class 12
PDF
키보드 키와 기호 이름 알아보기
PPT
MY SQL
PDF
Data Wrangling with dplyr
DOCX
Produce nice outputs for graphical, tabular and textual reporting in R-Report...
PDF
Appendix A Tables
TXT
ABAP EVENTS EXAMPLE
PDF
Slaying the Dragon: Implementing a Programming Language in Ruby
PDF
Sentencias básicas en oracle
PDF
Explore Data using dplyr
PDF
Protocols
Reading the .explain() Output
Sql commands
Bibashsql
[1062BPY12001] Data analysis with R / week 4
Tallerpractica
Sql
Computer practicals(part) Class 12
키보드 키와 기호 이름 알아보기
MY SQL
Data Wrangling with dplyr
Produce nice outputs for graphical, tabular and textual reporting in R-Report...
Appendix A Tables
ABAP EVENTS EXAMPLE
Slaying the Dragon: Implementing a Programming Language in Ruby
Sentencias básicas en oracle
Explore Data using dplyr
Protocols
Ad

Similar to Writeable ct es_pgcon_may_2011 (20)

PDF
Writeable CTEs: The Next Big Thing
PDF
Postgres can do THAT?
PDF
Find it. Fix it. Real-World SQL Tuning Cases with Karen Morton
PDF
Don't Do This [FOSDEM 2023]
PPTX
How to tune a query - ODTUG 2012
PDF
ETL Patterns with Postgres
KEY
PostgreSQL
PDF
Scaling MySQL Strategies for Developers
KEY
PostgreSQL talk, Database 2011 conference
PDF
Optimizer features in recent releases of other databases
PDF
Advanced query optimization
PPTX
Sql analytic queries tips
PDF
Performance Enhancements In Postgre Sql 8.4
PDF
Are you a monkey or an astronaut?
PDF
PostgreSQL 9.5 Features
PDF
Data Processing Inside PostgreSQL
 
PDF
Simon Elliston Ball – When to NoSQL and When to Know SQL - NoSQL matters Barc...
PPTX
MySQL Optimizer: What's New in 8.0
PDF
Non-Relational Postgres
 
PPTX
Rapid postgresql learning, part 2
Writeable CTEs: The Next Big Thing
Postgres can do THAT?
Find it. Fix it. Real-World SQL Tuning Cases with Karen Morton
Don't Do This [FOSDEM 2023]
How to tune a query - ODTUG 2012
ETL Patterns with Postgres
PostgreSQL
Scaling MySQL Strategies for Developers
PostgreSQL talk, Database 2011 conference
Optimizer features in recent releases of other databases
Advanced query optimization
Sql analytic queries tips
Performance Enhancements In Postgre Sql 8.4
Are you a monkey or an astronaut?
PostgreSQL 9.5 Features
Data Processing Inside PostgreSQL
 
Simon Elliston Ball – When to NoSQL and When to Know SQL - NoSQL matters Barc...
MySQL Optimizer: What's New in 8.0
Non-Relational Postgres
 
Rapid postgresql learning, part 2
Ad

More from David Fetter (16)

PDF
Assertions and how to use them
PDF
PostgreSQL Hooks for Fun and Profit
PDF
Tree tricks osdc_melbourne_20101124
PDF
Grouping sets sfpug_20141118
PDF
Rdbms roadmap 20140130
PPT
Slides pg conf_eu_20131031
PPT
Federation with foreign_data_wrappers_pg_conf_eu_20131031
PPT
Intergalactic data speak_highload++_20131028
PDF
G so c_and_commitfests_and_pointy_hair_oh_my_sfpug_20131008
PPT
Ct es past_present_future_nycpgday_20130322
KEY
Universal data access_with_sql_med
KEY
Lightning sf perl_mongers_20120327
KEY
Threat modeling sf_perl_mongers_20130227
KEY
Security revolutionized fosdem_20120205
KEY
View triggers pg_east_20110325
KEY
PL/Parrot San Francisco Perl Mongers 2010/05/25
Assertions and how to use them
PostgreSQL Hooks for Fun and Profit
Tree tricks osdc_melbourne_20101124
Grouping sets sfpug_20141118
Rdbms roadmap 20140130
Slides pg conf_eu_20131031
Federation with foreign_data_wrappers_pg_conf_eu_20131031
Intergalactic data speak_highload++_20131028
G so c_and_commitfests_and_pointy_hair_oh_my_sfpug_20131008
Ct es past_present_future_nycpgday_20130322
Universal data access_with_sql_med
Lightning sf perl_mongers_20120327
Threat modeling sf_perl_mongers_20130227
Security revolutionized fosdem_20120205
View triggers pg_east_20110325
PL/Parrot San Francisco Perl Mongers 2010/05/25

Recently uploaded (20)

PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
A novel scalable deep ensemble learning framework for big data classification...
PDF
Accuracy of neural networks in brain wave diagnosis of schizophrenia
PDF
Heart disease approach using modified random forest and particle swarm optimi...
PDF
A comparative study of natural language inference in Swahili using monolingua...
PDF
Hybrid model detection and classification of lung cancer
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
OMC Textile Division Presentation 2021.pptx
PDF
Approach and Philosophy of On baking technology
PPTX
1. Introduction to Computer Programming.pptx
PPTX
Tartificialntelligence_presentation.pptx
PDF
Transform Your ITIL® 4 & ITSM Strategy with AI in 2025.pdf
PDF
Web App vs Mobile App What Should You Build First.pdf
PDF
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
PDF
WOOl fibre morphology and structure.pdf for textiles
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PPTX
TechTalks-8-2019-Service-Management-ITIL-Refresh-ITIL-4-Framework-Supports-Ou...
PPTX
Group 1 Presentation -Planning and Decision Making .pptx
PDF
Getting Started with Data Integration: FME Form 101
PDF
A comparative analysis of optical character recognition models for extracting...
Univ-Connecticut-ChatGPT-Presentaion.pdf
A novel scalable deep ensemble learning framework for big data classification...
Accuracy of neural networks in brain wave diagnosis of schizophrenia
Heart disease approach using modified random forest and particle swarm optimi...
A comparative study of natural language inference in Swahili using monolingua...
Hybrid model detection and classification of lung cancer
Unlocking AI with Model Context Protocol (MCP)
OMC Textile Division Presentation 2021.pptx
Approach and Philosophy of On baking technology
1. Introduction to Computer Programming.pptx
Tartificialntelligence_presentation.pptx
Transform Your ITIL® 4 & ITSM Strategy with AI in 2025.pdf
Web App vs Mobile App What Should You Build First.pdf
ENT215_Completing-a-large-scale-migration-and-modernization-with-AWS.pdf
WOOl fibre morphology and structure.pdf for textiles
MIND Revenue Release Quarter 2 2025 Press Release
TechTalks-8-2019-Service-Management-ITIL-Refresh-ITIL-4-Framework-Supports-Ou...
Group 1 Presentation -Planning and Decision Making .pptx
Getting Started with Data Integration: FME Form 101
A comparative analysis of optical character recognition models for extracting...

Writeable ct es_pgcon_may_2011

  • 1. Writeable CTEs, A Revolution in SQL PGCon May 20, 2011 Copyright © 2011 David Fetter dfetter@vmware.com All Rights Reserved
  • 2. Current CTEs WITH [RECURSIVE] t1 [(column type,…)] AS ( [SELECT | VALUES] [UNION [ALL] [SELECT] ), t2 AS…tn AS… SELECT…
  • 4. Travelling Salesman Problem Given a number of cities and the costs of travelling from any city to any other city, what is the least- cost round-trip route that visits each city exactly once and then returns to the starting city?
  • 5. OBTW With CTE and Windowing, SQL is Turing Complete.
  • 6. What Didn't the Old Syntax Do?
  • 7. WRITE! WITH [RECURSIVE] t1 [(column type,…)] AS ( [SELECT | VALUES | (INSERT | UPDATE | DELETE) [RETURNING]] [UNION [ALL] [SELECT | VALUES | (INSERT | UPDATE | DELETE) [RETURNING]] ) (SELECT | INSERT | UPDATE | DELETE) …
  • 8. For 9.1: Simple Partition Management CREATE TABLE log ( ts TIMESTAMPTZ NOT NULL, msg TEXT NOT NULL );
  • 9. For 9.1: Simple Partition Management CREATE TABLE log_201101 () INHERITS(log); ALTER TABLE log_201101 ADD CONSTRAINT right_month CHECK( ts >= '2011-01-01' AND ts < '2011-02-01');
  • 10. For 9.1: Simple Partition Management berliner@pgday_eu_2010:54321# WITH t1 AS ( DELETE FROM ONLY log WHERE ts >='2011-01-01' AND ts < '2011-02-01' RETURNING * ) INSERT INTO log_201101 SELECT * FROM t1; INSERT 0 45270
  • 11. Query Clustering: I/O Minimization CREATE TABLE person ( id SERIAL PRIMARY KEY, first_name TEXT, last_name TEXT, CHECK (CASE WHEN first_name IS NULL THEN 0 ELSE 1 END + CASE WHEN last_name IS NULL THEN 0 ELSE 1 END >= 1), birthdate DATE NOT NULL, gender TEXT );
  • 12. Query Clustering: I/O Minimization CREATE TABLE im ( id SERIAL PRIMARY KEY, provider TEXT NOT NULL, /* should be fk */ handle TEXT NOT NULL );
  • 13. Query Clustering: I/O Minimization CREATE TABLE phone ( id SERIAL PRIMARY KEY, country_code TEXT NOT NULL, phone_number TEXT NOT NULL, extension TEXT );
  • 14. Query Clustering: I/O Minimization CREATE TABLE street ( id SERIAL PRIMARY KEY, street1 TEXT NOT NULL, street2 TEXT, street3 TEXT, city TEXT NOT NULL, state TEXT, country TEXT NOT NULL, post_code TEXT );
  • 15. Query Clustering: I/O Minimization CREATE TABLE person_im ( person_id INTEGER NOT NULL REFERENCES person (id), im_id INTEGER NOT NULL REFERENCES im (id), UNIQUE (person_id, im_id) ); CREATE TABLE person_phone ( person_id INTEGER NOT NULL REFERENCES person (id), phone_id INTEGER NOT NULL REFERENCES phone (id), UNIQUE (person_id, phone_id) ); CREATE TABLE person_street ( person_id INTEGER NOT NULL REFERENCES person (id), street_id INTEGER NOT NULL REFERENCES street (id), UNIQUE (person_id, street_id) );
  • 16. Query Clustering: I/O Minimization WITH t_person AS ( INSERT INTO person (first_name, last_name) VALUES ('David', 'Fetter') RETURNING id ),
  • 17. Query Clustering: I/O Minimization t_im AS ( INSERT INTO im (provider, handle) VALUES ('Yahoo!', 'dfetter'), ('AIM', 'dfetter666'), ('XMPP', 'davidfetter@postgresql.org') RETURNING id ), t_person_im AS ( INSERT INTO person_im SELECT * FROM t_person CROSS JOIN t_im ),
  • 18. Query Clustering: I/O Minimization t_phone (phone_id) AS ( INSERT INTO phone (country_code, phone_number) VALUES ('+1','415 235 3778'), ('+1','650 427 3751') RETURNING id ), t_person_phone AS ( INSERT INTO person_phone SELECT * FROM t_person CROSS JOIN t_phone ),
  • 19. Query Clustering: I/O Minimization t_street AS ( INSERT INTO street (street1, city, state, country, post_code) VALUES ('438 W. Grand Ave., Apartment 727','Oakland','California','USA','94612-2341'), ('3421 Hillview Avenue', 'Palo Alto','California','USA','94304') ), t_person_street AS ( INSERT INTO person_street SELECT * FROM t_person CROSS JOIN t_street )
  • 20. Query Clustering: I/O Minimization VALUES(true);
  • 21. Query Clustering: Transaction Management CREATE TABLE foo ( id SERIAL PRIMARY KEY, bar_id INTEGER NOT NULL ); CREATE TABLE bar ( id SERIAL PRIMARY KEY, foo_id INTEGER NOT NULL REFERENCES foo(id) ON DELETE CASCADE INITIALLY DEFERRED ); ALTER TABLE foo ADD FOREIGN KEY (bar_id) REFERENCES bar(id) ON DELETE CASCADE INITIALLY DEFERRED;
  • 22. Query Clustering: Transaction Management WITH t AS ( ! INSERT INTO foo(id, bar_id) ! VALUES( ! ! DEFAULT, ! ! nextval(pg_get_serial_sequence('bar', 'id')) ) ! RETURNING id AS foo_id, bar_id ) INSERT INTO bar(id,foo_id) SELECT bar_id, foo_id FROM t RETURNING *;
  • 23. Materialized Views WITH t AS ( DELETE FROM foo RETURNING a, b ) INSERT INTO mv_foo SELECT a, SUM(b) FROM t GROUP BY a
  • 24. Just Cool Stuff™ WITH t AS ( INSERT INTO foo VALUES (...),...,(...) RETURNING a ) SELECT bar.a, bar.b FROM bar JOIN t USING(a)
  • 25. Head-Scratcher: SNAPSHOTS WITH t AS (DELETE FROM foo) SELECT * FROM foo; (0 rows)? (100123 rows)?
  • 26. How'd He Do That?!? First try: David digs into the grammar and gets cut a few times.
  • 27. How'd He Do That?!? First try: Marko reworks the planner. It needs to know when it creates a ModifyTable node. These used to have another name.
  • 28. How'd He Do That?!? First try: Marko reworks the executor. It needs new nodes. Mmmm...nodes.
  • 29. How'd He Do That?!? Marko reworks the executor, Part II: Copy & Paste. Now it's getting ugly...
  • 30. How'd He Do That?!? Jaime Casanova, Tom Lane, and Robert Haas look at the reworked executor. D'oh!
  • 31. How'd He Do That?!? FAIL! Way too much code copying from top level to the new nodes.
  • 32. How'd He Do That?!? Planner changes for ModifyTable node (a few)
  • 33. How'd He Do That?!? Executor changes: ONE new node called ModifyTable
  • 34. How'd He Do That?!? Marko Tiikkaja restructures the whole code base for the ModifyTable node. "The usual stuff," (he said casually) for new nodes.
  • 35. How'd He Do That?!? WIN!
  • 36. Next Steps INSERT, UPDATE and DELETE on the top level. Finish the patch EXPLAIN ANALYZE SPI SQL functions Bug fixes, cleanup, testing RECURSIVE Optimization
  • 39. WITH t AS ( VALUES ('Questions'), ('Comments'), ('Rotten Tomatoes') ) INSERT INTO davidfetter SELECT * FROM t RETURNING...
  • 40. Thanks, eh! Copyright © 2011 David Fetter dfetter@vmware.com All Rights Reserved

Editor's Notes

  • #2: \n
  • #3: You could do some pretty cool stuff with them. Draw pretty pictures, for example.\n
  • #4: And now...\n
  • #5: Yep. That&apos;s NP-Hard. We can do it.\n
  • #6: \n
  • #7: Cook breakfast? Make coffee? Let&apos;s look again!\n
  • #8: \n
  • #9: Here&apos;s how we started.\n
  • #10: Here&apos;s how we started.\n
  • #11: Here&apos;s how we started.\n
  • #12: \n
  • #13: \n
  • #14: \n
  • #15: OK, now some join tables\n
  • #16: ...and let&apos;s start coding :)\n
  • #17: \n
  • #18: \n
  • #19: \n
  • #20: OK, we&apos;re done now :)\n
  • #21: How about some more sophisticated stuff?\n
  • #22: \n
  • #23: \n
  • #24: Thanks yet again, Marko! :)\n
  • #25: \n
  • #26: One snapshot per query.\n
  • #27: \n
  • #28: \n
  • #29: \n
  • #30: \n
  • #31: \n
  • #32: (In short, -1 from me. Tom Lane)\n
  • #33: \n
  • #34: \n
  • #35: \n
  • #36: \n
  • #37: IDU top-level, thanks to Hitoshi Harada\n
  • #38: Sometime over the course of this project, I realized that my hair had gotten pretty pointy.\n
  • #39: \n
  • #40: \n
  • #41: \n