Lua Foreign Data Wrapper for PostgreSQL

August 22, 2016 ยท View on GitHub

Based on the Blackhole Foreign Data Wrapper by Andrew Dunstan: https://bitbucket.org/adunstan/blackhole_fdw

Write PostgreSQL foreign data wrappers in Lua. Currently read-only.

Hello World

-- hello_world.lua

function ScanStart ()
  done = false
end

function ScanIterate ()
  if not done then
    done = true
    return { data = "hello world" }
  end
end

function ScanEnd ()
  -- noop
end
CREATE EXTENSION lua_fdw;
CREATE SERVER lua_srv FOREIGN DATA WRAPPER lua_fdw;
CREATE FOREIGN TABLE lua_test (data text) SERVER lua_srv OPTIONS (script '/path/to/hello_world.lua');
SELECT * FROM lua_test;
    data
-------------
 hello world
(1 row)

Lua API

The FDW looks for named Lua callback functions to be handle each stage of query execution. Missing callbacks are skipped.

| Lua callback function | Return | Stage | Description | | --- | --- | --- | --- | --- | | EstimateRowCount() | Integer | Planning | Approximate row count | | EstimateRowWidth() | Integer (bytes) | Planning | Average row width | | EstimateStartupCost() | Double | Planning | See EXPLAIN | | EstimateTotalCost() | Double | Planning | See EXPLAIN | | ScanStart() | N/A | Table Scan | Prepare for a table scan, open any resources, files, connections etc, but don't return any data yet | | ScanIterate() | Table (row) | Table Scan | Return the next available row, keys = column names, values = anything scalar. Missing columns are assumed to be NULL | | ScanRestart() | N/A | Table Scan | Restart the current table scan from the beginning | | ScanEnd() | N/A | Table Scan | Close/free any resources used for the current table scan | | ScanExplain() | Text | EXPLAIN | Return something useful to show in EXPLAIN output |

A global Lua table called fdw exposes information about the table and query. Some fields:

ElementLua TypeDescription
fdw.tablestringForeign table name
fdw.columnstable{ [column] = 'type', ... }
fdw.clausestableList of simple WHERE clauses: "column" (operator) 'constant'
fdw.ereport()functionPostgreSQL error messages, eg fdw.ereport(fdw.WARNING, "some text")
fdw.WARNINGnumberPostgreSQL error level. Also DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, ERROR, LOG, FATAL, and PANIC

Table OPTIONS

CREATE FOREIGN TABLE ... OPTIONS (
  script '/path/to/hello_world.lua'
  inject '... lua code ...',
  lua_path '/custom/path/?.lua',
  lua_cpath '/custom/path/?.so'
);
OptionDescription
scriptPath to the Lua script
injectFragment of Lua code to execute after the script is loaded. Useful for setting globals. May be replaced with a constructor callback.
lua_pathAppend to default LUA_PATH
lua_cpathAppend to default LUA_CPATH

Scan Clauses (condition pushdown)

To allow pushing filter conditions to the foreign data service, fdw.clauses lists any simple top-level WHERE clauses of the form "column" (operator) 'constant', eg:

"column" (operator) number
"column" (operator) 'string'
"column" (operator) timestamp

Each clause is split into a table:

{
  column = "email",
  operator = "eq",
  type = "text",
  constant = "me@example.com",
}