Hacking PostgreSQL to Gain SQL Parsing Superpowers

Hacking PostgreSQL to Gain SQL Parsing Superpowers

Talk at Microsoft Reactor / Open Source Show And Tell

https://github.com/lfittl/pg_query

27b304f67c0cadfa2f37a19f01af8f89?s=128

Lukas Fittl

April 22, 2016
Tweet

Transcript

  1. @LukasFittl Hacking PostgreSQL to Gain SQL Parsing Superpowers @LukasFittl

  2. @LukasFittl Parsing SQL

  3. @LukasFittl SELECT  *  FROM  x  WHERE  z  =  1

  4. @LukasFittl http://xkcd.com/208/

  5. @LukasFittl PostgreSQL

  6. @LukasFittl EXPLAIN  (PARSETREE  TRUE)      SELECT  *  FROM  x

     WHERE  y  =  1 Unfortunately doesn’t exist.
  7. @LukasFittl Parse Statement raw_parse(..) pg_catalog Rewrite Query Query Planner Execute

    How PostgreSQL runs a Query:
  8. @LukasFittl tree = raw_parser(query_str); str = nodeToString(tree); printf(str); ({SELECT  :distinctClause

     <>                    :intoClause  <>                    :targetList  (                        {RESTARGET                            :name  <>                            :indirection  <>                            :val  {COLUMNREF  :fields  ({A_STAR})  :location  7}                            :location  7})                    :fromClause  (                        {RANGEVAR                            :schemaname  <>                            :relname  x                            :inhOpt  2                            :relpersistence  p                            :alias  <>                            :location  14})                    :whereClause  {AEXPR    :name  (“=")                        :lexpr  {COLUMNREF  :fields  ("y")  :location  22}                        :rexpr  {PARAMREF  :number  0  :location  26}                        :location  24}                    :groupClause  <>                    :havingClause  <>                    :windowClause  <>  
  9. @LukasFittl Parse Statement raw_parse(..) pg_catalog Rewrite Query Query Planner Execute

  10. @LukasFittl PgQuery._raw_parse( “SELECT * FROM x WHERE y = 1”)

    ({SELECT  :distinctClause  <>                    :intoClause  <>                    :targetList  (                        {RESTARGET                            :name  <>                            :indirection  <>                            :val  {COLUMNREF  :fields  ({A_STAR})  :location  7}                            :location  7})                    :fromClause  (                        {RANGEVAR                            :schemaname  <>                            :relname  x                            :inhOpt  2                            :relpersistence  p                            :alias  <>                            :location  14})                    :whereClause  {AEXPR    :name  (“=")                        :lexpr  {COLUMNREF  :fields  ("y")  :location  22}                        :rexpr  {PARAMREF  :number  0  :location  26}                        :location  24}                    :groupClause  <>                    :havingClause  <>                    :windowClause  <>                    :valuesLists  <>  
  11. @LukasFittl PgQuery._raw_parse( “SELECT * FROM x WHERE y = 1”)

    [{     "SelectStmt":  {       "targetList":  [{         "ResTarget":  {           "val":  {             "ColumnRef":  {               "fields":  [{                 "A_Star":  {}               }],               "location":  7             }           },           "location":  7         }       }],       "fromClause":  [{         "RangeVar":  {           "relname":  "x",           "inhOpt":  2,           "relpersistence":  "p",           "location":  14         }       }],       "whereClause":  {         "A_Expr":  {  
  12. @LukasFittl github.com/lfittl/pg_query

  13. @LukasFittl De-Parsing SQL

  14. @LukasFittl SELECT * FROM the_table SELECT * FROM a_better_table

  15. @LukasFittl q  =  PgQuery.parse(‘SELECT  *  FROM  the_table’)   =>  

    [{"SelectStmt"  =>  {      "targetList"  =>[…],      "fromClause"  =>  [{          "RangeVar"  =>  {       "relname"  =>  “the_table",              …          }      }]   }}]   q.tree[0][‘SelectStmt']['fromClause'][0][‘RangeVar'] ['relname']  =  ‘the_other_table'   q.deparse   =>  "SELECT  *  FROM  \"the_other_table\"
  16. @LukasFittl Don’t like JSON?

  17. @LukasFittl pg_query_go package  main   import  (      "github.com/lfittl/pg_query_go"

      )   func  main()  {      tree,  err  :=  pg_query.Parse("SELECT  1")      if  err  !=  nil  {          panic(err);      }      …   }
  18. @LukasFittl pg_query.ParsetreeList{          Statements:  []nodes.Node{    

             nodes.SelectStmt{                  TargetList:  []nodes.Node{                      nodes.ResTarget{                          Val:  nodes.A_Const{                              Type:  "integer",                              Val:  nodes.Value{                                  Type:  nodes.T_Integer,                                  Ival:  1,                              },                              Location:  7,                          },                          Location:  7,                      },                  },              },          }, tree  :=  pg_query.Parse(“SELECT  1")
  19. @LukasFittl Other Languages C/C++: libpg_query Go: pg_query_go Node.js: pg-query-parser Not

    yet: Python, Java, [?]
  20. @LukasFittl Shameless Plug: github.com/citusdata/citus Open-source, distributed PostgreSQL

  21. @LukasFittl Thank You! github.com/lfittl/pg_query