aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/var.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-08-07 19:02:54 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-08-07 19:02:54 -0400
commit5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b (patch)
tree1a72c939a655e9acbba1c71a1831dd38ee41db95 /src/backend/optimizer/util/var.c
parent5078be480412790e4f1b2aeda04f8c65fc7a3b93 (diff)
downloadpostgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.tar.gz
postgresql-5ebaaa49445eb1ba7b299bbea3a477d4e4c0430b.zip
Implement SQL-standard LATERAL subqueries.
This patch implements the standard syntax of LATERAL attached to a sub-SELECT in FROM, and also allows LATERAL attached to a function in FROM, since set-returning function calls are expected to be one of the principal use-cases. The main change here is a rewrite of the mechanism for keeping track of which relations are visible for column references while the FROM clause is being scanned. The parser "namespace" lists are no longer lists of bare RTEs, but are lists of ParseNamespaceItem structs, which carry an RTE pointer as well as some visibility-controlling flags. Aside from supporting LATERAL correctly, this lets us get rid of the ancient hacks that required rechecking subqueries and JOIN/ON and function-in-FROM expressions for invalid references after they were initially parsed. Invalid column references are now always correctly detected on sight. In passing, remove assorted parser error checks that are now dead code by virtue of our having gotten rid of add_missing_from, as well as some comments that are obsolete for the same reason. (It was mainly add_missing_from that caused so much fudging here in the first place.) The planner support for this feature is very minimal, and will be improved in future patches. It works well enough for testing purposes, though. catversion bump forced due to new field in RangeTblEntry.
Diffstat (limited to 'src/backend/optimizer/util/var.c')
-rw-r--r--src/backend/optimizer/util/var.c165
1 files changed, 89 insertions, 76 deletions
diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c
index 9bc90c25313..81332ff1cd1 100644
--- a/src/backend/optimizer/util/var.c
+++ b/src/backend/optimizer/util/var.c
@@ -42,16 +42,15 @@ typedef struct
typedef struct
{
- int var_location;
+ List *vars;
int sublevels_up;
-} locate_var_of_level_context;
+} pull_vars_context;
typedef struct
{
int var_location;
- int relid;
int sublevels_up;
-} locate_var_of_relation_context;
+} locate_var_of_level_context;
typedef struct
{
@@ -77,12 +76,11 @@ typedef struct
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
static bool pull_varattnos_walker(Node *node, pull_varattnos_context *context);
+static bool pull_vars_walker(Node *node, pull_vars_context *context);
static bool contain_var_clause_walker(Node *node, void *context);
static bool contain_vars_of_level_walker(Node *node, int *sublevels_up);
static bool locate_var_of_level_walker(Node *node,
locate_var_of_level_context *context);
-static bool locate_var_of_relation_walker(Node *node,
- locate_var_of_relation_context *context);
static bool find_minimum_var_level_walker(Node *node,
find_minimum_var_level_context *context);
static bool pull_var_clause_walker(Node *node,
@@ -122,6 +120,31 @@ pull_varnos(Node *node)
return context.varnos;
}
+/*
+ * pull_varnos_of_level
+ * Create a set of all the distinct varnos present in a parsetree.
+ * Only Vars of the specified level are considered.
+ */
+Relids
+pull_varnos_of_level(Node *node, int levelsup)
+{
+ pull_varnos_context context;
+
+ context.varnos = NULL;
+ context.sublevels_up = levelsup;
+
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, we don't want to increment sublevels_up.
+ */
+ query_or_expression_tree_walker(node,
+ pull_varnos_walker,
+ (void *) &context,
+ 0);
+
+ return context.varnos;
+}
+
static bool
pull_varnos_walker(Node *node, pull_varnos_context *context)
{
@@ -231,6 +254,66 @@ pull_varattnos_walker(Node *node, pull_varattnos_context *context)
/*
+ * pull_vars_of_level
+ * Create a list of all Vars referencing the specified query level
+ * in the given parsetree.
+ *
+ * This is used on unplanned parsetrees, so we don't expect to see any
+ * PlaceHolderVars.
+ *
+ * Caution: the Vars are not copied, only linked into the list.
+ */
+List *
+pull_vars_of_level(Node *node, int levelsup)
+{
+ pull_vars_context context;
+
+ context.vars = NIL;
+ context.sublevels_up = levelsup;
+
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, we don't want to increment sublevels_up.
+ */
+ query_or_expression_tree_walker(node,
+ pull_vars_walker,
+ (void *) &context,
+ 0);
+
+ return context.vars;
+}
+
+static bool
+pull_vars_walker(Node *node, pull_vars_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ if (var->varlevelsup == context->sublevels_up)
+ context->vars = lappend(context->vars, var);
+ return false;
+ }
+ Assert(!IsA(node, PlaceHolderVar));
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, pull_vars_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, pull_vars_walker,
+ (void *) context);
+}
+
+
+/*
* contain_var_clause
* Recursively scan a clause to discover whether it contains any Var nodes
* (of the current query level).
@@ -406,76 +489,6 @@ locate_var_of_level_walker(Node *node,
/*
- * locate_var_of_relation
- * Find the parse location of any Var of the specified relation.
- *
- * Returns -1 if no such Var is in the querytree, or if they all have
- * unknown parse location.
- *
- * Will recurse into sublinks. Also, may be invoked directly on a Query.
- */
-int
-locate_var_of_relation(Node *node, int relid, int levelsup)
-{
- locate_var_of_relation_context context;
-
- context.var_location = -1; /* in case we find nothing */
- context.relid = relid;
- context.sublevels_up = levelsup;
-
- (void) query_or_expression_tree_walker(node,
- locate_var_of_relation_walker,
- (void *) &context,
- 0);
-
- return context.var_location;
-}
-
-static bool
-locate_var_of_relation_walker(Node *node,
- locate_var_of_relation_context *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
-
- if (var->varno == context->relid &&
- var->varlevelsup == context->sublevels_up &&
- var->location >= 0)
- {
- context->var_location = var->location;
- return true; /* abort tree traversal and return true */
- }
- return false;
- }
- if (IsA(node, CurrentOfExpr))
- {
- /* since CurrentOfExpr doesn't carry location, nothing we can do */
- return false;
- }
- /* No extra code needed for PlaceHolderVar; just look in contained expr */
- if (IsA(node, Query))
- {
- /* Recurse into subselects */
- bool result;
-
- context->sublevels_up++;
- result = query_tree_walker((Query *) node,
- locate_var_of_relation_walker,
- (void *) context,
- 0);
- context->sublevels_up--;
- return result;
- }
- return expression_tree_walker(node,
- locate_var_of_relation_walker,
- (void *) context);
-}
-
-
-/*
* find_minimum_var_level
* Recursively scan a clause to find the lowest variable level it
* contains --- for example, zero is returned if there are any local