Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
43515ba3
Commit
43515ba3
authored
Jul 24, 2002
by
Peter Eisentraut
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove _deadcode.
parent
739adf32
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
0 additions
and
4535 deletions
+0
-4535
src/backend/commands/_deadcode/recipe.c
src/backend/commands/_deadcode/recipe.c
+0
-1318
src/backend/commands/_deadcode/version.c
src/backend/commands/_deadcode/version.c
+0
-346
src/backend/executor/_deadcode/nodeTee.c
src/backend/executor/_deadcode/nodeTee.c
+0
-499
src/backend/optimizer/path/_deadcode/predmig.c
src/backend/optimizer/path/_deadcode/predmig.c
+0
-810
src/backend/optimizer/path/_deadcode/xfunc.c
src/backend/optimizer/path/_deadcode/xfunc.c
+0
-1479
src/include/optimizer/_deadcode/xfunc.h
src/include/optimizer/_deadcode/xfunc.h
+0
-83
No files found.
src/backend/commands/_deadcode/recipe.c
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* recipe.c
* routines for handling execution of Tioga recipes
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/recipe.c,v 1.17 2002/06/20 20:29:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "catalog/pg_type.h"
#include "commands/recipe.h"
#include "executor/executor.h"
#include "libpq/libpq-be.h"
#include "nodes/execnodes.h"
#include "nodes/makefuncs.h"
#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
#include "optimizer/planner.h"
#include "parser/parse_node.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "tcop/dest.h"
#include "tcop/pquery.h"
#include "utils/builtins.h"
#include "utils/relcache.h"
/* from tcop/postgres.c */
extern
CommandDest
whereToSendOutput
;
#ifndef TIOGA
void
beginRecipe
(
RecipeStmt
*
stmt
)
{
elog
(
WARNING
,
"You must compile with TIOGA defined in order to use recipes
\n
"
);
}
#else
#include "tioga/tgRecipe.h"
#define DEBUG_RECIPE 1
/* structure to keep track of the tee node plans */
typedef
struct
_teePlanInfo
{
char
*
tpi_relName
;
Query
*
tpi_parsetree
;
Plan
*
tpi_plan
;
}
TeePlanInfo
;
typedef
struct
_teeInfo
{
int
num
;
TeePlanInfo
*
val
;
}
TeeInfo
;
QueryTreeList
*
appendQlist
(
QueryTreeList
*
q1
,
QueryTreeList
*
q2
);
void
OffsetVarAttno
(
Node
*
node
,
int
varno
,
int
offset
);
static
void
appendTeeQuery
(
TeeInfo
*
teeInfo
,
QueryTreeList
*
q
,
char
*
teeNodeName
);
static
Plan
*
replaceTeeScans
(
Plan
*
plan
,
Query
*
parsetree
,
TeeInfo
*
teeInfo
);
static
void
replaceSeqScan
(
Plan
*
plan
,
Plan
*
parent
,
int
rt_ind
,
Plan
*
tplan
);
static
void
tg_rewriteQuery
(
TgRecipe
*
r
,
TgNode
*
n
,
QueryTreeList
*
q
,
QueryTreeList
*
inputQlist
);
static
Node
*
tg_replaceNumberedParam
(
Node
*
expression
,
int
pnum
,
int
rt_ind
,
char
*
teeRelName
);
static
Node
*
tg_rewriteParamsInExpr
(
Node
*
expression
,
QueryTreeList
*
inputQlist
);
static
QueryTreeList
*
tg_parseSubQuery
(
TgRecipe
*
r
,
TgNode
*
n
,
TeeInfo
*
teeInfo
);
static
QueryTreeList
*
tg_parseTeeNode
(
TgRecipe
*
r
,
TgNode
*
n
,
int
i
,
QueryTreeList
*
qList
,
TeeInfo
*
teeInfo
);
/*
The Tioga recipe rewrite algorithm:
To parse a Tioga recipe, we start from an eye node and go backwards through
its input nodes. To rewrite a Tioga node, we do the following:
1) parse the node we're at in the standard way (calling parser() )
2) rewrite its input nodes recursively using Tioga rewrite
3) now, with the rewritten input parse trees and the original parse tree
of the node, we rewrite the the node.
To do the rewrite, we use the target lists, range tables, and
qualifications of the input parse trees
*/
/*
* beginRecipe:
* this is the main function to recipe execution
* this function is invoked for EXECUTE RECIPE ... statements
*
* takes in a RecipeStmt structure from the parser
* and returns a list of cursor names
*/
void
beginRecipe
(
RecipeStmt
*
stmt
)
{
TgRecipe
*
r
;
int
i
,
numTees
;
QueryTreeList
*
qList
;
char
portalName
[
1024
];
Plan
*
plan
;
TupleDesc
attinfo
;
QueryDesc
*
queryDesc
;
Query
*
parsetree
;
TeeInfo
*
teeInfo
;
/*
* retrieveRecipe() reads the recipe from the database and returns a
* TgRecipe* structure we can work with
*/
r
=
retrieveRecipe
(
stmt
->
recipeName
);
if
(
r
==
NULL
)
return
;
/* find the number of tees in the recipe */
numTees
=
r
->
tees
->
num
;
if
(
numTees
>
0
)
{
/* allocate a teePlan structure */
teeInfo
=
(
TeeInfo
*
)
malloc
(
sizeof
(
TeeInfo
));
teeInfo
->
num
=
numTees
;
teeInfo
->
val
=
(
TeePlanInfo
*
)
malloc
(
numTees
*
sizeof
(
TeePlanInfo
));
for
(
i
=
0
;
i
<
numTees
;
i
++
)
{
teeInfo
->
val
[
i
].
tpi_relName
=
r
->
tees
->
val
[
i
]
->
nodeName
;
teeInfo
->
val
[
i
].
tpi_parsetree
=
NULL
;
teeInfo
->
val
[
i
].
tpi_plan
=
NULL
;
}
}
else
teeInfo
=
NULL
;
/*
* for each viewer in the recipe, go backwards from each viewer input
* and generate a plan. Attach the plan to cursors.
*/
for
(
i
=
0
;
i
<
r
->
eyes
->
num
;
i
++
)
{
TgNodePtr
e
;
e
=
r
->
eyes
->
val
[
i
];
if
(
e
->
inNodes
->
num
>
1
)
{
elog
(
WARNING
,
"beginRecipe: Currently eyes cannot have more than one input"
);
}
if
(
e
->
inNodes
->
num
==
0
)
{
/* no input to this eye, skip it */
continue
;
}
#ifdef DEBUG_RECIPE
elog
(
WARNING
,
"beginRecipe: eyes[%d] = %s
\n
"
,
i
,
e
->
nodeName
);
#endif
/* DEBUG_RECIPE */
qList
=
tg_parseSubQuery
(
r
,
e
->
inNodes
->
val
[
0
],
teeInfo
);
if
(
qList
==
NULL
)
{
/* eye is directly connected to a tee node */
/* XXX TODO: handle this case */
}
/* now, plan the queries */
/*
* should really do everything pg_plan() does, but for now, we
* skip the rule rewrite and time qual stuff
*/
/*
* 1) plan the main query, everything from an eye node back to a
* Tee
*/
parsetree
=
qList
->
qtrees
[
0
];
/*
* before we plan, we want to see all the changes we did, during
* the rewrite phase, such as creating the tee tables,
* CommandCounterIncrement() allows us to see the changes
*/
CommandCounterIncrement
();
plan
=
planner
(
parsetree
);
/*
* 2) plan the tee queries, (subgraphs rooted from a Tee) by the
* time the eye is processed, all tees that contribute to that eye
* will have been included in the teeInfo list
*/
if
(
teeInfo
)
{
int
t
;
Plan
*
tplan
;
Tee
*
newplan
;
for
(
t
=
0
;
t
<
teeInfo
->
num
;
t
++
)
{
if
(
teeInfo
->
val
[
t
].
tpi_plan
==
NULL
)
{
/* plan it in the usual fashion */
tplan
=
planner
(
teeInfo
->
val
[
t
].
tpi_parsetree
);
/* now add a tee node to the root of the plan */
elog
(
WARNING
,
"adding tee plan node to the root of the %s
\n
"
,
teeInfo
->
val
[
t
].
tpi_relName
);
newplan
=
(
Tee
*
)
makeNode
(
Tee
);
newplan
->
plan
.
targetlist
=
tplan
->
targetlist
;
newplan
->
plan
.
qual
=
NULL
;
/* tplan->qual; */
newplan
->
plan
.
lefttree
=
tplan
;
newplan
->
plan
.
righttree
=
NULL
;
newplan
->
leftParent
=
NULL
;
newplan
->
rightParent
=
NULL
;
/*
* the range table of the tee is the range table of
* the tplan
*/
newplan
->
rtentries
=
teeInfo
->
val
[
t
].
tpi_parsetree
->
rtable
;
strcpy
(
newplan
->
teeTableName
,
teeInfo
->
val
[
t
].
tpi_relName
);
teeInfo
->
val
[
t
].
tpi_plan
=
(
Plan
*
)
newplan
;
}
}
/*
* 3) replace the tee table scans in the main plan with actual
* tee plannodes
*/
plan
=
replaceTeeScans
(
plan
,
parsetree
,
teeInfo
);
}
/* if (teeInfo) */
/* define a portal for this viewer input */
/* for now, eyes can only have one input */
snprintf
(
portalName
,
1024
,
"%s%d"
,
e
->
nodeName
,
0
);
queryDesc
=
CreateQueryDesc
(
parsetree
,
plan
,
whereToSendOutput
);
/*
* call ExecStart to prepare the plan for execution
*/
attinfo
=
ExecutorStart
(
queryDesc
,
NULL
);
ProcessPortal
(
portalName
,
parsetree
,
plan
,
attinfo
,
whereToSendOutput
);
elog
(
WARNING
,
"beginRecipe: cursor named %s is now available"
,
portalName
);
}
}
/*
* tg_rewriteQuery -
* r - the recipe being rewritten
* n - the node that we're current at
* q - a QueryTree List containing the parse tree of the node
* inputQlist - the parsetrees of its input nodes,
* the size of inputQlist must be the same as the
* number of input nodes. Some elements in the inpuQlist
* may be null if the inputs to those nodes are unconnected
*
* this is the main routine for rewriting the recipe queries
* the original query tree 'q' is modified
*/
static
void
tg_rewriteQuery
(
TgRecipe
*
r
,
TgNode
*
n
,
QueryTreeList
*
q
,
QueryTreeList
*
inputQlist
)
{
Query
*
orig
;
Query
*
inputQ
;
int
i
;
List
*
rtable
;
List
*
input_rtable
;
int
rt_length
;
/* orig is the original parse tree of the node */
orig
=
q
->
qtrees
[
0
];
/*
* step 1:
*
* form a combined range table from all the range tables in the original
* query as well as the input nodes
*
* form a combined qualification from the qual in the original plus the
* quals of the input nodes
*/
/* start with the original range table */
rtable
=
orig
->
rtable
;
rt_length
=
length
(
rtable
);
for
(
i
=
0
;
i
<
n
->
inNodes
->
num
;
i
++
)
{
if
(
n
->
inNodes
->
val
[
i
]
!=
NULL
&&
n
->
inNodes
->
val
[
i
]
->
nodeType
!=
TG_TEE_NODE
)
{
inputQ
=
inputQlist
->
qtrees
[
i
];
input_rtable
=
inputQ
->
rtable
;
/*
* need to offset the var nodes in the qual and targetlist
* because they are indexed off the original rtable
*/
OffsetVarNodes
((
Node
*
)
inputQ
->
qual
,
rt_length
,
0
);
OffsetVarNodes
((
Node
*
)
inputQ
->
targetList
,
rt_length
,
0
);
/* append the range tables from the children nodes */
rtable
=
nconc
(
rtable
,
input_rtable
);
/*
* append the qualifications of the child node into the
* original qual list
*/
AddQual
(
orig
,
inputQ
->
qual
);
}
}
orig
->
rtable
=
rtable
;
/*
* step 2: rewrite the target list of the original parse tree if there
* are any references to params, replace them with the appropriate
* target list entry of the children node
*/
if
(
orig
->
targetList
!=
NIL
)
{
List
*
tl
;
TargetEntry
*
tle
;
foreach
(
tl
,
orig
->
targetList
)
{
tle
=
lfirst
(
tl
);
if
(
tle
->
resdom
!=
NULL
)
tle
->
expr
=
tg_rewriteParamsInExpr
(
tle
->
expr
,
inputQlist
);
}
}
/*
* step 3: rewrite the qual of the original parse tree if there are
* any references to params, replace them with the appropriate target
* list entry of the children node
*/
if
(
orig
->
qual
)
{
if
(
nodeTag
(
orig
->
qual
)
==
T_List
)
elog
(
ERROR
,
"tg_rewriteQuery: Whoa! why is my qual a List???"
);
orig
->
qual
=
tg_rewriteParamsInExpr
(
orig
->
qual
,
inputQlist
);
}
/*
* at this point, we're done with the rewrite, the querytreelist q has
* been modified
*/
}
/* tg_replaceNumberedParam:
this procedure replaces the specified numbered param with a
reference to a range table
this procedure recursively calls itself
it returns a (possibly modified) Node*.
*/
static
Node
*
tg_replaceNumberedParam
(
Node
*
expression
,
int
pnum
,
/* the number of the parameter */
int
rt_ind
,
/* the range table index */
char
*
teeRelName
)
/* the relname of the tee
* table */
{
TargetEntry
*
param_tle
;
Param
*
p
;
Var
*
newVar
,
*
oldVar
;
if
(
expression
==
NULL
)
return
NULL
;
switch
(
nodeTag
(
expression
))
{
case
T_Param
:
{
/*
* the node is a parameter, substitute the entry from the
* target list of the child that corresponds to the
* parameter number
*/
p
=
(
Param
*
)
expression
;
/* we only deal with the case of numbered parameters */
if
(
p
->
paramkind
==
PARAM_NUM
&&
p
->
paramid
==
pnum
)
{
if
(
p
->
param_tlist
)
{
/*
* we have a parameter with an attribute like
* $N.foo so replace it with a new var node
*/
/* param tlist can only have one entry in them! */
param_tle
=
(
TargetEntry
*
)
(
lfirst
(
p
->
param_tlist
));
oldVar
=
(
Var
*
)
param_tle
->
expr
;
oldVar
->
varno
=
rt_ind
;
oldVar
->
varnoold
=
rt_ind
;
return
(
Node
*
)
oldVar
;
}
else
{
/* we have $N without the .foo */
bool
defined
;
bool
isRel
;
/*
* TODO here, we need to check to see whether the
* type of the tee is a complex type (relation) or
* a simple type
*/
/*
* if it is a simple type, then we need to get the
* "result" attribute from the tee relation
*/
isRel
=
(
typeidTypeRelid
(
p
->
paramtype
)
!=
0
);
if
(
isRel
)
{
newVar
=
makeVar
(
rt_ind
,
0
,
/* the whole tuple */
TypeGet
(
teeRelName
,
&
defined
),
-
1
,
0
,
rt_ind
,
0
);
return
(
Node
*
)
newVar
;
}
else
newVar
=
makeVar
(
rt_ind
,
1
,
/* just the first field,
* which is 'result' */
TypeGet
(
teeRelName
,
&
defined
),
-
1
,
0
,
rt_ind
,
0
);
return
(
Node
*
)
newVar
;
}
}
else
elog
(
WARNING
,
"tg_replaceNumberedParam: unexpected paramkind value of %d"
,
p
->
paramkind
);
}
break
;
case
T_Expr
:
{
/*
* the node is an expression, we need to recursively call
* ourselves until we find parameter nodes
*/
List
*
l
;
Expr
*
expr
=
(
Expr
*
)
expression
;
List
*
newArgs
;
/*
* we have to make a new args lists because Params can be
* replaced by Var nodes in tg_replaceNumberedParam()
*/
newArgs
=
NIL
;
/*
* we only care about argument to expressions, it doesn't
* matter when the opType is
*/
/* recursively rewrite the arguments of this expression */
foreach
(
l
,
expr
->
args
)
{
newArgs
=
lappend
(
newArgs
,
tg_replaceNumberedParam
(
lfirst
(
l
),
pnum
,
rt_ind
,
teeRelName
));
}
/* change the arguments of the expression */
expr
->
args
=
newArgs
;
}
break
;
default:
{
/* ignore other expr types */
}
}
return
expression
;
}
/* tg_rewriteParamsInExpr:
rewrite the params in expressions by using the targetlist entries
from the input parsetrees
this procedure recursively calls itself
it returns a (possibly modified) Node*.
*/
static
Node
*
tg_rewriteParamsInExpr
(
Node
*
expression
,
QueryTreeList
*
inputQlist
)
{
List
*
tl
;
TargetEntry
*
param_tle
,
*
tle
;
Param
*
p
;
int
childno
;
char
*
resname
;
if
(
expression
==
NULL
)
return
NULL
;
switch
(
nodeTag
(
expression
))
{
case
T_Param
:
{
/*
* the node is a parameter, substitute the entry from the
* target list of the child that corresponds to the
* parameter number
*/
p
=
(
Param
*
)
expression
;
/* we only deal with the case of numbered parameters */
if
(
p
->
paramkind
==
PARAM_NUM
)
{
/* paramid's start from 1 */
childno
=
p
->
paramid
-
1
;
if
(
p
->
param_tlist
)
{
/*
* we have a parameter with an attribute like
* $N.foo so match the resname "foo" against the
* target list of the (N-1)th inputQlist
*/
/* param tlist can only have one entry in them! */
param_tle
=
(
TargetEntry
*
)
(
lfirst
(
p
->
param_tlist
));
resname
=
param_tle
->
resdom
->
resname
;
if
(
inputQlist
->
qtrees
[
childno
])
{
foreach
(
tl
,
inputQlist
->
qtrees
[
childno
]
->
targetList
)
{
tle
=
lfirst
(
tl
);
if
(
strcmp
(
resname
,
tle
->
resdom
->
resname
)
==
0
)
return
tle
->
expr
;
}
}
else
elog
(
ERROR
,
"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected"
,
p
->
paramid
);
}
else
{
/* we have $N without the .foo */
/* use the first resdom in the targetlist of the */
/* appropriate child query */
tl
=
inputQlist
->
qtrees
[
childno
]
->
targetList
;
tle
=
lfirst
(
tl
);
return
tle
->
expr
;
}
}
else
elog
(
WARNING
,
"tg_rewriteParamsInExpr: unexpected paramkind value of %d"
,
p
->
paramkind
);
}
break
;
case
T_Expr
:
{
/*
* the node is an expression, we need to recursively call
* ourselves until we find parameter nodes
*/
List
*
l
;
Expr
*
expr
=
(
Expr
*
)
expression
;
List
*
newArgs
;
/*
* we have to make a new args lists because Params can be
* replaced by Var nodes in tg_rewriteParamsInExpr()
*/
newArgs
=
NIL
;
/*
* we only care about argument to expressions, it doesn't
* matter when the opType is
*/
/* recursively rewrite the arguments of this expression */
foreach
(
l
,
expr
->
args
)
{
newArgs
=
lappend
(
newArgs
,
tg_rewriteParamsInExpr
(
lfirst
(
l
),
inputQlist
));
}
/* change the arguments of the expression */
expr
->
args
=
newArgs
;
}
break
;
default:
{
/* ignore other expr types */
}
}
return
expression
;
}
/*
getParamTypes:
given an element, finds its parameter types.
the typev array argument is set to the parameter types.
the parameterCount is returned
this code is very similar to ProcedureDefine() in pg_proc.c
*/
static
int
getParamTypes
(
TgElement
*
elem
,
Oid
*
typev
)
{
/* this code is similar to ProcedureDefine() */
int16
parameterCount
;
bool
defined
;
Oid
toid
;
char
*
t
;
int
i
,
j
;
parameterCount
=
0
;
for
(
i
=
0
;
i
<
FUNC_MAX_ARGS
;
i
++
)
typev
[
i
]
=
0
;
for
(
j
=
0
;
j
<
elem
->
inTypes
->
num
;
j
++
)
{
if
(
parameterCount
==
FUNC_MAX_ARGS
)
{
elog
(
ERROR
,
"getParamTypes: Ingredients cannot take > %d arguments"
,
FUNC_MAX_ARGS
);
}
t
=
elem
->
inTypes
->
val
[
j
];
if
(
strcmp
(
t
,
"opaque"
)
==
0
)
{
elog
(
ERROR
,
"getParamTypes: Ingredient functions cannot take type 'opaque'"
);
}
else
{
toid
=
TypeGet
(
elem
->
inTypes
->
val
[
j
],
&
defined
);
if
(
!
OidIsValid
(
toid
))
elog
(
ERROR
,
"getParamTypes: arg type '%s' is not defined"
,
t
);
if
(
!
defined
)
elog
(
WARNING
,
"getParamTypes: arg type '%s' is only a shell"
,
t
);
}
typev
[
parameterCount
++
]
=
toid
;
}
return
parameterCount
;
}
/*
* tg_parseTeeNode
*
* handles the parsing of the tee node
*
*
*/
static
QueryTreeList
*
tg_parseTeeNode
(
TgRecipe
*
r
,
TgNode
*
n
,
/* the tee node */
int
i
,
/* which input this node is to its parent */
QueryTreeList
*
qList
,
TeeInfo
*
teeInfo
)
{
QueryTreeList
*
q
;
char
*
tt
;
int
rt_ind
;
Query
*
orig
;
/*
* the input Node is a tee node, so we need to do the following: we
* need to parse the child of the tee node, we add that to our query
* tree list we need the name of the tee node table the tee node table
* is the table into which the tee node may materialize results. Call
* it TT we add a range table to our existing query with TT in it we
* need to replace the parameter $i with TT (otherwise the optimizer
* won't know to use the table on expression containining $i) After
* that rewrite, the optimizer will generate sequential scans of TT
*
* Later, in the glue phase, we replace all instances of TT sequential
* scans with the actual Tee node
*/
q
=
tg_parseSubQuery
(
r
,
n
,
teeInfo
);
/* tt is the name of the tee node table */
tt
=
n
->
nodeName
;
if
(
q
)
appendTeeQuery
(
teeInfo
,
q
,
tt
);
orig
=
qList
->
qtrees
[
0
];
rt_ind
=
RangeTablePosn
(
orig
->
rtable
,
tt
);
/*
* check to see that this table is not part of the range table
* already. This usually only happens if multiple inputs are
* connected to the same Tee.
*/
if
(
rt_ind
==
0
)
{
orig
->
rtable
=
lappend
(
orig
->
rtable
,
addRangeTableEntry
(
NULL
,
tt
,
tt
,
FALSE
,
FALSE
));
rt_ind
=
length
(
orig
->
rtable
);
}
orig
->
qual
=
tg_replaceNumberedParam
(
orig
->
qual
,
i
+
1
,
/* params start at 1 */
rt_ind
,
tt
);
return
qList
;
}
/*
* tg_parseSubQuery:
* go backwards from a node and parse the query
*
* the result parse tree is passed back
*
* could return NULL if trying to parse a teeNode
* that's already been processed by another parent
*
*/
static
QueryTreeList
*
tg_parseSubQuery
(
TgRecipe
*
r
,
TgNode
*
n
,
TeeInfo
*
teeInfo
)
{
TgElement
*
elem
;
char
*
funcName
;
Oid
typev
[
FUNC_MAX_ARGS
],
/* eight arguments maximum */
relid
;
int
i
,
parameterCount
;
QueryTreeList
*
qList
;
/* the parse tree of the nodeElement */
QueryTreeList
*
inputQlist
;
/* the list of parse trees for the inputs
* to this node */
QueryTreeList
*
q
;
TgNode
*
child
;
Relation
rel
;
unsigned
int
len
;
TupleDesc
tupdesc
;
qList
=
NULL
;
if
(
n
->
nodeType
==
TG_INGRED_NODE
)
{
/* parse each ingredient node in turn */
elem
=
n
->
nodeElem
;
switch
(
elem
->
srcLang
)
{
case
TG_SQL
:
{
/*
* for SQL ingredients, the SQL query is contained in
* the 'src' field
*/
#ifdef DEBUG_RECIPE
elog
(
WARNING
,
"calling parser with %s"
,
elem
->
src
);
#endif
/* DEBUG_RECIPE */
parameterCount
=
getParamTypes
(
elem
,
typev
);
qList
=
parser
(
elem
->
src
,
typev
,
parameterCount
);
if
(
qList
->
len
>
1
)
{
elog
(
WARNING
,
"tg_parseSubQuery: parser produced > 1 query tree"
);
}
}
break
;
case
TG_C
:
{
/* C ingredients are registered functions in postgres */
/*
* we create a new query string by using the function
* name (found in the 'src' field) and adding
* parameters to it so if the function was FOOBAR and
* took in two arguments, we would create a string
* select FOOBAR($1,$2)
*/
char
newquery
[
1000
];
funcName
=
elem
->
src
;
parameterCount
=
getParamTypes
(
elem
,
typev
);
if
(
parameterCount
>
0
)
{
int
i
;
snprintf
(
newquery
,
1000
,
"select %s($1"
,
funcName
);
for
(
i
=
1
;
i
<
parameterCount
;
i
++
)
snprintf
(
newquery
,
1000
,
"%s,$%d"
,
pstrdup
(
newquery
),
i
);
snprintf
(
newquery
,
1000
,
"%s)"
,
pstrdup
(
newquery
));
}
else
snprintf
(
newquery
,
1000
,
"select %s()"
,
funcName
);
#ifdef DEBUG_RECIPE
elog
(
WARNING
,
"calling parser with %s"
,
newquery
);
#endif
/* DEBUG_RECIPE */
qList
=
parser
(
newquery
,
typev
,
parameterCount
);
if
(
qList
->
len
>
1
)
{
elog
(
WARNING
,
"tg_parseSubQuery: parser produced > 1 query tree"
);
}
}
break
;
case
TG_RECIPE_GRAPH
:
elog
(
WARNING
,
"tg_parseSubQuery: can't parse recipe graph ingredients yet!"
);
break
;
case
TG_COMPILED
:
elog
(
WARNING
,
"tg_parseSubQuery: can't parse compiled ingredients yet!"
);
break
;
default:
elog
(
WARNING
,
"tg_parseSubQuery: unknown srcLang: %d"
,
elem
->
srcLang
);
}
/* parse each of the subrecipes that are input to this node */
if
(
n
->
inNodes
->
num
>
0
)
{
inputQlist
=
malloc
(
sizeof
(
QueryTreeList
));
inputQlist
->
len
=
n
->
inNodes
->
num
+
1
;
inputQlist
->
qtrees
=
(
Query
**
)
malloc
(
inputQlist
->
len
*
sizeof
(
Query
*
));
for
(
i
=
0
;
i
<
n
->
inNodes
->
num
;
i
++
)
{
inputQlist
->
qtrees
[
i
]
=
NULL
;
if
(
n
->
inNodes
->
val
[
i
])
{
if
(
n
->
inNodes
->
val
[
i
]
->
nodeType
==
TG_TEE_NODE
)
{
qList
=
tg_parseTeeNode
(
r
,
n
->
inNodes
->
val
[
i
],
i
,
qList
,
teeInfo
);
}
else
{
/* input node is not a Tee */
q
=
tg_parseSubQuery
(
r
,
n
->
inNodes
->
val
[
i
],
teeInfo
);
Assert
(
q
->
len
==
1
);
inputQlist
->
qtrees
[
i
]
=
q
->
qtrees
[
0
];
}
}
}
/* now, we have all the query trees from our input nodes */
/* transform the original parse tree appropriately */
tg_rewriteQuery
(
r
,
n
,
qList
,
inputQlist
);
}
}
else
if
(
n
->
nodeType
==
TG_EYE_NODE
)
{
/*
* if we hit an eye, we need to stop and make what we have into a
* subrecipe query block
*/
elog
(
WARNING
,
"tg_parseSubQuery: can't handle eye nodes yet"
);
}
else
if
(
n
->
nodeType
==
TG_TEE_NODE
)
{
/*
* if we hit a tee, check to see if the parsing has been done for
* this tee already by the other parent
*/
rel
=
RelationNameGetRelation
(
n
->
nodeName
);
if
(
RelationIsValid
(
rel
))
{
/*
* this tee has already been visited, no need to do any
* further processing
*/
return
NULL
;
}
else
{
/* we need to process the child of the tee first, */
child
=
n
->
inNodes
->
val
[
0
];
if
(
child
->
nodeType
==
TG_TEE_NODE
)
{
/* nested Tee nodes */
qList
=
tg_parseTeeNode
(
r
,
child
,
0
,
qList
,
teeInfo
);
return
qList
;
}
Assert
(
child
!=
NULL
);
/* parse the input node */
q
=
tg_parseSubQuery
(
r
,
child
,
teeInfo
);
Assert
(
q
->
len
==
1
);
/* add the parsed query to the main list of queries */
qList
=
appendQlist
(
qList
,
q
);
/* need to create the tee table here */
/*
* the tee table created is used both for materializing the
* values at the tee node, and for parsing and optimization.
* The optimization needs to have a real table before it will
* consider scans on it
*/
/*
* first, find the type of the tuples being produced by the
* tee. The type is the same as the output type of the child
* node.
*
* NOTE: we are assuming that the child node only has a single
* output here!
*/
getParamTypes
(
child
->
nodeElem
,
typev
);
/*
* the output type is either a complex type, (and is thus a
* relation) or is a simple type
*/
rel
=
RelationNameGetRelation
(
child
->
nodeElem
->
outTypes
->
val
[
0
]);
if
(
RelationIsValid
(
rel
))
{
/*
* for complex types, create new relation with the same
* tuple descriptor as the output table type
*/
len
=
length
(
q
->
qtrees
[
0
]
->
targetList
);
tupdesc
=
rel
->
rd_att
;
relid
=
heap_create_with_catalog
(
child
->
nodeElem
->
outTypes
->
val
[
0
],
tupdesc
,
RELKIND_RELATION
,
false
);
}
else
{
/*
* we have to create a relation with one attribute of the
* simple base type. That attribute will have an attr
* name of "result"
*/
/* NOTE: ignore array types for the time being */
len
=
1
;
tupdesc
=
CreateTemplateTupleDesc
(
len
);
if
(
!
TupleDescInitEntry
(
tupdesc
,
1
,
"result"
,
InvalidOid
,
-
1
,
0
,
false
))
elog
(
WARNING
,
"tg_parseSubQuery: unexpected result from TupleDescInitEntry"
);
else
{
relid
=
heap_create_with_catalog
(
child
->
nodeElem
->
outTypes
->
val
[
0
],
tupdesc
,
RELKIND_RELATION
,
false
);
}
}
}
}
else
if
(
n
->
nodeType
==
TG_RECIPE_NODE
)
elog
(
WARNING
,
"tg_parseSubQuery: can't handle embedded recipes yet!"
);
else
elog
(
WARNING
,
"unknown nodeType: %d"
,
n
->
nodeType
);
return
qList
;
}
/*
* OffsetVarAttno -
* recursively find all the var nodes with the specified varno
* and offset their varattno with the offset
*
* code is similar to OffsetVarNodes in rewriteManip.c
*/
void
OffsetVarAttno
(
Node
*
node
,
int
varno
,
int
offset
)
{
if
(
node
==
NULL
)
return
;
switch
(
nodeTag
(
node
))
{
case
T_TargetEntry
:
{
TargetEntry
*
tle
=
(
TargetEntry
*
)
node
;
OffsetVarAttno
(
tle
->
expr
,
varno
,
offset
);
}
break
;
case
T_Expr
:
{
Expr
*
expr
=
(
Expr
*
)
node
;
OffsetVarAttno
((
Node
*
)
expr
->
args
,
varno
,
offset
);
}
break
;
case
T_Var
:
{
Var
*
var
=
(
Var
*
)
node
;
if
(
var
->
varno
==
varno
)
var
->
varattno
+=
offset
;
}
break
;
case
T_List
:
{
List
*
l
;
foreach
(
l
,
(
List
*
)
node
)
OffsetVarAttno
(
lfirst
(
l
),
varno
,
offset
);
}
break
;
default:
/* ignore the others */
break
;
}
}
/*
* appendQlist
* add the contents of a QueryTreeList q2 to the end of the QueryTreeList
* q1
*
* returns a new querytree list
*/
QueryTreeList
*
appendQlist
(
QueryTreeList
*
q1
,
QueryTreeList
*
q2
)
{
QueryTreeList
*
newq
;
int
i
,
j
;
int
newlen
;
if
(
q1
==
NULL
)
return
q2
;
if
(
q2
==
NULL
)
return
q1
;
newlen
=
q1
->
len
+
q2
->
len
;
newq
=
(
QueryTreeList
*
)
malloc
(
sizeof
(
QueryTreeList
));
newq
->
len
=
newlen
;
newq
->
qtrees
=
(
Query
**
)
malloc
(
newlen
*
sizeof
(
Query
*
));
for
(
i
=
0
;
i
<
q1
->
len
;
i
++
)
newq
->
qtrees
[
i
]
=
q1
->
qtrees
[
i
];
for
(
j
=
0
;
j
<
q2
->
len
;
j
++
)
newq
->
qtrees
[
i
+
j
]
=
q2
->
qtrees
[
j
];
return
newq
;
}
/*
* appendTeeQuery
*
* modify the query field of the teeInfo list of the particular tee node
*/
static
void
appendTeeQuery
(
TeeInfo
*
teeInfo
,
QueryTreeList
*
q
,
char
*
teeNodeName
)
{
int
i
;
Assert
(
teeInfo
);
for
(
i
=
0
;
i
<
teeInfo
->
num
;
i
++
)
{
if
(
strcmp
(
teeInfo
->
val
[
i
].
tpi_relName
,
teeNodeName
)
==
0
)
{
Assert
(
q
->
len
==
1
);
teeInfo
->
val
[
i
].
tpi_parsetree
=
q
->
qtrees
[
0
];
return
;
}
}
elog
(
WARNING
,
"appendTeeQuery: teeNodeName '%s' not found in teeInfo"
);
}
/*
* replaceSeqScan
* replaces sequential scans of a specified relation with the tee plan
* the relation is specified by its index in the range table, rt_ind
*
* returns the modified plan
* the offset_attno is the offset that needs to be added to the parent's
* qual or targetlist because the child plan has been replaced with a tee node
*/
static
void
replaceSeqScan
(
Plan
*
plan
,
Plan
*
parent
,
int
rt_ind
,
Plan
*
tplan
)
{
Scan
*
snode
;
Tee
*
teePlan
;
Result
*
newPlan
;
if
(
plan
==
NULL
)
return
;
if
(
plan
->
type
==
T_SeqScan
)
{
snode
=
(
Scan
*
)
plan
;
if
(
snode
->
scanrelid
==
rt_ind
)
{
/*
* found the sequential scan that should be replaced with the
* tplan.
*/
/* we replace the plan, but we also need to modify its parent */
/*
* replace the sequential scan with a Result node the reason
* we use a result node is so that we get the proper
* projection behavior. The Result node is simply (ab)used as
* a projection node
*/
newPlan
=
makeNode
(
Result
);
newPlan
->
plan
.
cost
=
0
.
0
;
newPlan
->
plan
.
state
=
(
EState
*
)
NULL
;
newPlan
->
plan
.
targetlist
=
plan
->
targetlist
;
newPlan
->
plan
.
lefttree
=
tplan
;
newPlan
->
plan
.
righttree
=
NULL
;
newPlan
->
resconstantqual
=
NULL
;
newPlan
->
resstate
=
NULL
;
/* change all the varno's to 1 */
ChangeVarNodes
((
Node
*
)
newPlan
->
plan
.
targetlist
,
snode
->
scanrelid
,
1
);
if
(
parent
)
{
teePlan
=
(
Tee
*
)
tplan
;
if
(
parent
->
lefttree
==
plan
)
parent
->
lefttree
=
(
Plan
*
)
newPlan
;
else
parent
->
righttree
=
(
Plan
*
)
newPlan
;
if
(
teePlan
->
leftParent
==
NULL
)
teePlan
->
leftParent
=
(
Plan
*
)
newPlan
;
else
teePlan
->
rightParent
=
(
Plan
*
)
newPlan
;
/* comment for now to test out executor-stuff
if (parent->state) {
ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
}
*/
}
}
}
else
{
if
(
plan
->
lefttree
)
replaceSeqScan
(
plan
->
lefttree
,
plan
,
rt_ind
,
tplan
);
if
(
plan
->
righttree
)
replaceSeqScan
(
plan
->
righttree
,
plan
,
rt_ind
,
tplan
);
}
}
/*
* replaceTeeScans
* places the sequential scans of the Tee table with
* a connection to the actual tee plan node
*/
static
Plan
*
replaceTeeScans
(
Plan
*
plan
,
Query
*
parsetree
,
TeeInfo
*
teeInfo
)
{
int
i
;
List
*
rtable
;
RangeTblEntry
*
rte
;
char
prefix
[
5
];
int
rt_ind
;
Plan
*
tplan
;
rtable
=
parsetree
->
rtable
;
if
(
rtable
==
NULL
)
return
plan
;
/*
* look through the range table for the tee relation entry, that will
* give use the varno we need to detect which sequential scans need to
* be replaced with tee nodes
*/
rt_ind
=
0
;
while
(
rtable
!=
NIL
)
{
rte
=
lfirst
(
rtable
);
rtable
=
lnext
(
rtable
);
rt_ind
++
;
/* range table references in varno fields
* start w/ 1 */
/*
* look for the "tee_" prefix in the refname, also check to see
* that the relname and the refname are the same this should
* eliminate any user-specified table and leave us with the tee
* table entries only
*/
if
((
strlen
(
rte
->
refname
)
<
4
)
||
(
strcmp
(
rte
->
relname
,
rte
->
refname
)
!=
0
))
continue
;
StrNCpy
(
prefix
,
rte
->
refname
,
5
);
if
(
strcmp
(
prefix
,
"tee_"
)
==
0
)
{
/* okay, we found a tee node entry in the range table */
/* find the appropriate plan in the teeInfo list */
tplan
=
NULL
;
for
(
i
=
0
;
i
<
teeInfo
->
num
;
i
++
)
{
if
(
strcmp
(
teeInfo
->
val
[
i
].
tpi_relName
,
rte
->
refname
)
==
0
)
tplan
=
teeInfo
->
val
[
i
].
tpi_plan
;
}
if
(
tplan
==
NULL
)
elog
(
WARNING
,
"replaceTeeScans didn't find the corresponding tee plan"
);
/*
* replace the sequential scan node with that var number with
* the tee plan node
*/
replaceSeqScan
(
plan
,
NULL
,
rt_ind
,
tplan
);
}
}
return
plan
;
}
#endif
/* TIOGA */
src/backend/commands/_deadcode/version.c
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* version.c
* This file contains all the rules that govern all version semantics.
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* The version stuff has not been tested under postgres95 and probably
* doesn't work! - jolly 8/19/95
*
*
* $Id: version.c,v 1.30 2002/06/20 20:29:27 momjian Exp $
*
* NOTES
* At the point the version is defined, 2 physical relations are created
* <vname>_added and <vname>_deleted.
*
* In addition, 4 rules are defined which govern the semantics of
* versions w.r.t retrieves, appends, replaces and deletes.
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#define MAX_QUERY_LEN 1024
char
rule_buf
[
MAX_QUERY_LEN
];
/*
* problem: the version system assumes that the rules it declares will
* be fired in the order of declaration, it also assumes
* goh's silly instead semantics. Unfortunately, it is a pain
* to make the version system work with the new semantics.
* However the whole problem can be solved, and some nice
* functionality can be achieved if we get multiple action rules
* to work. So thats what I did -- glass
*
* Well, at least they've been working for about 20 minutes.
*
* So any comments in this code about 1 rule per transction are false...:)
*
*/
/*
* This is needed because the rule system only allows
* *1* rule to be defined per transaction.
*
* NOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
* OOOOOOOOOOOOOOOOOOO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* DONT DO THAT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* If you commit the current Xact all the palloced memory GOES AWAY
* and could be re-palloced in the new Xact and the whole hell breaks
* loose and poor people like me spend 2 hours of their live chassing
* a strange memory bug instead of watching the "Get Smart" marathon
* in NICK !
* DO NOT COMMIT THE XACT, just increase the Cid counter!
* _sp.
*/
#ifdef NOT_USED
static
void
eval_as_new_xact
(
char
*
query
)
{
/*------
* WARNING! do not uncomment the following lines WARNING!
*
* CommitTransactionCommand();
* StartTransactionCommand();
*------
*/
CommandCounterIncrement
();
pg_exec_query
(
query
);
}
#endif
/*
* Define a version.
*/
#ifdef NOT_USED
void
DefineVersion
(
char
*
name
,
char
*
fromRelname
,
char
*
date
)
{
char
*
bname
;
static
char
saved_basename
[
512
];
static
char
saved_snapshot
[
512
];
if
(
date
==
NULL
)
{
/* no time ranges */
bname
=
fromRelname
;
strcpy
(
saved_basename
,
(
char
*
)
bname
);
*
saved_snapshot
=
(
char
)
NULL
;
}
else
{
/* version is a snapshot */
bname
=
fromRelname
;
strcpy
(
saved_basename
,
(
char
*
)
bname
);
sprintf
(
saved_snapshot
,
"['%s']"
,
date
);
}
/*
* Calls the routine ``GetAttrList'' get the list of attributes from
* the base relation. Code is put here so that we only need to look up
* the attribute once for both appends and replaces.
*/
setAttrList
(
bname
);
VersionCreate
(
name
,
saved_basename
);
VersionAppend
(
name
,
saved_basename
);
VersionDelete
(
name
,
saved_basename
,
saved_snapshot
);
VersionReplace
(
name
,
saved_basename
,
saved_snapshot
);
VersionRetrieve
(
name
,
saved_basename
,
saved_snapshot
);
}
#endif
/*
* Creates the deltas.
*/
#ifdef NOT_USED
void
VersionCreate
(
char
*
vname
,
char
*
bname
)
{
static
char
query_buf
[
MAX_QUERY_LEN
];
/*
* Creating the dummy version relation for triggering rules.
*/
sprintf
(
query_buf
,
"SELECT * INTO TABLE %s from %s where 1 =2"
,
vname
,
bname
);
pg_exec_query
(
query_buf
);
/*
* Creating the ``v_added'' relation
*/
sprintf
(
query_buf
,
"SELECT * INTO TABLE %s_added from %s where 1 = 2"
,
vname
,
bname
);
eval_as_new_xact
(
query_buf
);
/*
* Creating the ``v_deleted'' relation.
*/
sprintf
(
query_buf
,
"CREATE TABLE %s_del (DOID oid)"
,
vname
);
eval_as_new_xact
(
query_buf
);
}
#endif
/*
* Given the relation name, does a catalog lookup for that relation and
* sets the global variable 'attr_list' with the list of attributes (names)
* for that relation.
*/
#ifdef NOT_USED
static
void
setAttrList
(
char
*
bname
)
{
Relation
rel
;
int
i
=
0
;
int
maxattrs
=
0
;
char
*
attrname
;
char
temp_buf
[
512
];
int
notfirst
=
0
;
rel
=
heap_openr
(
bname
);
if
(
rel
==
NULL
)
{
elog
(
ERROR
,
"Unable to expand all -- amopenr failed "
);
return
;
}
maxattrs
=
RelationGetNumberOfAttributes
(
rel
);
attr_list
[
0
]
=
'\0'
;
for
(
i
=
maxattrs
-
1
;
i
>
-
1
;
--
i
)
{
attrname
=
NameStr
(
rel
->
rd_att
->
attrs
[
i
]
->
attname
);
if
(
notfirst
==
1
)
sprintf
(
temp_buf
,
", %s = new.%s"
,
attrname
,
attrname
);
else
{
sprintf
(
temp_buf
,
"%s = new.%s"
,
attrname
,
attrname
);
notfirst
=
1
;
}
strcat
(
attr_list
,
temp_buf
);
}
heap_close
(
rel
);
return
;
}
#endif
/*
* This routine defines the rule governing the append semantics of
* versions. All tuples appended to a version gets appended to the
* <vname>_added relation.
*/
#ifdef NOT_USED
static
void
VersionAppend
(
char
*
vname
,
char
*
bname
)
{
sprintf
(
rule_buf
,
"define rewrite rule %s_append is on INSERT to %s do instead append %s_added(%s)"
,
vname
,
vname
,
vname
,
attr_list
);
eval_as_new_xact
(
rule_buf
);
}
#endif
/*
* This routine defines the rule governing the retrieval semantics of
* versions. To retrieve tuples from a version , we need to:
*
* 1. Retrieve all tuples in the <vname>_added relation.
* 2. Retrieve all tuples in the base relation which are not in
* the <vname>_del relation.
*/
#ifdef NOT_USED
void
VersionRetrieve
(
char
*
vname
,
char
*
bname
,
char
*
snapshot
)
{
sprintf
(
rule_buf
,
"define rewrite rule %s_retrieve is on SELECT to %s do instead
\n
\
SELECT %s_1.oid, %s_1.* from _%s in %s%s, %s_1 in (%s_added | _%s) \
where _%s.oid !!= '%s_del.DOID'"
,
vname
,
vname
,
vname
,
vname
,
bname
,
bname
,
snapshot
,
vname
,
vname
,
bname
,
bname
,
vname
);
eval_as_new_xact
(
rule_buf
);
/* printf("%s\n",rule_buf); */
}
#endif
/*
* This routine defines the rules that govern the delete semantics of
* versions. Two things happens when we delete a tuple from a version:
*
* 1. If the tuple to be deleted was added to the version *after*
* the version was created, then we simply delete the tuple
* from the <vname>_added relation.
* 2. If the tuple to be deleted is actually in the base relation,
* then we have to mark that tuple as being deleted by adding
* it to the <vname>_del relation.
*/
#ifdef NOT_USED
void
VersionDelete
(
char
*
vname
,
char
*
bname
,
char
*
snapshot
)
{
sprintf
(
rule_buf
,
"define rewrite rule %s_delete1 is on delete to %s do instead
\n
\
[delete %s_added where current.oid = %s_added.oid
\n
\
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid]
\n
"
,
vname
,
vname
,
vname
,
vname
,
vname
,
bname
,
bname
,
snapshot
,
bname
);
eval_as_new_xact
(
rule_buf
);
#ifdef OLD_REWRITE
sprintf
(
rule_buf
,
"define rewrite rule %s_delete2 is on delete to %s do instead
\n
\
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid
\n
"
,
vname
,
vname
,
vname
,
bname
,
bname
,
snapshot
,
bname
);
eval_as_new_xact
(
rule_buf
);
#endif
/* OLD_REWRITE */
}
#endif
/*
* This routine defines the rules that govern the update semantics
* of versions. To update a tuple in a version:
*
* 1. If the tuple is in <vname>_added, we simply ``replace''
* the tuple (as per postgres style).
* 2. if the tuple is in the base relation, then two things have to
* happen:
* 2.1 The tuple is marked ``deleted'' from the base relation by
* adding the tuple to the <vname>_del relation.
* 2.2 A copy of the tuple is appended to the <vname>_added relation
*/
#ifdef NOT_USED
void
VersionReplace
(
char
*
vname
,
char
*
bname
,
char
*
snapshot
)
{
sprintf
(
rule_buf
,
"define rewrite rule %s_replace1 is on replace to %s do instead
\n
\
[replace %s_added(%s) where current.oid = %s_added.oid
\n
\
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid
\n
\
append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = _%s.oid]
\n
"
,
vname
,
vname
,
vname
,
attr_list
,
vname
,
vname
,
bname
,
bname
,
snapshot
,
bname
,
vname
,
attr_list
,
bname
,
bname
,
snapshot
,
vname
,
bname
);
eval_as_new_xact
(
rule_buf
);
/* printf("%s\n",rule_buf); */
#ifdef OLD_REWRITE
sprintf
(
rule_buf
,
"define rewrite rule %s_replace2 is on replace to %s do
\n
\
append %s_del(DOID = current.oid) from _%s in %s%s \
where current.oid = _%s.oid
\n
"
,
vname
,
vname
,
vname
,
bname
,
bname
,
snapshot
,
bname
);
eval_as_new_xact
(
rule_buf
);
sprintf
(
rule_buf
,
"define rewrite rule %s_replace3 is on replace to %s do instead
\n
\
append %s_added(%s) from _%s in %s%s \
where current.oid !!= '%s_added.oid' and current.oid = \
_%s.oid
\n
"
,
vname
,
vname
,
vname
,
attr_list
,
bname
,
bname
,
snapshot
,
vname
,
bname
);
eval_as_new_xact
(
rule_buf
);
#endif
/* OLD_REWRITE */
/* printf("%s\n",rule_buf); */
}
#endif
src/backend/executor/_deadcode/nodeTee.c
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* nodeTee.c
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* DESCRIPTION
* This code provides support for a tee node, which allows
* multiple parent in a megaplan.
*
* INTERFACE ROUTINES
* ExecTee
* ExecInitTee
* ExecEndTee
*
* $Id: nodeTee.c,v 1.12 2002/06/20 20:29:28 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include <sys/file.h>
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "executor/executor.h"
#include "executor/nodeTee.h"
#include "optimizer/internal.h"
#include "storage/bufmgr.h"
#include "storage/smgr.h"
#include "tcop/pquery.h"
#include "utils/relcache.h"
/* ------------------------------------------------------------------
* ExecInitTee
*
* Create tee state
*
* ------------------------------------------------------------------
*/
bool
ExecInitTee
(
Tee
*
node
,
EState
*
currentEstate
,
Plan
*
parent
)
{
TeeState
*
teeState
;
Plan
*
outerPlan
;
int
len
;
Relation
bufferRel
;
TupleDesc
tupType
;
EState
*
estate
;
/*
* it is possible that the Tee has already been initialized since it
* can be reached by multiple parents. If it is already initialized,
* simply return and do not initialize the children nodes again
*/
if
(
node
->
plan
.
state
)
return
TRUE
;
/*
* assign the node's execution state
*/
/*
* make a new executor state, because we have a different
* es_range_table
*/
/* node->plan.state = estate;*/
estate
=
CreateExecutorState
();
estate
->
es_direction
=
currentEstate
->
es_direction
;
estate
->
es_BaseId
=
currentEstate
->
es_BaseId
;
estate
->
es_BaseId
=
currentEstate
->
es_BaseId
;
estate
->
es_tupleTable
=
currentEstate
->
es_tupleTable
;
estate
->
es_refcount
=
currentEstate
->
es_refcount
;
estate
->
es_junkFilter
=
currentEstate
->
es_junkFilter
;
estate
->
es_snapshot
=
currentEstate
->
es_snapshot
;
/*
* use the range table for Tee subplan since the range tables for the
* two parents may be different
*/
if
(
node
->
rtentries
)
estate
->
es_range_table
=
node
->
rtentries
;
else
estate
->
es_range_table
=
currentEstate
->
es_range_table
;
node
->
plan
.
state
=
estate
;
/*
* create teeState structure
*/
teeState
=
makeNode
(
TeeState
);
teeState
->
tee_leftPlace
=
0
;
teeState
->
tee_rightPlace
=
0
;
teeState
->
tee_lastPlace
=
0
;
teeState
->
tee_bufferRel
=
NULL
;
teeState
->
tee_leftScanDesc
=
NULL
;
teeState
->
tee_rightScanDesc
=
NULL
;
node
->
teestate
=
teeState
;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo
(
estate
,
&
(
teeState
->
cstate
),
parent
);
ExecAssignExprContext
(
estate
,
&
(
teeState
->
cstate
));
#define TEE_NSLOTS 2
/*
* initialize tuple slots
*/
ExecInitResultTupleSlot
(
estate
,
&
(
teeState
->
cstate
));
/* initialize child nodes */
outerPlan
=
outerPlan
((
Plan
*
)
node
);
ExecInitNode
(
outerPlan
,
estate
,
(
Plan
*
)
node
);
/*
* the tuple type info is from the outer plan of this node the result
* type is also the same as the outerplan
*/
ExecAssignResultTypeFromOuterPlan
((
Plan
*
)
node
,
&
(
teeState
->
cstate
));
ExecAssignProjectionInfo
((
Plan
*
)
node
,
&
teeState
->
cstate
);
/*
* initialize temporary relation to buffer tuples
*/
tupType
=
ExecGetResultType
(
&
(
teeState
->
cstate
));
len
=
ExecTargetListLength
(((
Plan
*
)
node
)
->
targetlist
);
/*
* create a catalogued relation even though this is a temporary
* relation
*/
/* cleanup of catalogued relations is easier to do */
if
(
node
->
teeTableName
[
0
]
!=
'\0'
)
{
Relation
r
;
teeState
->
tee_bufferRelname
=
pstrdup
(
node
->
teeTableName
);
/*
* we are given an tee table name, if a relation by that name
* exists, then we open it, else we create it and then open it
*/
r
=
RelationNameGetRelation
(
teeState
->
tee_bufferRelname
);
if
(
RelationIsValid
(
r
))
bufferRel
=
heap_openr
(
teeState
->
tee_bufferRelname
);
else
bufferRel
=
heap_open
(
heap_create_with_catalog
(
teeState
->
tee_bufferRelname
,
tupType
,
RELKIND_RELATION
,
false
));
}
else
{
sprintf
(
teeState
->
tee_bufferRelname
,
"ttemp_%d"
,
/* 'ttemp' for 'tee' temporary */
newoid
());
bufferRel
=
heap_open
(
heap_create_with_catalog
(
teeState
->
tee_bufferRelname
,
tupType
,
RELKIND_RELATION
,
false
));
}
teeState
->
tee_bufferRel
=
bufferRel
;
/*
* initialize a memory context for allocating thing like scan
* descriptors
*/
/*
* we do this so that on cleanup of the tee, we can free things. if we
* didn't have our own memory context, we would be in the memory
* context of the portal that we happen to be using at the moment
*/
teeState
->
tee_mcxt
=
(
MemoryContext
)
CreateGlobalMemory
(
teeState
->
tee_bufferRelname
);
/*
* don't initialize the scan descriptors here because it's not good to
* initialize scan descriptors on empty rels. Wait until the scan
* descriptors are needed before initializing them.
*/
teeState
->
tee_leftScanDesc
=
NULL
;
teeState
->
tee_rightScanDesc
=
NULL
;
return
TRUE
;
}
int
ExecCountSlotsTee
(
Tee
*
node
)
{
/* Tee nodes can't have innerPlans */
return
ExecCountSlotsNode
(
outerPlan
(
node
))
+
TEE_NSLOTS
;
}
/* ----------------------------------------------------------------
initTeeScanDescs
initializes the left and right scandescs on the temporary
relation of a Tee node
must open two separate scan descriptors,
because the left and right scans may be at different points
* ----------------------------------------------------------------
*/
static
void
initTeeScanDescs
(
Tee
*
node
)
{
TeeState
*
teeState
;
Relation
bufferRel
;
ScanDirection
dir
;
Snapshot
snapshot
;
MemoryContext
orig
;
teeState
=
node
->
teestate
;
if
(
teeState
->
tee_leftScanDesc
&&
teeState
->
tee_rightScanDesc
)
return
;
orig
=
CurrentMemoryContext
;
MemoryContextSwitchTo
(
teeState
->
tee_mcxt
);
bufferRel
=
teeState
->
tee_bufferRel
;
dir
=
((
Plan
*
)
node
)
->
state
->
es_direction
;
/* backwards not handled
* yet XXX */
snapshot
=
((
Plan
*
)
node
)
->
state
->
es_snapshot
;
if
(
teeState
->
tee_leftScanDesc
==
NULL
)
{
teeState
->
tee_leftScanDesc
=
heap_beginscan
(
bufferRel
,
ScanDirectionIsBackward
(
dir
),
snapshot
,
0
,
/* num scan keys */
NULL
/* scan keys */
);
}
if
(
teeState
->
tee_rightScanDesc
==
NULL
)
{
teeState
->
tee_rightScanDesc
=
heap_beginscan
(
bufferRel
,
ScanDirectionIsBackward
(
dir
),
snapshot
,
0
,
/* num scan keys */
NULL
/* scan keys */
);
}
MemoryContextSwitchTo
(
orig
);
}
/* ----------------------------------------------------------------
* ExecTee(node)
*
*
* A Tee serves to connect a subplan to multiple parents.
* the subplan is always the outplan of the Tee node.
*
* The Tee gets requests from either leftParent or rightParent,
* fetches the result tuple from the child, and then
* stored the result into a temporary relation (serving as a queue).
* leftPlace and rightPlace keep track of where the left and rightParents
* are.
* If a parent requests a tuple and that parent is not at the end
* of the temporary relation, then the request is satisfied from
* the queue instead of by executing the child plan
*
* ----------------------------------------------------------------
*/
TupleTableSlot
*
ExecTee
(
Tee
*
node
,
Plan
*
parent
)
{
EState
*
estate
;
TeeState
*
teeState
;
int
leftPlace
,
rightPlace
,
lastPlace
;
int
branch
;
TupleTableSlot
*
result
;
TupleTableSlot
*
slot
;
Plan
*
childNode
;
ScanDirection
dir
;
HeapTuple
heapTuple
;
Relation
bufferRel
;
HeapScanDesc
scanDesc
;
estate
=
((
Plan
*
)
node
)
->
state
;
teeState
=
node
->
teestate
;
leftPlace
=
teeState
->
tee_leftPlace
;
rightPlace
=
teeState
->
tee_rightPlace
;
lastPlace
=
teeState
->
tee_lastPlace
;
bufferRel
=
teeState
->
tee_bufferRel
;
childNode
=
outerPlan
(
node
);
dir
=
estate
->
es_direction
;
/* XXX doesn't handle backwards direction yet */
if
(
parent
==
node
->
leftParent
)
branch
=
leftPlace
;
else
if
((
parent
==
node
->
rightParent
)
||
(
parent
==
(
Plan
*
)
node
))
/*
* the tee node could be the root node of the plan, in which case,
* we treat it like a right-parent pull
*/
branch
=
rightPlace
;
else
{
elog
(
ERROR
,
"A Tee node can only be executed from its left or right parent
\n
"
);
return
NULL
;
}
if
(
branch
==
lastPlace
)
{
/* we're at the end of the queue already,
* - get a new tuple from the child plan,
* - store it in the queue, - increment
* lastPlace, - increment leftPlace or
* rightPlace as appropriate, - and return
* result */
slot
=
ExecProcNode
(
childNode
,
(
Plan
*
)
node
);
if
(
!
TupIsNull
(
slot
))
{
/*
* heap_insert changes something...
*/
if
(
slot
->
ttc_buffer
!=
InvalidBuffer
)
heapTuple
=
heap_copytuple
(
slot
->
val
);
else
heapTuple
=
slot
->
val
;
/* insert into temporary relation */
heap_insert
(
bufferRel
,
heapTuple
);
if
(
slot
->
ttc_buffer
!=
InvalidBuffer
)
heap_freetuple
(
heapTuple
);
/*
* once there is data in the temporary relation, ensure that
* the left and right scandescs are initialized
*/
initTeeScanDescs
(
node
);
scanDesc
=
(
parent
==
node
->
leftParent
)
?
teeState
->
tee_leftScanDesc
:
teeState
->
tee_rightScanDesc
;
{
/*
* move the scandesc forward so we don't re-read this
* tuple later
*/
HeapTuple
throwAway
;
/* Buffer buffer; */
throwAway
=
heap_getnext
(
scanDesc
,
ScanDirectionIsBackward
(
dir
));
}
/*
* set the shouldFree field of the child's slot so that when
* the child's slot is free'd, this tuple isn't free'd also
*/
/*
* does this mean this tuple has to be garbage collected
* later??
*/
slot
->
ttc_shouldFree
=
false
;
teeState
->
tee_lastPlace
=
lastPlace
+
1
;
}
result
=
slot
;
}
else
{
/* the desired data already exists in the
* temporary relation */
scanDesc
=
(
parent
==
node
->
leftParent
)
?
teeState
->
tee_leftScanDesc
:
teeState
->
tee_rightScanDesc
;
heapTuple
=
heap_getnext
(
scanDesc
,
ScanDirectionIsBackward
(
dir
));
/*
* Increase the pin count on the buffer page, because the tuple
* stored in the slot also points to it (as well as the scan
* descriptor). If we don't, ExecStoreTuple will decrease the pin
* count on the next iteration.
*/
if
(
scanDesc
->
rs_cbuf
!=
InvalidBuffer
)
IncrBufferRefCount
(
scanDesc
->
rs_cbuf
);
slot
=
teeState
->
cstate
.
cs_ResultTupleSlot
;
slot
->
ttc_tupleDescriptor
=
RelationGetDescr
(
bufferRel
);
result
=
ExecStoreTuple
(
heapTuple
,
/* tuple to store */
slot
,
/* slot to store in */
scanDesc
->
rs_cbuf
,
/* this tuple's buffer */
false
);
/* don't free stuff from
* heap_getnext */
}
if
(
parent
==
node
->
leftParent
)
teeState
->
tee_leftPlace
=
leftPlace
+
1
;
else
teeState
->
tee_rightPlace
=
rightPlace
+
1
;
return
result
;
}
/* ---------------------------------------------------------------------
* ExecEndTee
*
* End the Tee node, and free up any storage
* since a Tee node can be downstream of multiple parent nodes,
* only free when both parents are done
* --------------------------------------------------------------------
*/
void
ExecEndTee
(
Tee
*
node
,
Plan
*
parent
)
{
EState
*
estate
;
TeeState
*
teeState
;
int
leftPlace
,
rightPlace
,
lastPlace
;
Relation
bufferRel
;
MemoryContext
orig
;
estate
=
((
Plan
*
)
node
)
->
state
;
teeState
=
node
->
teestate
;
leftPlace
=
teeState
->
tee_leftPlace
;
rightPlace
=
teeState
->
tee_rightPlace
;
lastPlace
=
teeState
->
tee_lastPlace
;
if
(
!
node
->
leftParent
||
parent
==
node
->
leftParent
)
leftPlace
=
-
1
;
if
(
!
node
->
rightParent
||
parent
==
node
->
rightParent
)
rightPlace
=
-
1
;
if
(
parent
==
(
Plan
*
)
node
)
rightPlace
=
leftPlace
=
-
1
;
teeState
->
tee_leftPlace
=
leftPlace
;
teeState
->
tee_rightPlace
=
rightPlace
;
if
((
leftPlace
==
-
1
)
&&
(
rightPlace
==
-
1
))
{
/* remove the temporary relations */
/* and close the scan descriptors */
bufferRel
=
teeState
->
tee_bufferRel
;
if
(
bufferRel
)
{
heap_drop
(
bufferRel
);
teeState
->
tee_bufferRel
=
NULL
;
if
(
teeState
->
tee_mcxt
)
{
orig
=
CurrentMemoryContext
;
MemoryContextSwitchTo
(
teeState
->
tee_mcxt
);
}
else
orig
=
0
;
if
(
teeState
->
tee_leftScanDesc
)
{
heap_endscan
(
teeState
->
tee_leftScanDesc
);
teeState
->
tee_leftScanDesc
=
NULL
;
}
if
(
teeState
->
tee_rightScanDesc
)
{
heap_endscan
(
teeState
->
tee_rightScanDesc
);
teeState
->
tee_rightScanDesc
=
NULL
;
}
if
(
teeState
->
tee_mcxt
)
{
MemoryContextSwitchTo
(
orig
);
teeState
->
tee_mcxt
=
NULL
;
}
}
}
}
src/backend/optimizer/path/_deadcode/predmig.c
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* predmig.c
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.15 2002/06/20 20:29:30 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
** DESCRIPTION
** Main Routines to handle Predicate Migration (i.e. correct optimization
** of queries with expensive functions.)
**
** The reasoning behind some of these algorithms is rather detailed.
** Have a look at Sequoia Tech Report 92/13 for more info. Also
** see Monma and Sidney's paper "Sequencing with Series-Parallel
** Precedence Constraints", in "Mathematics of Operations Research",
** volume 4 (1979), pp. 215-224.
**
** The main thing that this code does that wasn't handled in xfunc.c is
** it considers the possibility that two joins in a stream may not
** be ordered by ascending rank -- in such a scenario, it may be optimal
** to pullup more restrictions than we did via xfunc_try_pullup.
**
** This code in some sense generalizes xfunc_try_pullup; if you
** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this
** code will do everything that xfunc_try_pullup would have, and maybe
** more. However, this results in no pruning, which may slow down the
** optimizer and/or cause the system to run out of memory.
** -- JMH, 11/13/92
*/
#include "nodes/pg_list.h"
#include "nodes/nodes.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "optimizer/pathnode.h"
#include "optimizer/internal.h"
#include "optimizer/cost.h"
#include "optimizer/keys.h"
#include "optimizer/tlist.h"
#define is_clause(node) (get_cinfo(node))
/* a stream node
* represents a clause
* (not a join) iff it has
* a non-NULL cinfo field */
static
void
xfunc_predmig
(
JoinPath
pathnode
,
Stream
streamroot
,
Stream
laststream
,
bool
*
progressp
);
static
bool
xfunc_series_llel
(
Stream
stream
);
static
bool
xfunc_llel_chains
(
Stream
root
,
Stream
bottom
);
static
Stream
xfunc_complete_stream
(
Stream
stream
);
static
bool
xfunc_prdmig_pullup
(
Stream
origstream
,
Stream
pullme
,
JoinPath
joinpath
);
static
void
xfunc_form_groups
(
Stream
root
,
Stream
bottom
);
static
void
xfunc_free_stream
(
Stream
root
);
static
Stream
xfunc_add_clauses
(
Stream
current
);
static
void
xfunc_setup_group
(
Stream
node
,
Stream
bottom
);
static
Stream
xfunc_streaminsert
(
RestrictInfo
restrictinfo
,
Stream
current
,
int
clausetype
);
static
int
xfunc_num_relids
(
Stream
node
);
static
StreamPtr
xfunc_get_downjoin
(
Stream
node
);
static
StreamPtr
xfunc_get_upjoin
(
Stream
node
);
static
Stream
xfunc_stream_qsort
(
Stream
root
,
Stream
bottom
);
static
int
xfunc_stream_compare
(
void
*
arg1
,
void
*
arg2
);
static
bool
xfunc_check_stream
(
Stream
node
);
static
bool
xfunc_in_stream
(
Stream
node
,
Stream
stream
);
/* ----------------- MAIN FUNCTIONS ------------------------ */
/*
** xfunc_do_predmig
** wrapper for Predicate Migration. It calls xfunc_predmig until no
** more progress is made.
** return value says if any changes were ever made.
*/
bool
xfunc_do_predmig
(
Path
root
)
{
bool
progress
,
changed
=
false
;
if
(
is_join
(
root
))
do
{
progress
=
false
;
Assert
(
IsA
(
root
,
JoinPath
));
xfunc_predmig
((
JoinPath
)
root
,
(
Stream
)
NULL
,
(
Stream
)
NULL
,
&
progress
);
if
(
changed
&&
progress
)
elog
(
DEBUG
,
"Needed to do a second round of predmig!
\n
"
);
if
(
progress
)
changed
=
true
;
}
while
(
progress
);
return
changed
;
}
/*
** xfunc_predmig
** The main routine for Predicate Migration. It traverses a join tree,
** and for each root-to-leaf path in the plan tree it constructs a
** "Stream", which it passes to xfunc_series_llel for optimization.
** Destructively modifies the join tree (via predicate pullup).
*/
static
void
xfunc_predmig
(
JoinPath
pathnode
,
/* root of the join tree */
Stream
streamroot
,
Stream
laststream
,
/* for recursive calls -- these are the
* root of the stream under construction,
* and the lowest node created so far */
bool
*
progressp
)
{
Stream
newstream
;
/*
* * traverse the join tree dfs-style, constructing a stream as you
* go. * When you hit a scan node, pass the stream off to
* xfunc_series_llel.
*/
/* sanity check */
if
((
!
streamroot
&&
laststream
)
||
(
streamroot
&&
!
laststream
))
elog
(
ERROR
,
"called xfunc_predmig with bad inputs"
);
if
(
streamroot
)
Assert
(
xfunc_check_stream
(
streamroot
));
/* add path node to stream */
newstream
=
RMakeStream
();
if
(
!
streamroot
)
streamroot
=
newstream
;
set_upstream
(
newstream
,
(
StreamPtr
)
laststream
);
if
(
laststream
)
set_downstream
(
laststream
,
(
StreamPtr
)
newstream
);
set_downstream
(
newstream
,
(
StreamPtr
)
NULL
);
set_pathptr
(
newstream
,
(
pathPtr
)
pathnode
);
set_cinfo
(
newstream
,
(
RestrictInfo
)
NULL
);
set_clausetype
(
newstream
,
XFUNC_UNKNOWN
);
/* base case: we're at a leaf, call xfunc_series_llel */
if
(
!
is_join
(
pathnode
))
{
/* form a fleshed-out copy of the stream */
Stream
fullstream
=
xfunc_complete_stream
(
streamroot
);
/* sort it via series-llel */
if
(
xfunc_series_llel
(
fullstream
))
*
progressp
=
true
;
/* free up the copy */
xfunc_free_stream
(
fullstream
);
}
else
{
/* visit left child */
xfunc_predmig
((
JoinPath
)
get_outerjoinpath
(
pathnode
),
streamroot
,
newstream
,
progressp
);
/* visit right child */
xfunc_predmig
((
JoinPath
)
get_innerjoinpath
(
pathnode
),
streamroot
,
newstream
,
progressp
);
}
/* remove this node */
if
(
get_upstream
(
newstream
))
set_downstream
((
Stream
)
get_upstream
(
newstream
),
(
StreamPtr
)
NULL
);
pfree
(
newstream
);
}
/*
** xfunc_series_llel
** A flavor of Monma and Sidney's Series-Parallel algorithm.
** Traverse stream downwards. When you find a node with restrictions on it,
** call xfunc_llel_chains on the substream from root to that node.
*/
static
bool
xfunc_series_llel
(
Stream
stream
)
{
Stream
temp
,
next
;
bool
progress
=
false
;
for
(
temp
=
stream
;
temp
!=
(
Stream
)
NULL
;
temp
=
next
)
{
next
=
(
Stream
)
xfunc_get_downjoin
(
temp
);
/*
* * if there are restrictions/secondary join clauses above this *
* node, call xfunc_llel_chains
*/
if
(
get_upstream
(
temp
)
&&
is_clause
((
Stream
)
get_upstream
(
temp
)))
if
(
xfunc_llel_chains
(
stream
,
temp
))
progress
=
true
;
}
return
progress
;
}
/*
** xfunc_llel_chains
** A flavor of Monma and Sidney's Parallel Chains algorithm.
** Given a stream which has been well-ordered except for its lowermost
** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate.
** What that means here is to form groups in the chain above the lowest
** join node above bottom inclusive, and then take all the restrictions
** following bottom, and try to pull them up as far as possible.
*/
static
bool
xfunc_llel_chains
(
Stream
root
,
Stream
bottom
)
{
bool
progress
=
false
;
Stream
origstream
;
Stream
tmpstream
,
pathstream
;
Stream
rootcopy
=
root
;
Assert
(
xfunc_check_stream
(
root
));
/* xfunc_prdmig_pullup will need an unmodified copy of the stream */
origstream
=
(
Stream
)
copyObject
((
Node
)
root
);
/* form groups among ill-ordered nodes */
xfunc_form_groups
(
root
,
bottom
);
/* sort chain by rank */
Assert
(
xfunc_in_stream
(
bottom
,
root
));
rootcopy
=
xfunc_stream_qsort
(
root
,
bottom
);
/*
* * traverse sorted stream -- if any restriction has moved above a
* join, * we must pull it up in the plan. That is, make plan tree *
* reflect order of sorted stream.
*/
for
(
tmpstream
=
rootcopy
,
pathstream
=
(
Stream
)
xfunc_get_downjoin
(
rootcopy
);
tmpstream
!=
(
Stream
)
NULL
&&
pathstream
!=
(
Stream
)
NULL
;
tmpstream
=
(
Stream
)
get_downstream
(
tmpstream
))
{
if
(
is_clause
(
tmpstream
)
&&
get_pathptr
(
pathstream
)
!=
get_pathptr
(
tmpstream
))
{
/*
* * If restriction moved above a Join after sort, we pull it *
* up in the join plan. * If restriction moved down, we
* ignore it. * This is because Joey's Sequoia paper proves
* that * restrictions should never move down. If this * one
* were moved down, it would violate "semantic correctness", *
* i.e. it would be lower than the attributes it references.
*/
Assert
(
xfunc_num_relids
(
pathstream
)
>
xfunc_num_relids
(
tmpstream
));
progress
=
xfunc_prdmig_pullup
(
origstream
,
tmpstream
,
(
JoinPath
)
get_pathptr
(
pathstream
));
}
if
(
get_downstream
(
tmpstream
))
pathstream
=
(
Stream
)
xfunc_get_downjoin
((
Stream
)
get_downstream
(
tmpstream
));
}
/* free up origstream */
xfunc_free_stream
(
origstream
);
return
progress
;
}
/*
** xfunc_complete_stream
** Given a stream composed of join nodes only, make a copy containing the
** join nodes along with the associated restriction nodes.
*/
static
Stream
xfunc_complete_stream
(
Stream
stream
)
{
Stream
tmpstream
,
copystream
,
curstream
=
(
Stream
)
NULL
;
copystream
=
(
Stream
)
copyObject
((
Node
)
stream
);
Assert
(
xfunc_check_stream
(
copystream
));
curstream
=
copystream
;
Assert
(
!
is_clause
(
curstream
));
/* curstream = (Stream)xfunc_get_downjoin(curstream); */
while
(
curstream
!=
(
Stream
)
NULL
)
{
xfunc_add_clauses
(
curstream
);
curstream
=
(
Stream
)
xfunc_get_downjoin
(
curstream
);
}
/* find top of stream and return it */
for
(
tmpstream
=
copystream
;
get_upstream
(
tmpstream
)
!=
(
StreamPtr
)
NULL
;
tmpstream
=
(
Stream
)
get_upstream
(
tmpstream
))
/* no body in for loop */
;
return
tmpstream
;
}
/*
** xfunc_prdmig_pullup
** pullup a clause in a path above joinpath. Since the JoinPath tree
** doesn't have upward pointers, it's difficult to deal with. Thus we
** require the original stream, which maintains pointers to all the path
** nodes. We use the original stream to find out what joins are
** above the clause.
*/
static
bool
xfunc_prdmig_pullup
(
Stream
origstream
,
Stream
pullme
,
JoinPath
joinpath
)
{
RestrictInfo
restrictinfo
=
get_cinfo
(
pullme
);
bool
progress
=
false
;
Stream
upjoin
,
orignode
,
temp
;
int
whichchild
;
/* find node in origstream that contains clause */
for
(
orignode
=
origstream
;
orignode
!=
(
Stream
)
NULL
&&
get_cinfo
(
orignode
)
!=
restrictinfo
;
orignode
=
(
Stream
)
get_downstream
(
orignode
))
/* empty body in for loop */
;
if
(
!
orignode
)
elog
(
ERROR
,
"Didn't find matching node in original stream"
);
/* pull up this node as far as it should go */
for
(
upjoin
=
(
Stream
)
xfunc_get_upjoin
(
orignode
);
upjoin
!=
(
Stream
)
NULL
&&
(
JoinPath
)
get_pathptr
((
Stream
)
xfunc_get_downjoin
(
upjoin
))
!=
joinpath
;
upjoin
=
(
Stream
)
xfunc_get_upjoin
(
upjoin
))
{
#ifdef DEBUG
elog
(
DEBUG
,
"pulling up in xfunc_predmig_pullup!"
);
#endif
/* move clause up in path */
if
(
get_pathptr
((
Stream
)
get_downstream
(
upjoin
))
==
(
pathPtr
)
get_outerjoinpath
((
JoinPath
)
get_pathptr
(
upjoin
)))
whichchild
=
OUTER
;
else
whichchild
=
INNER
;
restrictinfo
=
xfunc_pullup
((
Path
)
get_pathptr
((
Stream
)
get_downstream
(
upjoin
)),
(
JoinPath
)
get_pathptr
(
upjoin
),
restrictinfo
,
whichchild
,
get_clausetype
(
orignode
));
set_pathptr
(
pullme
,
get_pathptr
(
upjoin
));
/* pullme has been moved into locrestrictinfo */
set_clausetype
(
pullme
,
XFUNC_LOCPRD
);
/*
* * xfunc_pullup makes new path nodes for children of *
* get_pathptr(current). We must modify the stream nodes to point *
* to these path nodes
*/
if
(
whichchild
==
OUTER
)
{
for
(
temp
=
(
Stream
)
get_downstream
(
upjoin
);
is_clause
(
temp
);
temp
=
(
Stream
)
get_downstream
(
temp
))
set_pathptr
(
temp
,
(
pathPtr
)
get_outerjoinpath
((
JoinPath
)
get_pathptr
(
upjoin
)));
set_pathptr
(
temp
,
(
pathPtr
)
get_outerjoinpath
((
JoinPath
)
get_pathptr
(
upjoin
)));
}
else
{
for
(
temp
=
(
Stream
)
get_downstream
(
upjoin
);
is_clause
(
temp
);
temp
=
(
Stream
)
get_downstream
(
temp
))
set_pathptr
(
temp
,
(
pathPtr
)
get_innerjoinpath
((
JoinPath
)
get_pathptr
(
upjoin
)));
set_pathptr
(
temp
,
(
pathPtr
)
get_innerjoinpath
((
JoinPath
)
get_pathptr
(
upjoin
)));
}
progress
=
true
;
}
if
(
!
progress
)
elog
(
DEBUG
,
"didn't succeed in pulling up in xfunc_prdmig_pullup"
);
return
progress
;
}
/*
** xfunc_form_groups
** A group is a pair of stream nodes a,b such that a is constrained to
** precede b (for instance if a and b are both joins), but rank(a) > rank(b).
** In such a situation, Monma and Sidney prove that no clauses should end
** up between a and b, and therefore we may treat them as a group, with
** selectivity equal to the product of their selectivities, and cost
** equal to the cost of the first plus the selectivity of the first times the
** cost of the second. We define each node to be in a group by itself,
** and then repeatedly find adjacent groups which are ordered by descending
** rank, and make larger groups. You know that two adjacent nodes are in a
** group together if the lower has groupup set to true. They will both have
** the same groupcost and groupsel (since they're in the same group!)
*/
static
void
xfunc_form_groups
(
Query
*
queryInfo
,
Stream
root
,
Stream
bottom
)
{
Stream
temp
,
parent
;
int
lowest
=
xfunc_num_relids
((
Stream
)
xfunc_get_upjoin
(
bottom
));
bool
progress
;
LispValue
primjoin
;
int
whichchild
;
if
(
!
lowest
)
return
;
/* no joins in stream, so no groups */
/* initialize groups to be single nodes */
for
(
temp
=
root
;
temp
!=
(
Stream
)
NULL
&&
temp
!=
bottom
;
temp
=
(
Stream
)
get_downstream
(
temp
))
{
/* if a Join node */
if
(
!
is_clause
(
temp
))
{
if
(
get_pathptr
((
Stream
)
get_downstream
(
temp
))
==
(
pathPtr
)
get_outerjoinpath
((
JoinPath
)
get_pathptr
(
temp
)))
whichchild
=
OUTER
;
else
whichchild
=
INNER
;
set_groupcost
(
temp
,
xfunc_join_expense
((
JoinPath
)
get_pathptr
(
temp
),
whichchild
));
if
(
primjoin
=
xfunc_primary_join
((
JoinPath
)
get_pathptr
(
temp
)))
{
set_groupsel
(
temp
,
compute_clause_selec
(
queryInfo
,
primjoin
,
NIL
));
}
else
set_groupsel
(
temp
,
1
.
0
);
}
else
/* a restriction, or 2-ary join pred */
{
set_groupcost
(
temp
,
xfunc_expense
(
queryInfo
,
get_clause
(
get_cinfo
(
temp
))));
set_groupsel
(
temp
,
compute_clause_selec
(
queryInfo
,
get_clause
(
get_cinfo
(
temp
)),
NIL
));
}
set_groupup
(
temp
,
false
);
}
/* make passes upwards, forming groups */
do
{
progress
=
false
;
for
(
temp
=
(
Stream
)
get_upstream
(
bottom
);
temp
!=
(
Stream
)
NULL
;
temp
=
(
Stream
)
get_upstream
(
temp
))
{
/* check for grouping with node upstream */
if
(
!
get_groupup
(
temp
)
&&
/* not already grouped */
(
parent
=
(
Stream
)
get_upstream
(
temp
))
!=
(
Stream
)
NULL
&&
/* temp is a join or temp is the top of a group */
(
is_join
((
Path
)
get_pathptr
(
temp
))
||
get_downstream
(
temp
)
&&
get_groupup
((
Stream
)
get_downstream
(
temp
)))
&&
get_grouprank
(
parent
)
<
get_grouprank
(
temp
))
{
progress
=
true
;
/* we formed a new group */
set_groupup
(
temp
,
true
);
set_groupcost
(
temp
,
get_groupcost
(
temp
)
+
get_groupsel
(
temp
)
*
get_groupcost
(
parent
));
set_groupsel
(
temp
,
get_groupsel
(
temp
)
*
get_groupsel
(
parent
));
/* fix costs and sels of all members of group */
xfunc_setup_group
(
temp
,
bottom
);
}
}
}
while
(
progress
);
}
/* ------------------- UTILITY FUNCTIONS ------------------------- */
/*
** xfunc_free_stream
** walk down a stream and pfree it
*/
static
void
xfunc_free_stream
(
Stream
root
)
{
Stream
cur
,
next
;
Assert
(
xfunc_check_stream
(
root
));
if
(
root
!=
(
Stream
)
NULL
)
for
(
cur
=
root
;
cur
!=
(
Stream
)
NULL
;
cur
=
next
)
{
next
=
(
Stream
)
get_downstream
(
cur
);
pfree
(
cur
);
}
}
/*
** xfunc_add<_clauses
** find any clauses above current, and insert them into stream as
** appropriate. Return uppermost clause inserted, or current if none.
*/
static
Stream
xfunc_add_clauses
(
Stream
current
)
{
Stream
topnode
=
current
;
LispValue
temp
;
LispValue
primjoin
;
/* first add in the local clauses */
foreach
(
temp
,
get_loc_restrictinfo
((
Path
)
get_pathptr
(
current
)))
{
topnode
=
xfunc_streaminsert
((
RestrictInfo
)
lfirst
(
temp
),
topnode
,
XFUNC_LOCPRD
);
}
/* and add in the join clauses */
if
(
IsA
(
get_pathptr
(
current
),
JoinPath
))
{
primjoin
=
xfunc_primary_join
((
JoinPath
)
get_pathptr
(
current
));
foreach
(
temp
,
get_pathrestrictinfo
((
JoinPath
)
get_pathptr
(
current
)))
{
if
(
!
equal
(
get_clause
((
RestrictInfo
)
lfirst
(
temp
)),
primjoin
))
topnode
=
xfunc_streaminsert
((
RestrictInfo
)
lfirst
(
temp
),
topnode
,
XFUNC_JOINPRD
);
}
}
return
topnode
;
}
/*
** xfunc_setup_group
** find all elements of stream that are grouped with node and are above
** bottom, and set their groupcost and groupsel to be the same as node's.
*/
static
void
xfunc_setup_group
(
Stream
node
,
Stream
bottom
)
{
Stream
temp
;
if
(
node
!=
bottom
)
/* traverse downwards */
for
(
temp
=
(
Stream
)
get_downstream
(
node
);
temp
!=
(
Stream
)
NULL
&&
temp
!=
bottom
;
temp
=
(
Stream
)
get_downstream
(
temp
))
{
if
(
!
get_groupup
(
temp
))
break
;
else
{
set_groupcost
(
temp
,
get_groupcost
(
node
));
set_groupsel
(
temp
,
get_groupsel
(
node
));
}
}
/* traverse upwards */
for
(
temp
=
(
Stream
)
get_upstream
(
node
);
temp
!=
(
Stream
)
NULL
;
temp
=
(
Stream
)
get_upstream
(
temp
))
{
if
(
!
get_groupup
((
Stream
)
get_downstream
(
temp
)))
break
;
else
{
set_groupcost
(
temp
,
get_groupcost
(
node
));
set_groupsel
(
temp
,
get_groupsel
(
node
));
}
}
}
/*
** xfunc_streaminsert
** Make a new Stream node to hold clause, and insert it above current.
** Return new node.
*/
static
Stream
xfunc_streaminsert
(
RestrictInfo
restrictinfo
,
Stream
current
,
int
clausetype
)
/* XFUNC_LOCPRD or XFUNC_JOINPRD */
{
Stream
newstream
=
RMakeStream
();
set_upstream
(
newstream
,
get_upstream
(
current
));
if
(
get_upstream
(
current
))
set_downstream
((
Stream
)
(
get_upstream
(
current
)),
(
StreamPtr
)
newstream
);
set_upstream
(
current
,
(
StreamPtr
)
newstream
);
set_downstream
(
newstream
,
(
StreamPtr
)
current
);
set_pathptr
(
newstream
,
get_pathptr
(
current
));
set_cinfo
(
newstream
,
restrictinfo
);
set_clausetype
(
newstream
,
clausetype
);
return
newstream
;
}
/*
** Given a Stream node, find the number of relids referenced in the pathnode
** associated with the stream node. The number of relids gives a unique
** ordering on the joins in a stream, which we use to compare the height of
** join nodes.
*/
static
int
xfunc_num_relids
(
Stream
node
)
{
if
(
!
node
||
!
IsA
(
get_pathptr
(
node
),
JoinPath
))
return
0
;
else
return
(
length
(
get_relids
(
get_parent
((
JoinPath
)
get_pathptr
(
node
)))));
}
/*
** xfunc_get_downjoin
** Given a stream node, find the next lowest node which points to a
** join predicate or a scan node.
*/
static
StreamPtr
xfunc_get_downjoin
(
Stream
node
)
{
Stream
temp
;
if
(
!
is_clause
(
node
))
/* if this is a join */
node
=
(
Stream
)
get_downstream
(
node
);
for
(
temp
=
node
;
temp
&&
is_clause
(
temp
);
temp
=
(
Stream
)
get_downstream
(
temp
))
/* empty body in for loop */
;
return
(
StreamPtr
)
temp
;
}
/*
** xfunc_get_upjoin
** same as above, but upwards.
*/
static
StreamPtr
xfunc_get_upjoin
(
Stream
node
)
{
Stream
temp
;
if
(
!
is_clause
(
node
))
/* if this is a join */
node
=
(
Stream
)
get_upstream
(
node
);
for
(
temp
=
node
;
temp
&&
is_clause
(
temp
);
temp
=
(
Stream
)
get_upstream
(
temp
))
/* empty body in for loop */
;
return
(
StreamPtr
)
temp
;
}
/*
** xfunc_stream_qsort
** Given a stream, sort by group rank the elements in the stream from the
** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root.
*/
static
Stream
xfunc_stream_qsort
(
Stream
root
,
Stream
bottom
)
{
int
i
;
size_t
num
;
Stream
*
nodearray
,
output
;
Stream
tmp
;
/* find size of list */
for
(
num
=
0
,
tmp
=
root
;
tmp
!=
bottom
;
tmp
=
(
Stream
)
get_downstream
(
tmp
))
num
++
;
if
(
num
<=
1
)
return
root
;
/* copy elements of the list into an array */
nodearray
=
(
Stream
*
)
palloc
(
num
*
sizeof
(
Stream
));
for
(
tmp
=
root
,
i
=
0
;
tmp
!=
bottom
;
tmp
=
(
Stream
)
get_downstream
(
tmp
),
i
++
)
nodearray
[
i
]
=
tmp
;
/* sort the array */
qsort
(
nodearray
,
num
,
sizeof
(
LispValue
),
xfunc_stream_compare
);
/* paste together the array elements */
output
=
nodearray
[
num
-
1
];
set_upstream
(
output
,
(
StreamPtr
)
NULL
);
for
(
i
=
num
-
2
;
i
>=
0
;
i
--
)
{
set_downstream
(
nodearray
[
i
+
1
],
(
StreamPtr
)
nodearray
[
i
]);
set_upstream
(
nodearray
[
i
],
(
StreamPtr
)
nodearray
[
i
+
1
]);
}
set_downstream
(
nodearray
[
0
],
(
StreamPtr
)
bottom
);
if
(
bottom
)
set_upstream
(
bottom
,
(
StreamPtr
)
nodearray
[
0
]);
Assert
(
xfunc_check_stream
(
output
));
return
output
;
}
/*
** xfunc_stream_compare
** comparison function for xfunc_stream_qsort.
** Compare nodes by group rank. If group ranks are equal, ensure that
** join nodes appear in same order as in plan tree.
*/
static
int
xfunc_stream_compare
(
void
*
arg1
,
void
*
arg2
)
{
Stream
stream1
=
*
(
Stream
*
)
arg1
;
Stream
stream2
=
*
(
Stream
*
)
arg2
;
Cost
rank1
,
rank2
;
rank1
=
get_grouprank
(
stream1
);
rank2
=
get_grouprank
(
stream2
);
if
(
rank1
>
rank2
)
return
1
;
else
if
(
rank1
<
rank2
)
return
-
1
;
else
{
if
(
is_clause
(
stream1
)
&&
is_clause
(
stream2
))
return
0
;
/* doesn't matter what order if both are
* restrictions */
else
if
(
!
is_clause
(
stream1
)
&&
!
is_clause
(
stream2
))
{
if
(
xfunc_num_relids
(
stream1
)
<
xfunc_num_relids
(
stream2
))
return
-
1
;
else
return
1
;
}
else
if
(
is_clause
(
stream1
)
&&
!
is_clause
(
stream2
))
{
if
(
xfunc_num_relids
(
stream1
)
==
xfunc_num_relids
(
stream2
))
/* stream1 is a restriction over stream2 */
return
1
;
else
return
-
1
;
}
else
if
(
!
is_clause
(
stream1
)
&&
is_clause
(
stream2
))
{
/* stream2 is a restriction over stream1: never push down */
return
-
1
;
}
}
}
/* ------------------ DEBUGGING ROUTINES ---------------------------- */
/*
** Make sure all pointers in stream make sense. Make sure no joins are
** out of order.
*/
static
bool
xfunc_check_stream
(
Stream
node
)
{
Stream
temp
;
int
numrelids
,
tmp
;
/* set numrelids higher than max */
if
(
!
is_clause
(
node
))
numrelids
=
xfunc_num_relids
(
node
)
+
1
;
else
if
(
xfunc_get_downjoin
(
node
))
numrelids
=
xfunc_num_relids
((
Stream
)
xfunc_get_downjoin
(
node
))
+
1
;
else
numrelids
=
1
;
for
(
temp
=
node
;
get_downstream
(
temp
);
temp
=
(
Stream
)
get_downstream
(
temp
))
{
if
((
Stream
)
get_upstream
((
Stream
)
get_downstream
(
temp
))
!=
temp
)
{
elog
(
ERROR
,
"bad pointers in stream"
);
return
false
;
}
if
(
!
is_clause
(
temp
))
{
if
((
tmp
=
xfunc_num_relids
(
temp
))
>=
numrelids
)
{
elog
(
ERROR
,
"Joins got reordered!"
);
return
false
;
}
numrelids
=
tmp
;
}
}
return
true
;
}
/*
** xfunc_in_stream
** check if node is in stream
*/
static
bool
xfunc_in_stream
(
Stream
node
,
Stream
stream
)
{
Stream
temp
;
for
(
temp
=
stream
;
temp
;
temp
=
(
Stream
)
get_downstream
(
temp
))
if
(
temp
==
node
)
return
1
;
return
0
;
}
src/backend/optimizer/path/_deadcode/xfunc.c
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* xfunc.c
* Utility routines to handle expensive function optimization.
* Includes xfunc_trypullup(), which attempts early pullup of predicates
* to allow for maximal pruning.
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/xfunc.c,v 1.19 2002/06/20 20:29:30 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#include "postgres.h"
#include "access/heapam.h"
#include "access/htup.h"
#include "catalog/pg_language.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "lib/lispsort.h"
#include "nodes/nodes.h"
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/internal.h"
#include "optimizer/keys.h"
#include "optimizer/pathnode.h"
#include "optimizer/tlist.h"
#include "storage/buf_internals.h"
#include "tcop/dest.h"
#include "utils/syscache.h"
#define ever ; 1 ;
/* local funcs */
static
int
xfunc_card_unreferenced
(
Query
*
queryInfo
,
Expr
*
clause
,
Relids
referenced
);
*/
/*
** xfunc_trypullup
** Preliminary pullup of predicates, to allow for maximal pruning.
** Given a relation, check each of its paths and see if you can
** pullup clauses from its inner and outer.
*/
void
xfunc_trypullup
(
RelOptInfo
rel
)
{
LispValue
y
;
/* list ptr */
RestrictInfo
maxcinfo
;
/* The RestrictInfo to pull up, as
* calculated by xfunc_shouldpull() */
JoinPath
curpath
;
/* current path in list */
int
progress
;
/* has progress been made this time
* through? */
int
clausetype
;
do
{
progress
=
false
;
/* no progress yet in this iteration */
foreach
(
y
,
get_pathlist
(
rel
))
{
curpath
=
(
JoinPath
)
lfirst
(
y
);
/*
* * for each operand, attempt to pullup predicates until
* first * failure.
*/
for
(
ever
)
{
/* No, the following should NOT be '==' !! */
if
(
clausetype
=
xfunc_shouldpull
((
Path
)
get_innerjoinpath
(
curpath
),
curpath
,
INNER
,
&
maxcinfo
))
{
xfunc_pullup
((
Path
)
get_innerjoinpath
(
curpath
),
curpath
,
maxcinfo
,
INNER
,
clausetype
);
progress
=
true
;
}
else
break
;
}
for
(
ever
)
{
/* No, the following should NOT be '==' !! */
if
(
clausetype
=
xfunc_shouldpull
((
Path
)
get_outerjoinpath
(
curpath
),
curpath
,
OUTER
,
&
maxcinfo
))
{
xfunc_pullup
((
Path
)
get_outerjoinpath
(
curpath
),
curpath
,
maxcinfo
,
OUTER
,
clausetype
);
progress
=
true
;
}
else
break
;
}
/*
* * make sure the unpruneable flag bubbles up, i.e. * if
* anywhere below us in the path pruneable is false, * then
* pruneable should be false here
*/
if
(
get_pruneable
(
get_parent
(
curpath
))
&&
(
!
get_pruneable
(
get_parent
((
Path
)
get_innerjoinpath
(
curpath
)))
||
!
get_pruneable
(
get_parent
((
Path
)
get_outerjoinpath
(
curpath
)))))
{
set_pruneable
(
get_parent
(
curpath
),
false
);
progress
=
true
;
}
}
}
while
(
progress
);
}
/*
** xfunc_shouldpull
** find clause with highest rank, and decide whether to pull it up
** from child to parent. Currently we only pullup secondary join clauses
** that are in the pathrestrictinfo. Secondary hash and sort clauses are
** left where they are.
** If we find an expensive function but decide *not* to pull it up,
** we'd better set the unpruneable flag. -- JMH, 11/11/92
**
** Returns: 0 if nothing left to pullup
** XFUNC_LOCPRD if a local predicate is to be pulled up
** XFUNC_JOINPRD if a secondary join predicate is to be pulled up
*/
int
xfunc_shouldpull
(
Query
*
queryInfo
,
Path
childpath
,
JoinPath
parentpath
,
int
whichchild
,
RestrictInfo
*
maxcinfopt
)
/* Out: pointer to clause
* to pullup */
{
LispValue
clauselist
,
tmplist
;
/* lists of clauses */
RestrictInfo
maxcinfo
;
/* clause to pullup */
LispValue
primjoinclause
/* primary join clause */
=
xfunc_primary_join
(
parentpath
);
Cost
tmprank
,
maxrank
=
(
-
1
*
MAXFLOAT
);
/* ranks of clauses */
Cost
joinselec
=
0
;
/* selectivity of the join predicate */
Cost
joincost
=
0
;
/* join cost + primjoinclause cost */
int
retval
=
XFUNC_LOCPRD
;
clauselist
=
get_loc_restrictinfo
(
childpath
);
if
(
clauselist
!=
LispNil
)
{
/* find local predicate with maximum rank */
for
(
tmplist
=
clauselist
,
maxcinfo
=
(
RestrictInfo
)
lfirst
(
tmplist
),
maxrank
=
xfunc_rank
(
get_clause
(
maxcinfo
));
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
if
((
tmprank
=
xfunc_rank
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
))))
>
maxrank
)
{
maxcinfo
=
(
RestrictInfo
)
lfirst
(
tmplist
);
maxrank
=
tmprank
;
}
}
}
/*
* * If child is a join path, and there are multiple join clauses, *
* see if any join clause has even higher rank than the highest *
* local predicate
*/
if
(
is_join
(
childpath
)
&&
xfunc_num_join_clauses
((
JoinPath
)
childpath
)
>
1
)
for
(
tmplist
=
get_pathrestrictinfo
((
JoinPath
)
childpath
);
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
if
(
tmplist
!=
LispNil
&&
(
tmprank
=
xfunc_rank
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
))))
>
maxrank
)
{
maxcinfo
=
(
RestrictInfo
)
lfirst
(
tmplist
);
maxrank
=
tmprank
;
retval
=
XFUNC_JOINPRD
;
}
}
if
(
maxrank
==
(
-
1
*
MAXFLOAT
))
/* no expensive clauses */
return
0
;
/*
* * Pullup over join if clause is higher rank than join, or if * join
* is nested loop and current path is inner child (note that *
* restrictions on the inner of a nested loop don't buy you anything
* -- * you still have to scan the entire inner relation each time). *
* Note that the cost of a secondary join clause is only what's *
* calculated by xfunc_expense(), since the actual joining * (i.e. the
* usual path_cost) is paid for by the primary join clause.
*/
if
(
primjoinclause
!=
LispNil
)
{
joinselec
=
compute_clause_selec
(
queryInfo
,
primjoinclause
,
LispNil
);
joincost
=
xfunc_join_expense
(
parentpath
,
whichchild
);
if
(
XfuncMode
==
XFUNC_PULLALL
||
(
XfuncMode
!=
XFUNC_WAIT
&&
((
joincost
!=
0
&&
(
maxrank
=
xfunc_rank
(
get_clause
(
maxcinfo
)))
>
((
joinselec
-
1
.
0
)
/
joincost
))
||
(
joincost
==
0
&&
joinselec
<
1
)
||
(
!
is_join
(
childpath
)
&&
(
whichchild
==
INNER
)
&&
IsA
(
parentpath
,
NestPath
)
&&!
IsA
(
parentpath
,
HashPath
)
&&!
IsA
(
parentpath
,
MergePath
)))))
{
*
maxcinfopt
=
maxcinfo
;
return
retval
;
}
else
if
(
maxrank
!=
-
(
MAXFLOAT
))
{
/*
* * we've left an expensive restriction below a join. Since *
* we may pullup this restriction in predmig.c, we'd best *
* set the RelOptInfo of this join to be unpruneable
*/
set_pruneable
(
get_parent
(
parentpath
),
false
);
/* and fall through */
}
}
return
0
;
}
/*
** xfunc_pullup
** move clause from child pathnode to parent pathnode. This operation
** makes the child pathnode produce a larger relation than it used to.
** This means that we must construct a new RelOptInfo just for the childpath,
** although this RelOptInfo will not be added to the list of Rels to be joined up
** in the query; it's merely a parent for the new childpath.
** We also have to fix up the path costs of the child and parent.
**
** Now returns a pointer to the new pulled-up RestrictInfo. -- JMH, 11/18/92
*/
RestrictInfo
xfunc_pullup
(
Query
*
queryInfo
,
Path
childpath
,
JoinPath
parentpath
,
RestrictInfo
cinfo
,
/* clause to pull up */
int
whichchild
,
/* whether child is INNER or OUTER of join */
int
clausetype
)
/* whether clause to pull is join or local */
{
Path
newkid
;
RelOptInfo
newrel
;
Cost
pulled_selec
;
Cost
cost
;
RestrictInfo
newinfo
;
/* remove clause from childpath */
newkid
=
(
Path
)
copyObject
((
Node
)
childpath
);
if
(
clausetype
==
XFUNC_LOCPRD
)
{
set_locrestrictinfo
(
newkid
,
xfunc_LispRemove
((
LispValue
)
cinfo
,
(
List
)
get_loc_restrictinfo
(
newkid
)));
}
else
{
set_pathrestrictinfo
((
JoinPath
)
newkid
,
xfunc_LispRemove
((
LispValue
)
cinfo
,
(
List
)
get_pathrestrictinfo
((
JoinPath
)
newkid
)));
}
/*
* * give the new child path its own RelOptInfo node that reflects the *
* lack of the pulled-up predicate
*/
pulled_selec
=
compute_clause_selec
(
queryInfo
,
get_clause
(
cinfo
),
LispNil
);
xfunc_copyrel
(
get_parent
(
newkid
),
&
newrel
);
set_parent
(
newkid
,
newrel
);
set_pathlist
(
newrel
,
makeList1
(
newkid
));
set_unorderedpath
(
newrel
,
(
PathPtr
)
newkid
);
set_cheapestpath
(
newrel
,
(
PathPtr
)
newkid
);
set_size
(
newrel
,
(
Count
)
((
Cost
)
get_size
(
get_parent
(
childpath
))
/
pulled_selec
));
/*
* * fix up path cost of newkid. To do this we subtract away all the *
* xfunc_costs of childpath, then recompute the xfunc_costs of newkid
*/
cost
=
get_path_cost
(
newkid
)
-
xfunc_get_path_cost
(
childpath
);
Assert
(
cost
>=
0
);
set_path_cost
(
newkid
,
cost
);
cost
=
get_path_cost
(
newkid
)
+
xfunc_get_path_cost
(
newkid
);
set_path_cost
(
newkid
,
cost
);
/*
* * We copy the cinfo, since it may appear in other plans, and we're
* going * to munge it. -- JMH, 7/22/92
*/
newinfo
=
(
RestrictInfo
)
copyObject
((
Node
)
cinfo
);
/*
* * Fix all vars in the clause * to point to the right varno and
* varattno in parentpath
*/
xfunc_fixvars
(
get_clause
(
newinfo
),
newrel
,
whichchild
);
/* add clause to parentpath, and fix up its cost. */
set_locrestrictinfo
(
parentpath
,
lispCons
((
LispValue
)
newinfo
,
(
LispValue
)
get_loc_restrictinfo
(
parentpath
)));
/* put new childpath into the path tree */
if
(
whichchild
==
INNER
)
set_innerjoinpath
(
parentpath
,
(
pathPtr
)
newkid
);
else
set_outerjoinpath
(
parentpath
,
(
pathPtr
)
newkid
);
/*
* * recompute parentpath cost from scratch -- the cost * of the join
* method has changed
*/
cost
=
xfunc_total_path_cost
(
parentpath
);
set_path_cost
(
parentpath
,
cost
);
return
newinfo
;
}
/*
** calculate (selectivity-1)/cost.
*/
Cost
xfunc_rank
(
Query
*
queryInfo
,
LispValue
clause
)
{
Cost
selec
=
compute_clause_selec
(
queryInfo
,
clause
,
LispNil
);
Cost
cost
=
xfunc_expense
(
queryInfo
,
clause
);
if
(
cost
==
0
)
if
(
selec
>
1
)
return
MAXFLOAT
;
else
return
-
(
MAXFLOAT
);
return
(
selec
-
1
)
/
cost
;
}
/*
** Find the "global" expense of a clause; i.e. the local expense divided
** by the cardinalities of all the base relations of the query that are *not*
** referenced in the clause.
*/
Cost
xfunc_expense
(
Query
*
queryInfo
,
clause
)
LispValue
clause
;
{
Cost
cost
=
xfunc_local_expense
(
clause
);
if
(
cost
)
{
Count
card
=
xfunc_card_unreferenced
(
queryInfo
,
clause
,
LispNil
);
if
(
card
)
cost
/=
card
;
}
return
cost
;
}
/*
** xfunc_join_expense
** Find global expense of a join clause
*/
Cost
xfunc_join_expense
(
Query
*
queryInfo
,
JoinPath
path
,
int
whichchild
)
{
LispValue
primjoinclause
=
xfunc_primary_join
(
path
);
/*
* * the second argument to xfunc_card_unreferenced reflects all the *
* relations involved in the join clause, i.e. all the relids in the
* RelOptInfo * of the join clause
*/
Count
card
=
0
;
Cost
cost
=
xfunc_expense_per_tuple
(
path
,
whichchild
);
card
=
xfunc_card_unreferenced
(
queryInfo
,
primjoinclause
,
get_relids
(
get_parent
(
path
)));
if
(
primjoinclause
)
cost
+=
xfunc_local_expense
(
primjoinclause
);
if
(
card
)
cost
/=
card
;
return
cost
;
}
/*
** Recursively find the per-tuple expense of a clause. See
** xfunc_func_expense for more discussion.
*/
Cost
xfunc_local_expense
(
LispValue
clause
)
{
Cost
cost
=
0
;
/* running expense */
LispValue
tmpclause
;
/* First handle the base case */
if
(
IsA
(
clause
,
Const
)
||
IsA
(
clause
,
Var
)
||
IsA
(
clause
,
Param
))
return
0
;
/* now other stuff */
else
if
(
IsA
(
clause
,
Iter
))
/* Too low. Should multiply by the expected number of iterations. */
return
xfunc_local_expense
(
get_iterexpr
((
Iter
)
clause
));
else
if
(
IsA
(
clause
,
ArrayRef
))
return
xfunc_local_expense
(
get_refexpr
((
ArrayRef
)
clause
));
else
if
(
fast_is_clause
(
clause
))
return
(
xfunc_func_expense
((
LispValue
)
get_op
(
clause
),
(
LispValue
)
get_opargs
(
clause
)));
else
if
(
fast_is_funcclause
(
clause
))
return
(
xfunc_func_expense
((
LispValue
)
get_function
(
clause
),
(
LispValue
)
get_funcargs
(
clause
)));
else
if
(
fast_not_clause
(
clause
))
return
xfunc_local_expense
(
lsecond
(
clause
));
else
if
(
fast_or_clause
(
clause
)
||
fast_and_clause
(
clause
))
{
/* find cost of evaluating each disjunct */
for
(
tmpclause
=
lnext
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
cost
+=
xfunc_local_expense
(
lfirst
(
tmpclause
));
return
cost
;
}
else
{
elog
(
ERROR
,
"Clause node of undetermined type"
);
return
-
1
;
}
}
/*
** xfunc_func_expense
** given a Func or Oper and its args, find its expense.
** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric
** than the one here. We can ignore the expected number of tuples for
** our calculations; we just need the per-tuple expense. But he also
** proposes components to take into account the costs of accessing disk and
** archive. We didn't adopt that scheme here; eventually the vacuum
** cleaner should be able to tell us what percentage of bytes to find on
** which storage level, and that should be multiplied in appropriately
** in the cost function below. Right now we don't model the cost of
** accessing secondary or tertiary storage, since we don't have sufficient
** stats to do it right.
*/
Cost
xfunc_func_expense
(
LispValue
node
,
LispValue
args
)
{
HeapTuple
tupl
;
/* the pg_proc tuple for each function */
Form_pg_proc
proc
;
/* a data structure to hold the pg_proc
* tuple */
int
width
=
0
;
/* byte width of the field referenced by
* each clause */
RegProcedure
funcid
;
/* ID of function associate with node */
Cost
cost
=
0
;
/* running expense */
LispValue
tmpclause
;
LispValue
operand
;
/* one operand of an operator */
if
(
IsA
(
node
,
Oper
))
{
/* don't trust the opid in the Oper node. Use the opno. */
if
(
!
(
funcid
=
get_opcode
(
get_opno
((
Oper
)
node
))))
elog
(
ERROR
,
"Oper's function is undefined"
);
}
else
funcid
=
get_funcid
((
Func
)
node
);
/* look up tuple in cache */
tupl
=
SearchSysCacheTuple
(
PROCOID
,
ObjectIdGetDatum
(
funcid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tupl
))
elog
(
ERROR
,
"Cache lookup failed for procedure %u"
,
funcid
);
proc
=
(
Form_pg_proc
)
GETSTRUCT
(
tupl
);
/*
* * if it's a Postquel function, its cost is stored in the *
* associated plan.
*/
if
(
proc
->
prolang
==
SQLlanguageId
)
{
LispValue
tmpplan
;
List
planlist
;
if
(
IsA
(
node
,
Oper
)
||
get_func_planlist
((
Func
)
node
)
==
LispNil
)
{
Oid
*
argOidVect
;
/* vector of argtypes */
char
*
pq_src
;
/* text of PQ function */
int
nargs
;
/* num args to PQ function */
QueryTreeList
*
queryTree_list
;
/* dummy variable */
/*
* * plan the function, storing it in the Func node for later *
* use by the executor.
*/
pq_src
=
(
char
*
)
textout
(
&
(
proc
->
prosrc
));
nargs
=
proc
->
pronargs
;
if
(
nargs
>
0
)
argOidVect
=
proc
->
proargtypes
;
planlist
=
(
List
)
pg_parse_and_plan
(
pq_src
,
argOidVect
,
nargs
,
&
parseTree_list
,
None
,
FALSE
);
if
(
IsA
(
node
,
Func
))
set_func_planlist
((
Func
)
node
,
planlist
);
}
else
{
/* plan has been cached inside the Func
* node already */
planlist
=
get_func_planlist
((
Func
)
node
);
}
/*
* * Return the sum of the costs of the plans (the PQ function *
* may have many queries in its body).
*/
foreach
(
tmpplan
,
planlist
)
cost
+=
get_cost
((
Plan
)
lfirst
(
tmpplan
));
return
cost
;
}
else
{
/* it's a C function */
/*
* * find the cost of evaluating the function's arguments * and
* the width of the operands
*/
for
(
tmpclause
=
args
;
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
{
if
((
operand
=
lfirst
(
tmpclause
))
!=
LispNil
)
{
cost
+=
xfunc_local_expense
(
operand
);
width
+=
xfunc_width
(
operand
);
}
}
/*
* * when stats become available, add in cost of accessing
* secondary * and tertiary storage here.
*/
return
(
cost
+
(
Cost
)
proc
->
propercall_cpu
+
(
Cost
)
proc
->
properbyte_cpu
*
(
Cost
)
proc
->
probyte_pct
/
100
.
00
*
(
Cost
)
width
/*
* Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width
* Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 *
* width Pct_of_obj_on_arch
*/
);
}
}
/*
** xfunc_width
** recursively find the width of a expression
*/
int
xfunc_width
(
LispValue
clause
)
{
Relation
rd
;
/* Relation Descriptor */
HeapTuple
tupl
;
/* structure to hold a cached tuple */
Form_pg_type
type
;
/* structure to hold a type tuple */
int
retval
=
0
;
if
(
IsA
(
clause
,
Const
))
{
/* base case: width is the width of this constant */
retval
=
get_constlen
((
Const
)
clause
);
goto
exit
;
}
else
if
(
IsA
(
clause
,
ArrayRef
))
{
/* base case: width is width of the refelem within the array */
retval
=
get_refelemlength
((
ArrayRef
)
clause
);
goto
exit
;
}
else
if
(
IsA
(
clause
,
Var
))
{
/* base case: width is width of this attribute */
tupl
=
SearchSysCacheTuple
(
TYPEOID
,
ObjectIdGetDatum
(
get_vartype
((
Var
)
clause
)),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tupl
))
elog
(
ERROR
,
"Cache lookup failed for type %u"
,
get_vartype
((
Var
)
clause
));
type
=
(
Form_pg_type
)
GETSTRUCT
(
tupl
);
if
(
get_varattno
((
Var
)
clause
)
==
0
)
{
/* clause is a tuple. Get its width */
rd
=
heap_open
(
type
->
typrelid
);
retval
=
xfunc_tuple_width
(
rd
);
heap_close
(
rd
);
}
else
{
/* attribute is a base type */
retval
=
type
->
typlen
;
}
goto
exit
;
}
else
if
(
IsA
(
clause
,
Param
))
{
if
(
typeidTypeRelids
(
get_paramtype
((
Param
)
clause
)))
{
/* Param node returns a tuple. Find its width */
rd
=
heap_open
(
typeidTypeRelids
(
get_paramtype
((
Param
)
clause
)));
retval
=
xfunc_tuple_width
(
rd
);
heap_close
(
rd
);
}
else
if
(
get_param_tlist
((
Param
)
clause
)
!=
LispNil
)
{
/* Param node projects a complex type */
Assert
(
length
(
get_param_tlist
((
Param
)
clause
))
==
1
);
/* sanity */
retval
=
xfunc_width
((
LispValue
)
get_expr
(
lfirst
(
get_param_tlist
((
Param
)
clause
))));
}
else
{
/* Param node returns a base type */
retval
=
typeLen
(
typeidType
(
get_paramtype
((
Param
)
clause
)));
}
goto
exit
;
}
else
if
(
IsA
(
clause
,
Iter
))
{
/*
* * An Iter returns a setof things, so return the width of a
* single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET
* FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! *
* This whole Iter business is bogus, anyway.
*/
retval
=
xfunc_width
(
get_iterexpr
((
Iter
)
clause
));
goto
exit
;
}
else
if
(
fast_is_clause
(
clause
))
{
/*
* * get function associated with this Oper, and treat this as * a
* Func
*/
tupl
=
SearchSysCacheTuple
(
OPEROID
,
ObjectIdGetDatum
(
get_opno
((
Oper
)
get_op
(
clause
))),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tupl
))
elog
(
ERROR
,
"Cache lookup failed for procedure %u"
,
get_opno
((
Oper
)
get_op
(
clause
)));
return
(
xfunc_func_width
((
RegProcedure
)
(((
Form_pg_operator
)
(
GETSTRUCT
(
tupl
)))
->
oprcode
),
(
LispValue
)
get_opargs
(
clause
)));
}
else
if
(
fast_is_funcclause
(
clause
))
{
Func
func
=
(
Func
)
get_function
(
clause
);
if
(
get_func_tlist
(
func
)
!=
LispNil
)
{
/*
* this function has a projection on it. Get the length of
* the projected attribute
*/
Assert
(
length
(
get_func_tlist
(
func
))
==
1
);
/* sanity */
retval
=
xfunc_width
((
LispValue
)
get_expr
(
lfirst
(
get_func_tlist
(
func
))));
goto
exit
;
}
else
{
return
(
xfunc_func_width
((
RegProcedure
)
get_funcid
(
func
),
(
LispValue
)
get_funcargs
(
clause
)));
}
}
else
{
elog
(
ERROR
,
"Clause node of undetermined type"
);
return
-
1
;
}
exit:
if
(
retval
==
-
1
)
retval
=
VARLEN_DEFAULT
;
return
retval
;
}
/*
** xfunc_card_unreferenced:
** find all relations not referenced in clause, and multiply their
** cardinalities. Ignore relation of cardinality 0.
** User may pass in referenced list, if they know it (useful
** for joins).
*/
static
Count
xfunc_card_unreferenced
(
Query
*
queryInfo
,
LispValue
clause
,
Relids
referenced
)
{
Relids
unreferenced
,
allrelids
=
LispNil
;
LispValue
temp
;
/* find all relids of base relations referenced in query */
foreach
(
temp
,
queryInfo
->
base_rel_list
)
{
Assert
(
lnext
(
get_relids
((
RelOptInfo
)
lfirst
(
temp
)))
==
LispNil
);
allrelids
=
lappend
(
allrelids
,
lfirst
(
get_relids
((
RelOptInfo
)
lfirst
(
temp
))));
}
/* find all relids referenced in query but not in clause */
if
(
!
referenced
)
referenced
=
xfunc_find_references
(
clause
);
unreferenced
=
set_difference
(
allrelids
,
referenced
);
return
xfunc_card_product
(
unreferenced
);
}
/*
** xfunc_card_product
** multiple together cardinalities of a list relations.
*/
Count
xfunc_card_product
(
Query
*
queryInfo
,
Relids
relids
)
{
LispValue
cinfonode
;
LispValue
temp
;
RelOptInfo
currel
;
Cost
tuples
;
Count
retval
=
0
;
foreach
(
temp
,
relids
)
{
currel
=
get_rel
(
lfirst
(
temp
));
tuples
=
get_tuples
(
currel
);
if
(
tuples
)
{
/* not of cardinality 0 */
/* factor in the selectivity of all zero-cost clauses */
foreach
(
cinfonode
,
get_restrictinfo
(
currel
))
{
if
(
!
xfunc_expense
(
queryInfo
,
get_clause
((
RestrictInfo
)
lfirst
(
cinfonode
))))
tuples
*=
compute_clause_selec
(
queryInfo
,
get_clause
((
RestrictInfo
)
lfirst
(
cinfonode
)),
LispNil
);
}
if
(
retval
==
0
)
retval
=
tuples
;
else
retval
*=
tuples
;
}
}
if
(
retval
==
0
)
retval
=
1
;
/* saves caller from dividing by zero */
return
retval
;
}
/*
** xfunc_find_references:
** Traverse a clause and find all relids referenced in the clause.
*/
List
xfunc_find_references
(
LispValue
clause
)
{
List
retval
=
(
List
)
LispNil
;
LispValue
tmpclause
;
/* Base cases */
if
(
IsA
(
clause
,
Var
))
return
lispCons
(
lfirst
(
get_varid
((
Var
)
clause
)),
LispNil
);
else
if
(
IsA
(
clause
,
Const
)
||
IsA
(
clause
,
Param
))
return
(
List
)
LispNil
;
/* recursion */
else
if
(
IsA
(
clause
,
Iter
))
/*
* Too low. Should multiply by the expected number of iterations.
* maybe
*/
return
xfunc_find_references
(
get_iterexpr
((
Iter
)
clause
));
else
if
(
IsA
(
clause
,
ArrayRef
))
return
xfunc_find_references
(
get_refexpr
((
ArrayRef
)
clause
));
else
if
(
fast_is_clause
(
clause
))
{
/* string together result of all operands of Oper */
for
(
tmpclause
=
(
LispValue
)
get_opargs
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
retval
=
nconc
(
retval
,
xfunc_find_references
(
lfirst
(
tmpclause
)));
return
retval
;
}
else
if
(
fast_is_funcclause
(
clause
))
{
/* string together result of all args of Func */
for
(
tmpclause
=
(
LispValue
)
get_funcargs
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
retval
=
nconc
(
retval
,
xfunc_find_references
(
lfirst
(
tmpclause
)));
return
retval
;
}
else
if
(
fast_not_clause
(
clause
))
return
xfunc_find_references
(
lsecond
(
clause
));
else
if
(
fast_or_clause
(
clause
)
||
fast_and_clause
(
clause
))
{
/* string together result of all operands of OR */
for
(
tmpclause
=
lnext
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
retval
=
nconc
(
retval
,
xfunc_find_references
(
lfirst
(
tmpclause
)));
return
retval
;
}
else
{
elog
(
ERROR
,
"Clause node of undetermined type"
);
return
(
List
)
LispNil
;
}
}
/*
** xfunc_primary_join:
** Find the primary join clause: for Hash and Merge Joins, this is the
** min rank Hash or Merge clause, while for Nested Loop it's the
** min rank pathclause
*/
LispValue
xfunc_primary_join
(
JoinPath
pathnode
)
{
LispValue
joinclauselist
=
get_pathrestrictinfo
(
pathnode
);
RestrictInfo
mincinfo
;
LispValue
tmplist
;
LispValue
minclause
=
LispNil
;
Cost
minrank
,
tmprank
;
if
(
IsA
(
pathnode
,
MergePath
))
{
for
(
tmplist
=
get_path_mergeclauses
((
MergePath
)
pathnode
),
minclause
=
lfirst
(
tmplist
),
minrank
=
xfunc_rank
(
minclause
);
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
if
((
tmprank
=
xfunc_rank
(
lfirst
(
tmplist
)))
<
minrank
)
{
minrank
=
tmprank
;
minclause
=
lfirst
(
tmplist
);
}
return
minclause
;
}
else
if
(
IsA
(
pathnode
,
HashPath
))
{
for
(
tmplist
=
get_path_hashclauses
((
HashPath
)
pathnode
),
minclause
=
lfirst
(
tmplist
),
minrank
=
xfunc_rank
(
minclause
);
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
if
((
tmprank
=
xfunc_rank
(
lfirst
(
tmplist
)))
<
minrank
)
{
minrank
=
tmprank
;
minclause
=
lfirst
(
tmplist
);
}
return
minclause
;
}
/* if we drop through, it's nested loop join */
if
(
joinclauselist
==
LispNil
)
return
LispNil
;
for
(
tmplist
=
joinclauselist
,
mincinfo
=
(
RestrictInfo
)
lfirst
(
joinclauselist
),
minrank
=
xfunc_rank
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
)));
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
if
((
tmprank
=
xfunc_rank
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
))))
<
minrank
)
{
minrank
=
tmprank
;
mincinfo
=
(
RestrictInfo
)
lfirst
(
tmplist
);
}
return
(
LispValue
)
get_clause
(
mincinfo
);
}
/*
** xfunc_get_path_cost
** get the expensive function costs of the path
*/
Cost
xfunc_get_path_cost
(
Query
*
queryInfo
,
Path
pathnode
)
{
Cost
cost
=
0
;
LispValue
tmplist
;
Cost
selec
=
1
.
0
;
/*
* * first add in the expensive local function costs. * We ensure that
* the clauses are sorted by rank, so that we * know (via
* selectivities) the number of tuples that will be checked * by each
* function. If we're not doing any optimization of expensive *
* functions, we don't sort.
*/
if
(
XfuncMode
!=
XFUNC_OFF
)
set_locrestrictinfo
(
pathnode
,
lisp_qsort
(
get_loc_restrictinfo
(
pathnode
),
xfunc_cinfo_compare
));
for
(
tmplist
=
get_loc_restrictinfo
(
pathnode
),
selec
=
1
.
0
;
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
cost
+=
(
Cost
)
(
xfunc_local_expense
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
)))
*
(
Cost
)
get_tuples
(
get_parent
(
pathnode
))
*
selec
);
selec
*=
compute_clause_selec
(
queryInfo
,
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
)),
LispNil
);
}
/*
* * Now add in any node-specific expensive function costs. * Again,
* we must ensure that the clauses are sorted by rank.
*/
if
(
IsA
(
pathnode
,
JoinPath
))
{
if
(
XfuncMode
!=
XFUNC_OFF
)
set_pathrestrictinfo
((
JoinPath
)
pathnode
,
lisp_qsort
(
get_pathrestrictinfo
((
JoinPath
)
pathnode
),
xfunc_cinfo_compare
));
for
(
tmplist
=
get_pathrestrictinfo
((
JoinPath
)
pathnode
),
selec
=
1
.
0
;
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
cost
+=
(
Cost
)
(
xfunc_local_expense
(
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
)))
*
(
Cost
)
get_tuples
(
get_parent
(
pathnode
))
*
selec
);
selec
*=
compute_clause_selec
(
queryInfo
,
get_clause
((
RestrictInfo
)
lfirst
(
tmplist
)),
LispNil
);
}
}
if
(
IsA
(
pathnode
,
HashPath
))
{
if
(
XfuncMode
!=
XFUNC_OFF
)
set_path_hashclauses
((
HashPath
)
pathnode
,
lisp_qsort
(
get_path_hashclauses
((
HashPath
)
pathnode
),
xfunc_clause_compare
));
for
(
tmplist
=
get_path_hashclauses
((
HashPath
)
pathnode
),
selec
=
1
.
0
;
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
cost
+=
(
Cost
)
(
xfunc_local_expense
(
lfirst
(
tmplist
))
*
(
Cost
)
get_tuples
(
get_parent
(
pathnode
))
*
selec
);
selec
*=
compute_clause_selec
(
queryInfo
,
lfirst
(
tmplist
),
LispNil
);
}
}
if
(
IsA
(
pathnode
,
MergePath
))
{
if
(
XfuncMode
!=
XFUNC_OFF
)
set_path_mergeclauses
((
MergePath
)
pathnode
,
lisp_qsort
(
get_path_mergeclauses
((
MergePath
)
pathnode
),
xfunc_clause_compare
));
for
(
tmplist
=
get_path_mergeclauses
((
MergePath
)
pathnode
),
selec
=
1
.
0
;
tmplist
!=
LispNil
;
tmplist
=
lnext
(
tmplist
))
{
cost
+=
(
Cost
)
(
xfunc_local_expense
(
lfirst
(
tmplist
))
*
(
Cost
)
get_tuples
(
get_parent
(
pathnode
))
*
selec
);
selec
*=
compute_clause_selec
(
queryInfo
,
lfirst
(
tmplist
),
LispNil
);
}
}
Assert
(
cost
>=
0
);
return
cost
;
}
/*
** Recalculate the cost of a path node. This includes the basic cost of the
** node, as well as the cost of its expensive functions.
** We need to do this to the parent after pulling a clause from a child into a
** parent. Thus we should only be calling this function on JoinPaths.
*/
Cost
xfunc_total_path_cost
(
JoinPath
pathnode
)
{
Cost
cost
=
xfunc_get_path_cost
((
Path
)
pathnode
);
Assert
(
IsA
(
pathnode
,
JoinPath
));
if
(
IsA
(
pathnode
,
MergePath
))
{
MergePath
mrgnode
=
(
MergePath
)
pathnode
;
cost
+=
cost_mergejoin
(
get_path_cost
((
Path
)
get_outerjoinpath
(
mrgnode
)),
get_path_cost
((
Path
)
get_innerjoinpath
(
mrgnode
)),
get_outersortkeys
(
mrgnode
),
get_innersortkeys
(
mrgnode
),
get_tuples
(
get_parent
((
Path
)
get_outerjoinpath
(
mrgnode
))),
get_tuples
(
get_parent
((
Path
)
get_innerjoinpath
(
mrgnode
))),
get_width
(
get_parent
((
Path
)
get_outerjoinpath
(
mrgnode
))),
get_width
(
get_parent
((
Path
)
get_innerjoinpath
(
mrgnode
))));
Assert
(
cost
>=
0
);
return
cost
;
}
else
if
(
IsA
(
pathnode
,
HashPath
))
{
HashPath
hashnode
=
(
HashPath
)
pathnode
;
cost
+=
cost_hashjoin
(
get_path_cost
((
Path
)
get_outerjoinpath
(
hashnode
)),
get_path_cost
((
Path
)
get_innerjoinpath
(
hashnode
)),
get_outerhashkeys
(
hashnode
),
get_innerhashkeys
(
hashnode
),
get_tuples
(
get_parent
((
Path
)
get_outerjoinpath
(
hashnode
))),
get_tuples
(
get_parent
((
Path
)
get_innerjoinpath
(
hashnode
))),
get_width
(
get_parent
((
Path
)
get_outerjoinpath
(
hashnode
))),
get_width
(
get_parent
((
Path
)
get_innerjoinpath
(
hashnode
))));
Assert
(
cost
>=
0
);
return
cost
;
}
else
/* Nested Loop Join */
{
cost
+=
cost_nestloop
(
get_path_cost
((
Path
)
get_outerjoinpath
(
pathnode
)),
get_path_cost
((
Path
)
get_innerjoinpath
(
pathnode
)),
get_tuples
(
get_parent
((
Path
)
get_outerjoinpath
(
pathnode
))),
get_tuples
(
get_parent
((
Path
)
get_innerjoinpath
(
pathnode
))),
get_pages
(
get_parent
((
Path
)
get_outerjoinpath
(
pathnode
))),
IsA
(
get_innerjoinpath
(
pathnode
),
IndexPath
));
Assert
(
cost
>=
0
);
return
cost
;
}
}
/*
** xfunc_expense_per_tuple
** return the expense of the join *per-tuple* of the input relation.
** The cost model here is that a join costs
** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n
**
** We treat the l and m terms by considering them to be like restrictions
** constrained to be right under the join. Thus the cost per inner and
** cost per outer of the join is different, reflecting these virtual nodes.
**
** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple
** of inner is k + m/referenced(outer).
** The constants k, l, m and n depend on the join method. Measures here are
** based on the costs in costsize.c, with fudging for HashJoin and Sorts to
** make it fit our model (the 'q' in HashJoin results in a
** card(outer)/card(inner) term, and sorting results in a log term.
*/
Cost
xfunc_expense_per_tuple
(
JoinPath
joinnode
,
int
whichchild
)
{
RelOptInfo
outerrel
=
get_parent
((
Path
)
get_outerjoinpath
(
joinnode
));
RelOptInfo
innerrel
=
get_parent
((
Path
)
get_innerjoinpath
(
joinnode
));
Count
outerwidth
=
get_width
(
outerrel
);
Count
outers_per_page
=
ceil
(
BLCKSZ
/
(
outerwidth
+
MinTupleSize
));
if
(
IsA
(
joinnode
,
HashPath
))
{
if
(
whichchild
==
INNER
)
return
(
1
+
cpu_page_weight
)
*
outers_per_page
/
NBuffers
;
else
return
(((
1
+
cpu_page_weight
)
*
outers_per_page
/
NBuffers
)
+
cpu_page_weight
/
xfunc_card_product
(
get_relids
(
innerrel
)));
}
else
if
(
IsA
(
joinnode
,
MergePath
))
{
/* assumes sort exists, and costs one (I/O + CPU) per tuple */
if
(
whichchild
==
INNER
)
return
((
2
*
cpu_page_weight
+
1
)
/
xfunc_card_product
(
get_relids
(
outerrel
)));
else
return
((
2
*
cpu_page_weight
+
1
)
/
xfunc_card_product
(
get_relids
(
innerrel
)));
}
else
/* nestloop */
{
Assert
(
IsA
(
joinnode
,
JoinPath
));
return
cpu_page_weight
;
}
}
/*
** xfunc_fixvars
** After pulling up a clause, we must walk its expression tree, fixing Var
** nodes to point to the correct varno (either INNER or OUTER, depending
** on which child the clause was pulled from), and the right varattno in the
** target list of the child's former relation. If the target list of the
** child RelOptInfo does not contain the attribute we need, we add it.
*/
void
xfunc_fixvars
(
LispValue
clause
,
/* clause being pulled up */
RelOptInfo
rel
,
/* rel it's being pulled from */
int
varno
)
/* whether rel is INNER or OUTER of join */
{
LispValue
tmpclause
;
/* temporary variable */
TargetEntry
*
tle
;
/* tlist member corresponding to var */
if
(
IsA
(
clause
,
Const
)
||
IsA
(
clause
,
Param
))
return
;
else
if
(
IsA
(
clause
,
Var
))
{
/* here's the meat */
tle
=
tlistentry_member
((
Var
)
clause
,
get_targetlist
(
rel
));
if
(
tle
==
LispNil
)
{
/*
* * The attribute we need is not in the target list, * so we
* have to add it. *
*
*/
add_var_to_tlist
(
rel
,
(
Var
)
clause
);
tle
=
tlistentry_member
((
Var
)
clause
,
get_targetlist
(
rel
));
}
set_varno
(((
Var
)
clause
),
varno
);
set_varattno
(((
Var
)
clause
),
get_resno
(
get_resdom
(
get_entry
(
tle
))));
}
else
if
(
IsA
(
clause
,
Iter
))
xfunc_fixvars
(
get_iterexpr
((
Iter
)
clause
),
rel
,
varno
);
else
if
(
fast_is_clause
(
clause
))
{
xfunc_fixvars
(
lfirst
(
lnext
(
clause
)),
rel
,
varno
);
xfunc_fixvars
(
lfirst
(
lnext
(
lnext
(
clause
))),
rel
,
varno
);
}
else
if
(
fast_is_funcclause
(
clause
))
for
(
tmpclause
=
lnext
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
xfunc_fixvars
(
lfirst
(
tmpclause
),
rel
,
varno
);
else
if
(
fast_not_clause
(
clause
))
xfunc_fixvars
(
lsecond
(
clause
),
rel
,
varno
);
else
if
(
fast_or_clause
(
clause
)
||
fast_and_clause
(
clause
))
for
(
tmpclause
=
lnext
(
clause
);
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
xfunc_fixvars
(
lfirst
(
tmpclause
),
rel
,
varno
);
else
elog
(
ERROR
,
"Clause node of undetermined type"
);
}
/*
** Comparison function for lisp_qsort() on a list of RestrictInfo's.
** arg1 and arg2 should really be of type (RestrictInfo *).
*/
int
xfunc_cinfo_compare
(
void
*
arg1
,
void
*
arg2
)
{
RestrictInfo
info1
=
*
(
RestrictInfo
*
)
arg1
;
RestrictInfo
info2
=
*
(
RestrictInfo
*
)
arg2
;
LispValue
clause1
=
(
LispValue
)
get_clause
(
info1
),
clause2
=
(
LispValue
)
get_clause
(
info2
);
return
xfunc_clause_compare
((
void
*
)
&
clause1
,
(
void
*
)
&
clause2
);
}
/*
** xfunc_clause_compare: comparison function for lisp_qsort() that compares two
** clauses based on expense/(1 - selectivity)
** arg1 and arg2 are really pointers to clauses.
*/
int
xfunc_clause_compare
(
void
*
arg1
,
void
*
arg2
)
{
LispValue
clause1
=
*
(
LispValue
*
)
arg1
;
LispValue
clause2
=
*
(
LispValue
*
)
arg2
;
Cost
rank1
,
/* total xfunc rank of clause1 */
rank2
;
/* total xfunc rank of clause2 */
rank1
=
xfunc_rank
(
clause1
);
rank2
=
xfunc_rank
(
clause2
);
if
(
rank1
<
rank2
)
return
-
1
;
else
if
(
rank1
==
rank2
)
return
0
;
else
return
1
;
}
/*
** xfunc_disjunct_sort
** given a list of clauses, for each clause sort the disjuncts by cost
** (this assumes the predicates have been converted to Conjunctive NF)
** Modifies the clause list!
*/
void
xfunc_disjunct_sort
(
LispValue
clause_list
)
{
LispValue
temp
;
foreach
(
temp
,
clause_list
)
if
(
or_clause
(
lfirst
(
temp
)))
lnext
(
lfirst
(
temp
))
=
lisp_qsort
(
lnext
(
lfirst
(
temp
)),
xfunc_disjunct_compare
);
}
/*
** xfunc_disjunct_compare: comparison function for qsort() that compares two
** disjuncts based on cost/selec.
** arg1 and arg2 are really pointers to disjuncts
*/
int
xfunc_disjunct_compare
(
Query
*
queryInfo
,
void
*
arg1
,
void
*
arg2
)
{
LispValue
disjunct1
=
*
(
LispValue
*
)
arg1
;
LispValue
disjunct2
=
*
(
LispValue
*
)
arg2
;
Cost
cost1
,
/* total cost of disjunct1 */
cost2
,
/* total cost of disjunct2 */
selec1
,
selec2
;
Cost
rank1
,
rank2
;
cost1
=
xfunc_expense
(
queryInfo
,
disjunct1
);
cost2
=
xfunc_expense
(
queryInfo
,
disjunct2
);
selec1
=
compute_clause_selec
(
queryInfo
,
disjunct1
,
LispNil
);
selec2
=
compute_clause_selec
(
queryInfo
,
disjunct2
,
LispNil
);
if
(
selec1
==
0
)
rank1
=
MAXFLOAT
;
else
if
(
cost1
==
0
)
rank1
=
0
;
else
rank1
=
cost1
/
selec1
;
if
(
selec2
==
0
)
rank2
=
MAXFLOAT
;
else
if
(
cost2
==
0
)
rank2
=
0
;
else
rank2
=
cost2
/
selec2
;
if
(
rank1
<
rank2
)
return
-
1
;
else
if
(
rank1
==
rank2
)
return
0
;
else
return
1
;
}
/* ------------------------ UTILITY FUNCTIONS ------------------------------- */
/*
** xfunc_func_width
** Given a function OID and operands, find the width of the return value.
*/
int
xfunc_func_width
(
RegProcedure
funcid
,
LispValue
args
)
{
Relation
rd
;
/* Relation Descriptor */
HeapTuple
tupl
;
/* structure to hold a cached tuple */
Form_pg_proc
proc
;
/* structure to hold the pg_proc tuple */
Form_pg_type
type
;
/* structure to hold the pg_type tuple */
LispValue
tmpclause
;
int
retval
;
/* lookup function and find its return type */
Assert
(
RegProcedureIsValid
(
funcid
));
tupl
=
SearchSysCacheTuple
(
PROCOID
,
ObjectIdGetDatum
(
funcid
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tupl
))
elog
(
ERROR
,
"Cache lookup failed for procedure %u"
,
funcid
);
proc
=
(
Form_pg_proc
)
GETSTRUCT
(
tupl
);
/* if function returns a tuple, get the width of that */
if
(
typeidTypeRelids
(
proc
->
prorettype
))
{
rd
=
heap_open
(
typeidTypeRelids
(
proc
->
prorettype
));
retval
=
xfunc_tuple_width
(
rd
);
heap_close
(
rd
);
goto
exit
;
}
else
/* function returns a base type */
{
tupl
=
SearchSysCacheTuple
(
TYPEOID
,
ObjectIdGetDatum
(
proc
->
prorettype
),
0
,
0
,
0
);
if
(
!
HeapTupleIsValid
(
tupl
))
elog
(
ERROR
,
"Cache lookup failed for type %u"
,
proc
->
prorettype
);
type
=
(
Form_pg_type
)
GETSTRUCT
(
tupl
);
/* if the type length is known, return that */
if
(
type
->
typlen
!=
-
1
)
{
retval
=
type
->
typlen
;
goto
exit
;
}
else
/* estimate the return size */
{
/* find width of the function's arguments */
for
(
tmpclause
=
args
;
tmpclause
!=
LispNil
;
tmpclause
=
lnext
(
tmpclause
))
retval
+=
xfunc_width
(
lfirst
(
tmpclause
));
/* multiply by outin_ratio */
retval
=
(
int
)
(
proc
->
prooutin_ratio
/
100
.
0
*
retval
);
goto
exit
;
}
}
exit:
return
retval
;
}
/*
** xfunc_tuple_width
** Return the sum of the lengths of all the attributes of a given relation
*/
int
xfunc_tuple_width
(
Relation
rd
)
{
int
i
;
int
retval
=
0
;
TupleDesc
tdesc
=
RelationGetDescr
(
rd
);
for
(
i
=
0
;
i
<
tdesc
->
natts
;
i
++
)
{
if
(
tdesc
->
attrs
[
i
]
->
attlen
!=
-
1
)
retval
+=
tdesc
->
attrs
[
i
]
->
attlen
;
else
retval
+=
VARLEN_DEFAULT
;
}
return
retval
;
}
/*
** xfunc_num_join_clauses
** Find the number of join clauses associated with this join path
*/
int
xfunc_num_join_clauses
(
JoinPath
path
)
{
int
num
=
length
(
get_pathrestrictinfo
(
path
));
if
(
IsA
(
path
,
MergePath
))
return
num
+
length
(
get_path_mergeclauses
((
MergePath
)
path
));
else
if
(
IsA
(
path
,
HashPath
))
return
num
+
length
(
get_path_hashclauses
((
HashPath
)
path
));
else
return
num
;
}
/*
** xfunc_LispRemove
** Just like LispRemove, but it whines if the item to be removed ain't there
*/
LispValue
xfunc_LispRemove
(
LispValue
foo
,
List
bar
)
{
LispValue
temp
=
LispNil
;
LispValue
result
=
LispNil
;
int
sanity
=
false
;
for
(
temp
=
bar
;
!
null
(
temp
);
temp
=
lnext
(
temp
))
if
(
!
equal
((
Node
)
(
foo
),
(
Node
)
(
lfirst
(
temp
))))
result
=
lappend
(
result
,
lfirst
(
temp
));
else
sanity
=
true
;
/* found a matching item to remove! */
if
(
!
sanity
)
elog
(
ERROR
,
"xfunc_LispRemove: didn't find a match!"
);
return
result
;
}
#define Node_Copy(a, b, c, d) \
do { \
if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) \
{ \
return false; \
} \
} while(0)
/*
** xfunc_copyrel
** Just like _copyRel, but doesn't copy the paths
*/
bool
xfunc_copyrel
(
RelOptInfo
from
,
RelOptInfo
*
to
)
{
RelOptInfo
newnode
;
Pointer
(
*
alloc
)
()
=
palloc
;
/* COPY_CHECKARGS() */
if
(
to
==
NULL
)
return
false
;
/* COPY_CHECKNULL() */
if
(
from
==
NULL
)
{
(
*
to
)
=
NULL
;
return
true
;
}
/* COPY_NEW(c) */
newnode
=
(
RelOptInfo
)
(
*
alloc
)
(
classSize
(
RelOptInfo
));
if
(
newnode
==
NULL
)
return
false
;
/*
* copy node superclass fields
*/
CopyNodeFields
((
Node
)
from
,
(
Node
)
newnode
,
alloc
);
/*
* copy remainder of node
*/
Node_Copy
(
from
,
newnode
,
alloc
,
relids
);
newnode
->
indexed
=
from
->
indexed
;
newnode
->
pages
=
from
->
pages
;
newnode
->
tuples
=
from
->
tuples
;
newnode
->
size
=
from
->
size
;
newnode
->
width
=
from
->
width
;
Node_Copy
(
from
,
newnode
,
alloc
,
targetlist
);
/*
* No!!!! Node_Copy(from, newnode, alloc, pathlist);
* Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from,
* newnode, alloc, cheapestpath);
*/
#if 0 /* can't use Node_copy now. 2/95 -ay */
Node_Copy(from, newnode, alloc, classlist);
Node_Copy(from, newnode, alloc, indexkeys);
Node_Copy(from, newnode, alloc, ordering);
#endif
Node_Copy
(
from
,
newnode
,
alloc
,
restrictinfo
);
Node_Copy
(
from
,
newnode
,
alloc
,
joininfo
);
Node_Copy
(
from
,
newnode
,
alloc
,
innerjoin
);
(
*
to
)
=
newnode
;
return
true
;
}
src/include/optimizer/_deadcode/xfunc.h
deleted
100644 → 0
View file @
739adf32
/*-------------------------------------------------------------------------
*
* xfunc.h
* prototypes for xfunc.c and predmig.c.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: xfunc.h,v 1.9 2002/06/20 20:29:51 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef XFUNC_H
#define XFUNC_H
#include "nodes/relation.h"
#include "utils/rel.h"
/* command line arg flags */
#define XFUNC_OFF -1
/* do no optimization of expensive preds */
#define XFUNC_NOR 2
/* do no optimization of OR clauses */
#define XFUNC_NOPULL 4
/* never pull restrictions above joins */
#define XFUNC_NOPM 8
/* don't do predicate migration */
#define XFUNC_WAIT 16
/* don't do pullup until predicate
* migration */
#define XFUNC_PULLALL 32
/* pull all expensive restrictions up,
* always */
/* constants for local and join predicates */
#define XFUNC_LOCPRD 1
#define XFUNC_JOINPRD 2
#define XFUNC_UNKNOWN 0
extern
int
XfuncMode
;
/* defined in tcop/postgres.c */
/* default width assumed for variable length attributes */
#define VARLEN_DEFAULT 128;
/* Macro to get group rank out of group cost and group sel */
#define get_grouprank(a) ((get_groupsel(a) - 1) / get_groupcost(a))
/* Macro to see if a path node is actually a Join */
#define is_join(pathnode) (length(get_relids(get_parent(pathnode))) > 1 ? 1 : 0)
/* function prototypes from planner/path/xfunc.c */
extern
void
xfunc_trypullup
(
RelOptInfo
*
rel
);
extern
int
xfunc_shouldpull
(
Path
*
childpath
,
JoinPath
*
parentpath
,
int
whichchild
,
RestrictInfo
*
maxcinfopt
);
extern
RestrictInfo
*
xfunc_pullup
(
Path
*
childpath
,
JoinPath
*
parentpath
,
RestrictInfo
*
cinfo
,
int
whichchild
,
int
clausetype
);
extern
Cost
xfunc_rank
(
Expr
*
clause
);
extern
Cost
xfunc_expense
(
Query
*
queryInfo
,
Expr
*
clause
);
extern
Cost
xfunc_join_expense
(
JoinPath
*
path
,
int
whichchild
);
extern
Cost
xfunc_local_expense
(
Expr
*
clause
);
extern
Cost
xfunc_func_expense
(
Expr
*
node
,
List
*
args
);
extern
int
xfunc_width
(
Expr
*
clause
);
/* static, moved to xfunc.c */
/* extern int xfunc_card_unreferenced(Expr *clause, Relids referenced); */
extern
int
xfunc_card_product
(
Relids
relids
);
extern
List
*
xfunc_find_references
(
List
*
clause
);
extern
List
*
xfunc_primary_join
(
JoinPath
*
pathnode
);
extern
Cost
xfunc_get_path_cost
(
Path
*
pathnode
);
extern
Cost
xfunc_total_path_cost
(
JoinPath
*
pathnode
);
extern
Cost
xfunc_expense_per_tuple
(
JoinPath
*
joinnode
,
int
whichchild
);
extern
void
xfunc_fixvars
(
Expr
*
clause
,
RelOptInfo
*
rel
,
int
varno
);
extern
int
xfunc_cinfo_compare
(
void
*
arg1
,
void
*
arg2
);
extern
int
xfunc_clause_compare
(
void
*
arg1
,
void
*
arg2
);
extern
void
xfunc_disjunct_sort
(
List
*
clause_list
);
extern
int
xfunc_disjunct_compare
(
void
*
arg1
,
void
*
arg2
);
extern
int
xfunc_func_width
(
RegProcedure
funcid
,
List
*
args
);
extern
int
xfunc_tuple_width
(
Relation
rd
);
extern
int
xfunc_num_join_clauses
(
JoinPath
*
path
);
extern
List
*
xfunc_LispRemove
(
List
*
foo
,
List
*
bar
);
extern
bool
xfunc_copyrel
(
RelOptInfo
*
from
,
RelOptInfo
**
to
);
/*
* function prototypes for path/predmig.c
*/
extern
bool
xfunc_do_predmig
(
Path
root
);
#endif
/* XFUNC_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment