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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to