/*-------------------------------------------------------------------------
 *
 * view.c--
 *    use rewrite rules to construct views
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /usr/local/devel/pglite/cvs/src/backend/commands/view.c,v 1.7 1995/03/17 20:25:37 andrew Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <stdio.h>	/* for sprintf() */
#include "postgres.h"
#include "access/heapam.h"
#include "access/xact.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "utils/elog.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "nodes/parsenodes.h"
#include "parser/catalog_utils.h"
#include "parser/parse_query.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteRemove.h"
#include "commands/creatinh.h"

Name
attname(Name relname, int attnum)
{
    Relation relptr;
    HeapTuple atp;
    Oid reloid;
    Name varname;
    
    relptr = heap_openr (relname );
    reloid = RelationGetRelationId ( relptr );
    
    atp = SearchSysCacheTuple(ATTNUM,
			      (char *) ObjectIdGetDatum(reloid),
			      (char *) Int32GetDatum(attnum),
			      (char *) NULL, (char *) NULL);
    if (!HeapTupleIsValid(atp)) {
	elog(WARN, "getattnvals: no attribute tuple %d %d",
	     reloid, attnum);
	return(0);
    }
    varname = (Name)&( ((AttributeTupleForm) GETSTRUCT(atp))->attname );
    
    return ( varname );
}

/*---------------------------------------------------------------------
 * DefineVirtualRelation
 *
 * Create the "view" relation.
 * `DefineRelation' does all the work, we just provide the correct
 * arguments!
 *
 * If the relation already exists, then 'DefineRelation' will abort
 * the xact...
 *---------------------------------------------------------------------
 */
static void
DefineVirtualRelation(Name relname, List *tlist)
{
    CreateStmt createStmt;
    List *attrList, *t;
    TargetEntry *entry;
    Resdom  *res;
    Name resname;
    Name restypename;
    
    /*
     * create a list with one entry per attribute of this relation.
     * Each entry is a two element list. The first element is the
     * name of the attribute (a string) and the second the name of the type
     * (NOTE: a string, not a type id!).
     */
    attrList = NIL;
    if (tlist!=NIL) {
	foreach (t, tlist ) {
	    ColumnDef *def = makeNode(ColumnDef);
	    TypeName *typename;

	    /*
	     * find the names of the attribute & its type
	     */
	    entry = lfirst(t);
	    res   = entry->resdom;
	    resname = res->resname;
	    restypename = tname(get_id_type((long)res->restype));

	    typename = makeNode(TypeName);

	    /* XXX char * */
	    typename->name = (char*)makeName((char*)&restypename->data[0]); 
	    def->colname = (char*)makeName((char*)&resname->data[0]);

	    def->typename = typename;

	    attrList = lappend(attrList, def);
	}
    } else {
	elog ( WARN, "attempted to define virtual relation with no attrs");
    }
    
    /*
     * now create the parametesr for keys/inheritance etc.
     * All of them are nil...
     */
    createStmt.relname = (char*)relname;
    createStmt.tableElts = attrList;
    createStmt.inhRelnames = NIL;
    createStmt.archiveType = ARCH_NONE;
    createStmt.location = -1;
    createStmt.archiveLoc = -1;

    /*
     * finally create the relation...
     */
    DefineRelation(&createStmt);
}    

/*------------------------------------------------------------------
 * makeViewRetrieveRuleName
 *
 * Given a view name, create the name for the 'on retrieve to "view"'
 * rule.
 * This routine is called when defining/removing a view.
 *
 * NOTE: it quarantees that the name is at most 15 chars long
 *
 * XXX it also means view_name cannot be 16 chars long! - ay 11/94
 *------------------------------------------------------------------
 */
void
makeRetrieveViewRuleName(Name rule_name, Name view_name)
{
    char buf[100];
    
    /*
     * make sure that no non-null characters follow the
     * '\0' at the end of the string...
     */
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "_RET%.*s", NAMEDATALEN, view_name->data);
    buf[15] = '\0';
    namestrcpy(rule_name, buf);
}

static RuleStmt *
FormViewRetrieveRule(Name view_name, Query *view_parse)
{
    RuleStmt *rule;
    char16 rname;
    Attr *attr;
    
    /*
     * Create a RuleStmt that corresponds to the suitable
     * rewrite rule args for DefineQueryRewrite();
     */
    rule = makeNode(RuleStmt);
    makeRetrieveViewRuleName(&rname, view_name);

    attr = makeNode(Attr);
    attr->relname = (char*)makeName((char*)view_name);

    rule->rulename = (char*)makeName((char*)&rname);	/* XXX char * */
    rule->whereClause = NULL;
    rule->event = CMD_SELECT;
    rule->object = attr;
    rule->instead = true;
    rule->actions = lcons(view_parse, NIL);
	
    return rule;
}

static void
DefineViewRules(Name view_name, Query *view_parse)
{
    RuleStmt *retrieve_rule	= NULL;
#ifdef NOTYET
    RuleStmt *replace_rule	= NULL;
    RuleStmt *append_rule	= NULL;
    RuleStmt *delete_rule	= NULL;
#endif
    
    retrieve_rule = 
	FormViewRetrieveRule(view_name, view_parse);
    
#ifdef NOTYET
    
    replace_rule =
	FormViewReplaceRule(view_name, view_parse);
    append_rule = 
	FormViewAppendRule(view_name, view_parse);
    delete_rule = 
	FormViewDeleteRule(view_name, view_parse);
    
#endif
    
    DefineQueryRewrite(retrieve_rule);

#ifdef NOTYET
    DefineQueryRewrite(replace_rule);
    DefineQueryRewrite(append_rule);
    DefineQueryRewrite(delete_rule);
#endif
    
}     

/*---------------------------------------------------------------
 * UpdateRangeTableOfViewParse
 *
 * Update the range table of the given parsetree.
 * This update consists of adding two new entries IN THE BEGINNING
 * of the range table (otherwise the rule system will die a slow,
 * horrible and painful death, and we do not want that now, do we?)
 * one for the CURRENT relation and one for the NEW one (both of
 * them refer in fact to the "view" relation).
 *
 * Of course we must also increase the 'varnos' of all the Var nodes
 * by 2...
 *
 * NOTE: these are destructive changes. It would be difficult to
 * make a complete copy of the parse tree and make the changes
 * in the copy.
 *---------------------------------------------------------------
 */
static void
UpdateRangeTableOfViewParse(Name view_name, Query *view_parse)
{
    List *old_rt;
    List *new_rt;
    RangeTblEntry *rt_entry1, *rt_entry2;
    
    /*
     * first offset all var nodes by 2
     */
    OffsetVarNodes((Node*)view_parse->targetList, 2);
    OffsetVarNodes(view_parse->qual, 2);
    
    /*
     * find the old range table...
     */
    old_rt = view_parse->rtable;

    /*
     * create the 2 new range table entries and form the new
     * range table...
     * CURRENT first, then NEW....
     */
    rt_entry1 =
	makeRangeTableEntry((char*)view_name, FALSE, NULL, "*CURRENT*");
    rt_entry2 =
	makeRangeTableEntry((char*)view_name, FALSE, NULL, "*NEW*");
    new_rt = lcons(rt_entry2, old_rt);
    new_rt = lcons(rt_entry1, new_rt);
    
    /*
     * Now the tricky part....
     * Update the range table in place... Be careful here, or
     * hell breaks loooooooooooooOOOOOOOOOOOOOOOOOOSE!
     */
    view_parse->rtable = new_rt;
}

/*-------------------------------------------------------------------
 * DefineView
 *
 *	- takes a "viewname", "parsetree" pair and then
 *	1)	construct the "virtual" relation 
 *	2)	commit the command but NOT the transaction,
 *		so that the relation exists
 *		before the rules are defined.
 *	2)	define the "n" rules specified in the PRS2 paper
 *		over the "virtual" relation
 *-------------------------------------------------------------------
 */
void
DefineView(Name view_name, Query *view_parse)
{
    List *view_tlist;

    view_tlist = view_parse->targetList;
    
    /*
     * Create the "view" relation
     * NOTE: if it already exists, the xaxt will be aborted.
     */
    DefineVirtualRelation(view_name, view_tlist);
    
    /*
     * The relation we have just created is not visible
     * to any other commands running with the same transaction &
     * command id.
     * So, increment the command id counter (but do NOT pfree any
     * memory!!!!)
     */
    CommandCounterIncrement();
    
    /*
     * The range table of 'view_parse' does not contain entries
     * for the "CURRENT" and "NEW" relations.
     * So... add them!
     * NOTE: we make the update in place! After this call 'view_parse' 
     * will never be what it used to be...
     */
    UpdateRangeTableOfViewParse(view_name, view_parse);
    DefineViewRules(view_name, view_parse);
}

/*------------------------------------------------------------------
 * RemoveView
 *
 * Remove a view given its name
 *------------------------------------------------------------------
 */
void
RemoveView(Name view_name)
{
    char16 rname;
    
    /*
     * first remove all the "view" rules...
     * Currently we only have one!
     */
    makeRetrieveViewRuleName(&rname, view_name);
    RemoveRewriteRule(&rname);

    /*
     * we don't really need that, but just in case...
     */
    CommandCounterIncrement();
    
    /*
     * now remove the relation.
     */
    heap_destroy(view_name->data);
}
