Parse a plpgsql function body into an AST.
pl_gram.y is the specification. What that grammar actually does --
and what this does -- is parse the statement structure and hand
every embedded expression and query to the SQL parser untouched.
plpgsql has no expression grammar of its own: IF a > b THEN finds
the THEN and gives a > b to the SQL layer as SELECT a > b. So
the job here is to find where each embedded fragment ends, and to
keep its SOURCE, which the executor later runs through the ordinary
statement path.
Everything pl_gram.y accepts is parsed, including the constructs
the executor does not implement. That is deliberate: CREATE FUNCTION walks the AST and refuses a body that uses one, naming it,
so an unimplemented construct is a clean 0A000 at definition time
rather than a wrong answer at call time.
The tokenizer is the SQL classifier's, so a keyword inside a string, a comment, a dollar-quoted body or a quoted identifier is not a keyword.
Parse a plpgsql function body into an AST. `pl_gram.y` is the specification. What that grammar actually does -- and what this does -- is parse the *statement structure* and hand every embedded expression and query to the SQL parser untouched. plpgsql has no expression grammar of its own: `IF a > b THEN` finds the `THEN` and gives `a > b` to the SQL layer as `SELECT a > b`. So the job here is to find where each embedded fragment ends, and to keep its SOURCE, which the executor later runs through the ordinary statement path. Everything `pl_gram.y` accepts is parsed, including the constructs the executor does not implement. That is deliberate: `CREATE FUNCTION` walks the AST and refuses a body that uses one, naming it, so an unimplemented construct is a clean 0A000 at definition time rather than a wrong answer at call time. The tokenizer is the SQL classifier's, so a keyword inside a string, a comment, a dollar-quoted body or a quoted identifier is not a keyword.
Node kinds the executor runs. CREATE FUNCTION refuses a body that
uses anything else, naming it, so a gap is a 0A000 at definition time
rather than a wrong answer at call time.
Node kinds the executor runs. `CREATE FUNCTION` refuses a body that uses anything else, naming it, so a gap is a 0A000 at definition time rather than a wrong answer at call time.
(parse-body src)Parse a plpgsql function body. Returns the top-level :block node.
Raises 42601 with PostgreSQL's SQLSTATE on a body this cannot read.
Parse a plpgsql function body. Returns the top-level `:block` node. Raises 42601 with PostgreSQL's SQLSTATE on a body this cannot read.
A parameter written $n in a body becomes a variable of this name.
It cannot stay $n: the translator rewrites LITERALS to $n
placeholders so a plan can be cached across values, so by the time a
body's statement is translated its own $1 is indistinguishable from
a 0 the rewriter lifted. SELECT 0 came back as the first
argument.
A parameter written `$n` in a body becomes a variable of this name. It cannot stay `$n`: the translator rewrites LITERALS to `$n` placeholders so a plan can be cached across values, so by the time a body's statement is translated its own `$1` is indistinguishable from a `0` the rewriter lifted. `SELECT 0` came back as the first argument.
(unsupported-constructs ast)The distinct node kinds in ast the executor cannot run, plus
:exception-handler when the body catches. Empty when it can run.
The distinct node kinds in `ast` the executor cannot run, plus `:exception-handler` when the body catches. Empty when it can run.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |