aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c478
1 files changed, 409 insertions, 69 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3f9264308dd..872b607e87c 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.71 2001/01/03 22:01:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.72 2001/02/14 21:35:05 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -35,11 +35,11 @@
*
**********************************************************************/
+#include "postgres.h"
+
#include <unistd.h>
#include <fcntl.h>
-#include "postgres.h"
-
#include "catalog/pg_index.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_shadow.h"
@@ -52,6 +52,7 @@
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -59,13 +60,30 @@
* Local data types
* ----------
*/
+
+/* Context info needed for invoking a recursive querytree display routine */
typedef struct
{
StringInfo buf; /* output buffer to append to */
- List *rangetables; /* List of List of RangeTblEntry */
+ List *namespaces; /* List of deparse_namespace nodes */
bool varprefix; /* TRUE to print prefixes on Vars */
} deparse_context;
+/*
+ * Each level of query context around a subtree needs a level of Var namespace.
+ * The rangetable is the list of actual RTEs, and the namespace indicates
+ * which parts of the rangetable are accessible (and under what aliases)
+ * in the expression currently being looked at. A Var having varlevelsup=N
+ * refers to the N'th item (counting from 0) in the current context's
+ * namespaces list.
+ */
+typedef struct
+{
+ List *rtable; /* List of RangeTblEntry nodes */
+ List *namespace; /* List of joinlist items (RangeTblRef and
+ * JoinExpr nodes) */
+} deparse_namespace;
+
/* ----------
* Global data
@@ -92,7 +110,7 @@ static char *query_getopclass = "SELECT * FROM pg_opclass WHERE oid = $1";
*/
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc);
-static void get_query_def(Query *query, StringInfo buf, List *parentrtables);
+static void get_query_def(Query *query, StringInfo buf, List *parentnamespace);
static void get_select_query_def(Query *query, deparse_context *context);
static void get_insert_query_def(Query *query, deparse_context *context);
static void get_update_query_def(Query *query, deparse_context *context);
@@ -102,7 +120,14 @@ static void get_basic_select_query(Query *query, deparse_context *context);
static void get_setop_query(Node *setOp, Query *query,
deparse_context *context, bool toplevel);
static bool simple_distinct(List *distinctClause, List *targetList);
-static RangeTblEntry *get_rte_for_var(Var *var, deparse_context *context);
+static void get_names_for_var(Var *var, deparse_context *context,
+ char **refname, char **attname);
+static bool get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
+ char **refname, char **attname);
+static bool find_alias_in_namespace(Node *nsnode, Node *expr,
+ List *rangetable, int levelsup,
+ char **refname, char **attname);
+static bool phony_equal(Node *expr1, Node *expr2, int levelsup);
static void get_rule_expr(Node *node, deparse_context *context);
static void get_func_expr(Expr *expr, deparse_context *context);
static void get_tle_expr(TargetEntry *tle, deparse_context *context);
@@ -599,30 +624,24 @@ pg_get_userbyid(PG_FUNCTION_ARGS)
* expr is the node tree to be deparsed. It must be a transformed expression
* tree (ie, not the raw output of gram.y).
*
- * rangetables is a List of Lists of RangeTblEntry nodes: first sublist is for
- * varlevelsup = 0, next for varlevelsup = 1, etc. In each sublist the first
- * item is for varno = 1, next varno = 2, etc. (Each sublist has the same
- * format as the rtable list of a parsetree or query.)
+ * dpcontext is a list of deparse_namespace nodes representing the context
+ * for interpreting Vars in the node tree.
*
* forceprefix is TRUE to force all Vars to be prefixed with their table names.
- * Otherwise, a prefix is printed only if there's more than one table involved
- * (and someday the code might try to print one only if there's ambiguity).
*
* The result is a palloc'd string.
* ----------
*/
char *
-deparse_expression(Node *expr, List *rangetables, bool forceprefix)
+deparse_expression(Node *expr, List *dpcontext, bool forceprefix)
{
StringInfoData buf;
deparse_context context;
initStringInfo(&buf);
context.buf = &buf;
- context.rangetables = rangetables;
- context.varprefix = (forceprefix ||
- length(rangetables) != 1 ||
- length((List *) lfirst(rangetables)) != 1);
+ context.namespaces = dpcontext;
+ context.varprefix = forceprefix;
rulename = ""; /* in case of errors */
@@ -632,6 +651,43 @@ deparse_expression(Node *expr, List *rangetables, bool forceprefix)
}
/* ----------
+ * deparse_context_for - Build deparse context for a single relation
+ *
+ * Given the name and OID of a relation, build deparsing context for an
+ * expression referencing only that relation (as varno 1, varlevelsup 0).
+ * This is presently sufficient for the external uses of deparse_expression.
+ * ----------
+ */
+List *
+deparse_context_for(char *relname, Oid relid)
+{
+ deparse_namespace *dpns;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+
+ dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+
+ /* Build a minimal RTE for the rel */
+ rte = makeNode(RangeTblEntry);
+ rte->relname = relname;
+ rte->relid = relid;
+ rte->eref = makeNode(Attr);
+ rte->eref->relname = relname;
+ rte->inh = false;
+ rte->inFromCl = true;
+ /* Build one-element rtable */
+ dpns->rtable = makeList1(rte);
+
+ /* Build a namespace list referencing this RTE only */
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = 1;
+ dpns->namespace = makeList1(rtr);
+
+ /* Return a one-deep namespace stack */
+ return makeList1(dpns);
+}
+
+/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
* ----------
@@ -722,6 +778,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
Node *qual;
Query *query;
deparse_context context;
+ deparse_namespace dpns;
appendStringInfo(buf, " WHERE ");
@@ -729,8 +786,10 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
query = (Query *) lfirst(actions);
context.buf = buf;
- context.rangetables = makeList1(query->rtable);
+ context.namespaces = makeList1(&dpns);
context.varprefix = (length(query->rtable) != 1);
+ dpns.rtable = query->rtable;
+ dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
get_rule_expr(qual, &context);
}
@@ -844,14 +903,17 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
* ----------
*/
static void
-get_query_def(Query *query, StringInfo buf, List *parentrtables)
+get_query_def(Query *query, StringInfo buf, List *parentnamespace)
{
deparse_context context;
+ deparse_namespace dpns;
context.buf = buf;
- context.rangetables = lcons(query->rtable, parentrtables);
- context.varprefix = (parentrtables != NIL ||
+ context.namespaces = lcons(&dpns, parentnamespace);
+ context.varprefix = (parentnamespace != NIL ||
length(query->rtable) != 1);
+ dpns.rtable = query->rtable;
+ dpns.namespace = query->jointree ? query->jointree->fromlist : NIL;
switch (query->commandType)
{
@@ -1025,11 +1087,10 @@ get_basic_select_query(Query *query, deparse_context *context)
else
{
Var *var = (Var *) (tle->expr);
- RangeTblEntry *rte;
+ char *refname;
char *attname;
- rte = get_rte_for_var(var, context);
- attname = get_rte_attribute_name(rte, var->varattno);
+ get_names_for_var(var, context, &refname, &attname);
tell_as = (strcmp(attname, tle->resdom->resname) != 0);
}
@@ -1088,7 +1149,7 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
Query *subquery = rte->subquery;
Assert(subquery != NULL);
- get_query_def(subquery, buf, context->rangetables);
+ get_query_def(subquery, buf, context->namespaces);
}
else if (IsA(setOp, SetOperationStmt))
{
@@ -1336,20 +1397,277 @@ get_utility_query_def(Query *query, deparse_context *context)
/*
- * Find the RTE referenced by a (possibly nonlocal) Var.
+ * Get the relation refname and attname for a (possibly nonlocal) Var.
+ *
+ * This is trickier than it ought to be because of the possibility of aliases
+ * and limited scope of refnames. We have to try to return the correct alias
+ * with respect to the current namespace given by the context.
*/
-static RangeTblEntry *
-get_rte_for_var(Var *var, deparse_context *context)
+static void
+get_names_for_var(Var *var, deparse_context *context,
+ char **refname, char **attname)
{
- List *rtlist = context->rangetables;
+ List *nslist = context->namespaces;
int sup = var->varlevelsup;
+ deparse_namespace *dpns;
+ RangeTblEntry *rte;
+
+ /* Find appropriate nesting depth */
+ while (sup-- > 0 && nslist != NIL)
+ nslist = lnext(nslist);
+ if (nslist == NIL)
+ elog(ERROR, "get_names_for_var: bogus varlevelsup %d",
+ var->varlevelsup);
+ dpns = (deparse_namespace *) lfirst(nslist);
+
+ /* Scan namespace to see if we can find an alias for the var */
+ if (find_alias_in_namespace((Node *) dpns->namespace, (Node *) var,
+ dpns->rtable, var->varlevelsup,
+ refname, attname))
+ return;
+
+ /*
+ * Otherwise, fall back on the rangetable entry. This should happen
+ * only for uses of special RTEs like *NEW* and *OLD*, which won't
+ * get placed in our namespace.
+ */
+ rte = rt_fetch(var->varno, dpns->rtable);
+ *refname = rte->eref->relname;
+ *attname = get_rte_attribute_name(rte, var->varattno);
+}
+
+/*
+ * Check to see if a CASE expression matches a FULL JOIN's output expression.
+ * If so, return the refname and alias it should be expressed as.
+ */
+static bool
+get_alias_for_case(CaseExpr *caseexpr, deparse_context *context,
+ char **refname, char **attname)
+{
+ List *nslist;
+ int sup;
- while (sup-- > 0)
- rtlist = lnext(rtlist);
+ /*
+ * This could be done more efficiently if we first groveled through the
+ * CASE to find varlevelsup values, but it's probably not worth the
+ * trouble. All this code will go away someday anyway ...
+ */
- return rt_fetch(var->varno, (List *) lfirst(rtlist));
+ sup = 0;
+ foreach(nslist, context->namespaces)
+ {
+ deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
+
+ if (find_alias_in_namespace((Node *) dpns->namespace,
+ (Node *) caseexpr,
+ dpns->rtable, sup,
+ refname, attname))
+ return true;
+ sup++;
+ }
+ return false;
}
+/*
+ * Recursively scan a namespace (same representation as a jointree) to see
+ * if we can find an alias for the given expression. If so, return the
+ * correct alias refname and attname. The expression may be either a plain
+ * Var or a CASE expression (which may be a FULL JOIN reference).
+ */
+static bool
+find_alias_in_namespace(Node *nsnode, Node *expr,
+ List *rangetable, int levelsup,
+ char **refname, char **attname)
+{
+ if (nsnode == NULL)
+ return false;
+ if (IsA(nsnode, RangeTblRef))
+ {
+ if (IsA(expr, Var))
+ {
+ Var *var = (Var *) expr;
+ int rtindex = ((RangeTblRef *) nsnode)->rtindex;
+
+ if (var->varno == rtindex && var->varlevelsup == levelsup)
+ {
+ RangeTblEntry *rte = rt_fetch(rtindex, rangetable);
+
+ *refname = rte->eref->relname;
+ *attname = get_rte_attribute_name(rte, var->varattno);
+ return true;
+ }
+ }
+ }
+ else if (IsA(nsnode, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) nsnode;
+
+ if (j->alias)
+ {
+ List *vlist;
+ List *nlist;
+
+ /*
+ * Does the expr match any of the output columns of the join?
+ *
+ * We can't just use equal() here, because the given expr may
+ * have nonzero levelsup, whereas the saved expression in the
+ * JoinExpr should have zero levelsup.
+ */
+ nlist = j->colnames;
+ foreach(vlist, j->colvars)
+ {
+ if (phony_equal(lfirst(vlist), expr, levelsup))
+ {
+ *refname = j->alias->relname;
+ *attname = strVal(lfirst(nlist));
+ return true;
+ }
+ nlist = lnext(nlist);
+ }
+ /*
+ * Tables within an aliased join are invisible from outside
+ * the join, according to the scope rules of SQL92 (the join
+ * is considered a subquery). So, stop here.
+ */
+ return false;
+ }
+ if (find_alias_in_namespace(j->larg, expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ if (find_alias_in_namespace(j->rarg, expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ }
+ else if (IsA(nsnode, List))
+ {
+ List *l;
+
+ foreach(l, (List *) nsnode)
+ {
+ if (find_alias_in_namespace(lfirst(l), expr,
+ rangetable, levelsup,
+ refname, attname))
+ return true;
+ }
+ }
+ else
+ elog(ERROR, "find_alias_in_namespace: unexpected node type %d",
+ nodeTag(nsnode));
+ return false;
+}
+
+/*
+ * Check for equality of two expressions, with the proviso that all Vars in
+ * expr1 should have varlevelsup = 0, while all Vars in expr2 should have
+ * varlevelsup = levelsup.
+ *
+ * In reality we only need to support equality checks on Vars and the type
+ * of CASE expression that is used for FULL JOIN outputs, so not all node
+ * types need be handled here.
+ *
+ * Otherwise, this code is a straight ripoff from equalfuncs.c.
+ */
+static bool
+phony_equal(Node *expr1, Node *expr2, int levelsup)
+{
+ if (expr1 == NULL || expr2 == NULL)
+ return (expr1 == expr2);
+ if (nodeTag(expr1) != nodeTag(expr2))
+ return false;
+ if (IsA(expr1, Var))
+ {
+ Var *a = (Var *) expr1;
+ Var *b = (Var *) expr2;
+
+ if (a->varno != b->varno)
+ return false;
+ if (a->varattno != b->varattno)
+ return false;
+ if (a->vartype != b->vartype)
+ return false;
+ if (a->vartypmod != b->vartypmod)
+ return false;
+ if (a->varlevelsup != 0 || b->varlevelsup != levelsup)
+ return false;
+ if (a->varnoold != b->varnoold)
+ return false;
+ if (a->varoattno != b->varoattno)
+ return false;
+ return true;
+ }
+ if (IsA(expr1, CaseExpr))
+ {
+ CaseExpr *a = (CaseExpr *) expr1;
+ CaseExpr *b = (CaseExpr *) expr2;
+
+ if (a->casetype != b->casetype)
+ return false;
+ if (!phony_equal(a->arg, b->arg, levelsup))
+ return false;
+ if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
+ return false;
+ if (!phony_equal(a->defresult, b->defresult, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, CaseWhen))
+ {
+ CaseWhen *a = (CaseWhen *) expr1;
+ CaseWhen *b = (CaseWhen *) expr2;
+
+ if (!phony_equal(a->expr, b->expr, levelsup))
+ return false;
+ if (!phony_equal(a->result, b->result, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, Expr))
+ {
+ Expr *a = (Expr *) expr1;
+ Expr *b = (Expr *) expr2;
+
+ if (a->opType != b->opType)
+ return false;
+ if (!phony_equal(a->oper, b->oper, levelsup))
+ return false;
+ if (!phony_equal((Node *) a->args, (Node *) b->args, levelsup))
+ return false;
+ return true;
+ }
+ if (IsA(expr1, Func))
+ {
+ Func *a = (Func *) expr1;
+ Func *b = (Func *) expr2;
+
+ if (a->funcid != b->funcid)
+ return false;
+ if (a->functype != b->functype)
+ return false;
+ return true;
+ }
+ if (IsA(expr1, List))
+ {
+ List *la = (List *) expr1;
+ List *lb = (List *) expr2;
+ List *l;
+
+ if (length(la) != length(lb))
+ return false;
+ foreach(l, la)
+ {
+ if (!phony_equal(lfirst(l), lfirst(lb), levelsup))
+ return false;
+ lb = lnext(lb);
+ }
+ return true;
+ }
+ /* If we get here, there was something weird in a JOIN's colvars list */
+ elog(ERROR, "phony_equal: unexpected node type %d", nodeTag(expr1));
+ return false;
+}
/* ----------
* get_rule_expr - Parse back an expression
@@ -1381,21 +1699,21 @@ get_rule_expr(Node *node, deparse_context *context)
case T_Var:
{
Var *var = (Var *) node;
- RangeTblEntry *rte = get_rte_for_var(var, context);
+ char *refname;
+ char *attname;
+ get_names_for_var(var, context, &refname, &attname);
if (context->varprefix)
{
- if (strcmp(rte->eref->relname, "*NEW*") == 0)
+ if (strcmp(refname, "*NEW*") == 0)
appendStringInfo(buf, "new.");
- else if (strcmp(rte->eref->relname, "*OLD*") == 0)
+ else if (strcmp(refname, "*OLD*") == 0)
appendStringInfo(buf, "old.");
else
appendStringInfo(buf, "%s.",
- quote_identifier(rte->eref->relname));
+ quote_identifier(refname));
}
- appendStringInfo(buf, "%s",
- quote_identifier(get_rte_attribute_name(rte,
- var->varattno)));
+ appendStringInfo(buf, "%s", quote_identifier(attname));
}
break;
@@ -1606,6 +1924,19 @@ get_rule_expr(Node *node, deparse_context *context)
{
CaseExpr *caseexpr = (CaseExpr *) node;
List *temp;
+ char *refname;
+ char *attname;
+
+ /* Hack for providing aliases for FULL JOIN outputs */
+ if (get_alias_for_case(caseexpr, context,
+ &refname, &attname))
+ {
+ if (context->varprefix)
+ appendStringInfo(buf, "%s.",
+ quote_identifier(refname));
+ appendStringInfo(buf, "%s", quote_identifier(attname));
+ break;
+ }
appendStringInfo(buf, "CASE");
foreach(temp, caseexpr->args)
@@ -1645,6 +1976,7 @@ get_func_expr(Expr *expr, deparse_context *context)
{
StringInfo buf = context->buf;
Func *func = (Func *) (expr->oper);
+ Oid funcoid = func->funcid;
HeapTuple proctup;
Form_pg_proc procStruct;
char *proname;
@@ -1653,42 +1985,37 @@ get_func_expr(Expr *expr, deparse_context *context)
char *sep;
/*
+ * nullvalue() and nonnullvalue() should get turned into special
+ * syntax
+ */
+ if (funcoid == F_NULLVALUE)
+ {
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) lfirst(expr->args), context);
+ appendStringInfo(buf, " ISNULL)");
+ return;
+ }
+ if (funcoid == F_NONNULLVALUE)
+ {
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) lfirst(expr->args), context);
+ appendStringInfo(buf, " NOTNULL)");
+ return;
+ }
+
+ /*
* Get the functions pg_proc tuple
*/
proctup = SearchSysCache(PROCOID,
- ObjectIdGetDatum(func->funcid),
+ ObjectIdGetDatum(funcoid),
0, 0, 0);
if (!HeapTupleIsValid(proctup))
- elog(ERROR, "cache lookup for proc %u failed", func->funcid);
+ elog(ERROR, "cache lookup for proc %u failed", funcoid);
procStruct = (Form_pg_proc) GETSTRUCT(proctup);
proname = NameStr(procStruct->proname);
/*
- * nullvalue() and nonnullvalue() should get turned into special
- * syntax
- */
- if (procStruct->pronargs == 1 && procStruct->proargtypes[0] == InvalidOid)
- {
- if (strcmp(proname, "nullvalue") == 0)
- {
- appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) lfirst(expr->args), context);
- appendStringInfo(buf, " ISNULL)");
- ReleaseSysCache(proctup);
- return;
- }
- if (strcmp(proname, "nonnullvalue") == 0)
- {
- appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) lfirst(expr->args), context);
- appendStringInfo(buf, " NOTNULL)");
- ReleaseSysCache(proctup);
- return;
- }
- }
-
- /*
* Check to see if function is a length-coercion function for some
* datatype. If so, display the operation as a type cast.
*/
@@ -1968,7 +2295,7 @@ get_sublink_expr(Node *node, deparse_context *context)
if (need_paren)
appendStringInfoChar(buf, '(');
- get_query_def(query, buf, context->rangetables);
+ get_query_def(query, buf, context->namespaces);
if (need_paren)
appendStringInfo(buf, "))");
@@ -2024,6 +2351,16 @@ static void
get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
+ deparse_namespace *dpns;
+ List *sv_namespace;
+
+ /*
+ * FROM-clause items have limited visibility of query's namespace.
+ * Save and restore the outer namespace setting while we munge it.
+ */
+ dpns = (deparse_namespace *) lfirst(context->namespaces);
+ sv_namespace = dpns->namespace;
+ dpns->namespace = NIL;
if (IsA(jtnode, RangeTblRef))
{
@@ -2042,7 +2379,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
/* Subquery RTE */
Assert(rte->subquery != NULL);
appendStringInfoChar(buf, '(');
- get_query_def(rte->subquery, buf, context->rangetables);
+ get_query_def(rte->subquery, buf, context->namespaces);
appendStringInfoChar(buf, ')');
}
if (rte->alias != NULL)
@@ -2053,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
List *col;
- appendStringInfo(buf, " (");
+ appendStringInfo(buf, "(");
foreach(col, rte->alias->attrs)
{
if (col != rte->alias->attrs)
@@ -2116,6 +2453,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
}
else if (j->quals)
{
+ dpns->namespace = makeList2(j->larg, j->rarg);
appendStringInfo(buf, " ON (");
get_rule_expr(j->quals, context);
appendStringInfoChar(buf, ')');
@@ -2131,7 +2469,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
List *col;
- appendStringInfo(buf, " (");
+ appendStringInfo(buf, "(");
foreach(col, j->alias->attrs)
{
if (col != j->alias->attrs)
@@ -2146,6 +2484,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
else
elog(ERROR, "get_from_clause_item: unexpected node type %d",
nodeTag(jtnode));
+
+ dpns->namespace = sv_namespace;
}