Run a parsed plpgsql body.
The evaluator has two primitives and everything else is control flow:
SELECT <expr> and take one valueBoth go through the handler this function was called from, which is
what SPI is for PostgreSQL: the nested statement sees the same
transaction, the same temporary tables and the same session. That is
also why pl_exec.c is 9,226 lines and this is not -- most of it is
memory contexts, plan caching and TupleDesc conversion, none of which
we have to reproduce.
A variable reaches an embedded statement as a PARAMETER: the
statement's references to it are rewritten to $n and the values are
bound per call. That is how PostgreSQL does it, and it is what makes
the plan cacheable -- resolving a variable to its VALUE during
translation made the plan specific to that value, so every execution
re-translated and a body reading one variable cost 11ms a call
against 0.3ms for one reading none.
Run a parsed plpgsql body. The evaluator has two primitives and everything else is control flow: - evaluate an expression -- run `SELECT <expr>` and take one value - run a statement -- hand the source to the query handler Both go through the handler this function was called from, which is what SPI is for PostgreSQL: the nested statement sees the same transaction, the same temporary tables and the same session. That is also why `pl_exec.c` is 9,226 lines and this is not -- most of it is memory contexts, plan caching and TupleDesc conversion, none of which we have to reproduce. A variable reaches an embedded statement as a PARAMETER: the statement's references to it are rewritten to `$n` and the values are bound per call. That is how PostgreSQL does it, and it is what makes the plan cacheable -- resolving a variable to its VALUE during translation made the plan specific to that value, so every execution re-translated and a body reading one variable cost 11ms a call against 0.3ms for one reading none.
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.
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 |