Hello
some years ago there was some plans about parser's extensibility. I am
able write bison extensions, but I thing, so lot of work should be
done via hooking of transform stage.
I did small example - real implementation of Oracle's decode function.
It's based on hooking transformExpr function.
It works. And I thing, this should to solve lot of special task
related to increase compatibility with Oracle, Informix, or it could
be usefull for some others (json support).
postgres=# load 'decode';
LOAD
postgres=# select
decode(null::integer,2,'ahoj',3,'Petr',1,'Pavel',null, 'jaja');
decode
--------
jaja
(1 row)
postgres=# select decode(3,2,'ahoj',3,'Petr',1,'Pavel',null, 'jaja');
decode
--------
Petr
(1 row)
postgres=# select decode(6,2,'ahoj',3,'Petr',1,'Pavel',null, 'jaja');
decode
--------
(1 row)
postgres=# select decode(6,2,'ahoj',3,'Petr',1,'Pavel',null, 'jaja', 'Milos');
decode
--------
Milos
(1 row)
Any ideas, notes?
regards
Pavel Stehule
/*-------------------------------------------------------------------------
*
* decode.c
*
*
* Copyright (c) 2008-2009, PostgreSQL Global Developent Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/contrib/auto_explain/auto_explain.c,v 1.4
2009/01/05 13:35:38 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "fmgr.h"
#include "catalog/namespace.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "catalog/pg_type.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
PG_MODULE_MAGIC;
/* Saved hook value */
static ParseExprTransform_hook_type prev_transformExpr = NULL;
void _PG_init(void);
void _PG_fini(void);
static Node * transformDecode(ParseState *pstate, Node *expr);
/*
* Module load callback
*/
void
_PG_init(void)
{
/* Install hooks. */
prev_transformExpr = ParseExprTransform_hook;
ParseExprTransform_hook = transformDecode;
}
/*
* Module unload callback
*/
void
_PG_fini(void)
{
/* Uninstall hooks. */
ParseExprTransform_hook = prev_transformExpr;
}
/*
* Decode transform hook. When I diagnose decode func call, I transform it.
*/
Node *
transformDecode(ParseState *pstate, Node *expr)
{
if (IsA(expr, FuncCall))
{
FuncCall *fnc = (FuncCall *) expr;
char *schemaname;
char *funcname;
DeconstructQualifiedName(fnc->funcname, &schemaname, &funcname);
if (schemaname != NULL && strncmp(schemaname, "pg_catalog", 10)
!= 0)
goto not_decode_func;
if (strncmp(funcname, "decode", 6) == 0)
{
CaseExpr *newc = makeNode(CaseExpr);
int pos = 0;
ListCell *l;
int def_pos;
int nargs = list_length(fnc->args);
CaseTestExpr *placeholder = NULL;
CaseWhen *neww = NULL;
List *newargs = NIL;
List *resultexprs = NIL;
Node *defresult = NULL;
Oid ptype;
def_pos = nargs % 2 == 0 ? nargs - 1: -1;
foreach(l, fnc->args)
{
/* first param, generate placeholder */
if (pos == 0)
{
Node *arg = transformExpr(pstate, (Node
*) lfirst(l));
if (exprType(arg) == UNKNOWNOID)
arg =
coerce_to_common_type(pstate, arg, TEXTOID, "DECODE");
placeholder = makeNode(CaseTestExpr);
placeholder->typeId = exprType(arg);
placeholder->typeMod = exprTypmod(arg);
newc->arg = (Expr *) arg;
}
/* searched value, generate CaseWhen node */
if (pos % 2 != 0 && pos != def_pos)
{
Node *warg;
Node *expr = (Node *) lfirst(l);
neww = makeNode(CaseWhen);
neww->location = exprLocation(expr);
warg = (Node *) makeA_Expr(AEXPR_NOT,
NIL, NULL,
(Node
*) makeSimpleA_Expr(AEXPR_DISTINCT,
"=",
(Node *) placeholder,
expr,
exprLocation(expr)),
exprLocation(expr));
neww->expr = (Expr *)
transformExpr(pstate, warg);
neww->expr = (Expr *)
coerce_to_boolean(pstate,
(Node *) neww->expr,
"DECODE");
}
/* result value, fill last generated CaseWhen
node */
if (pos % 2 == 0 && pos > 0)
{
neww->result = (Expr *)
transformExpr(pstate, (Node *) lfirst(l));;
newargs = lappend(newargs, neww);
resultexprs = lappend(resultexprs,
neww->result);
}
if (pos == def_pos)
defresult = (Node *)
transformExpr(pstate, (Node *) lfirst(l));;
pos += 1;
}
newc->args = newargs;
newc->location = fnc->location;
if (defresult == NULL)
{
A_Const *n = makeNode(A_Const);
n->val.type = T_Null;
n->location = -1;
defresult = (Node *) transformExpr(pstate,
(Node *) n);
}
newc->defresult = (Expr *) defresult;
resultexprs = lcons(newc->defresult, resultexprs);
ptype = select_common_type(pstate, resultexprs,
"DECODE", NULL);
Assert(OidIsValid(ptype));
newc->casetype = ptype;
/* Convert default result clause, if necessary */
newc->defresult = (Expr *)
coerce_to_common_type(pstate,
(Node *)
newc->defresult,
ptype,
"DECODE");
/* Convert when-clause results, if necessary */
foreach(l, newc->args)
{
CaseWhen *w = (CaseWhen *) lfirst(l);
w->result = (Expr *)
coerce_to_common_type(pstate,
(Node *)
w->result,
ptype,
"DECODE");
}
newc->location = fnc->location;
return (Node *) newc;
}
}
not_decode_func:
if (prev_transformExpr)
return prev_transformExpr(pstate, expr);
else
return standard_transformExpr(pstate, expr);
}
*** ./parse_expr.c.orig 2009-02-10 22:50:47.000000000 +0100
--- ./parse_expr.c 2009-02-11 06:45:37.000000000 +0100
***************
*** 36,41 ****
--- 36,44 ----
bool Transform_null_equals = false;
+ /* Hook for plugins to get control in transformExpr */
+ ParseExprTransform_hook_type ParseExprTransform_hook = NULL;
+
static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
***************
*** 97,105 ****
--- 100,118 ----
* a Const. More care is needed for node types that are used as both
* input and output of transformExpr; see SubLink for example.
*/
+
Node *
transformExpr(ParseState *pstate, Node *expr)
{
+ if (ParseExprTransform_hook)
+ return (*ParseExprTransform_hook) (pstate, expr);
+ else
+ return standard_transformExpr(pstate, expr);
+ }
+
+ Node *
+ standard_transformExpr(ParseState *pstate, Node *expr)
+ {
Node *result = NULL;
if (expr == NULL)
***************
*** 107,113 ****
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
!
switch (nodeTag(expr))
{
case T_ColumnRef:
--- 120,126 ----
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
!
switch (nodeTag(expr))
{
case T_ColumnRef:
***************
*** 313,319 ****
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
! }
return result;
}
--- 326,332 ----
/* should not reach here */
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
break;
! }
return result;
}
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers