mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
FOR UPDATE is in parser & rules.
This commit is contained in:
parent
c8ae6afd13
commit
12be3e08f1
@ -34,7 +34,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# IDENTIFICATION
|
||||||
# $Header: /cvsroot/pgsql/src/backend/Makefile,v 1.35 1999/01/17 06:18:11 momjian Exp $
|
# $Header: /cvsroot/pgsql/src/backend/Makefile,v 1.36 1999/01/21 16:08:36 vadim Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -111,9 +111,6 @@ catalog/global1.description catalog/local1_template1.description:
|
|||||||
postgres.o: $(OBJS)
|
postgres.o: $(OBJS)
|
||||||
$(CC) -r -o postgres.o $(OBJS) $(LDFLAGS)
|
$(CC) -r -o postgres.o $(OBJS) $(LDFLAGS)
|
||||||
|
|
||||||
fast:
|
|
||||||
$(CC) -r -o postgres.o $(OBJS) $(LDFLAGS)
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# The following targets are specified in make commands that appear in the
|
# The following targets are specified in make commands that appear in the
|
||||||
# make files in our subdirectories.
|
# make files in our subdirectories.
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: outfuncs.c,v 1.59 1999/01/18 00:09:45 momjian Exp $
|
* $Id: outfuncs.c,v 1.60 1999/01/21 16:08:36 vadim Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||||
@ -162,6 +162,7 @@ _outIndexElem(StringInfo str, IndexElem *node)
|
|||||||
static void
|
static void
|
||||||
_outQuery(StringInfo str, Query *node)
|
_outQuery(StringInfo str, Query *node)
|
||||||
{
|
{
|
||||||
|
|
||||||
appendStringInfo(str, " QUERY :command %d ", node->commandType);
|
appendStringInfo(str, " QUERY :command %d ", node->commandType);
|
||||||
|
|
||||||
if (node->utilityStmt)
|
if (node->utilityStmt)
|
||||||
@ -235,6 +236,10 @@ _outQuery(StringInfo str, Query *node)
|
|||||||
|
|
||||||
appendStringInfo(str, " :limitCount ");
|
appendStringInfo(str, " :limitCount ");
|
||||||
_outNode(str, node->limitCount);
|
_outNode(str, node->limitCount);
|
||||||
|
|
||||||
|
appendStringInfo(str, " :rowMark ");
|
||||||
|
_outNode(str, node->rowMark);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -907,6 +912,12 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
|
|||||||
node->skipAcl ? "true" : "false");
|
node->skipAcl ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_outRowMark(StringInfo str, RowMark *node)
|
||||||
|
{
|
||||||
|
appendStringInfo(str, " ROWMARK :rti %u :info %u", node->rti, node->info);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Path is a subclass of Node.
|
* Path is a subclass of Node.
|
||||||
*/
|
*/
|
||||||
@ -1528,6 +1539,9 @@ _outNode(StringInfo str, void *obj)
|
|||||||
case T_RangeTblEntry:
|
case T_RangeTblEntry:
|
||||||
_outRangeTblEntry(str, obj);
|
_outRangeTblEntry(str, obj);
|
||||||
break;
|
break;
|
||||||
|
case T_RowMark:
|
||||||
|
_outRowMark(str, obj);
|
||||||
|
break;
|
||||||
case T_Path:
|
case T_Path:
|
||||||
_outPath(str, obj);
|
_outPath(str, obj);
|
||||||
break;
|
break;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.41 1999/01/18 00:09:46 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.42 1999/01/21 16:08:37 vadim Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||||
@ -174,6 +174,9 @@ _readQuery()
|
|||||||
token = lsptok(NULL, &length); /* skip :limitCount */
|
token = lsptok(NULL, &length); /* skip :limitCount */
|
||||||
local_node->limitCount = nodeRead(true);
|
local_node->limitCount = nodeRead(true);
|
||||||
|
|
||||||
|
token = lsptok(NULL, &length); /* skip :rowMark */
|
||||||
|
local_node->rowMark = nodeRead(true);
|
||||||
|
|
||||||
return local_node;
|
return local_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1427,6 +1430,24 @@ _readRangeTblEntry()
|
|||||||
return local_node;
|
return local_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RowMark *
|
||||||
|
_readRowMark()
|
||||||
|
{
|
||||||
|
RowMark *local_node = makeNode(RowMark);
|
||||||
|
char *token;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
token = lsptok(NULL, &length); /* eat :rti */
|
||||||
|
token = lsptok(NULL, &length); /* get :rti */
|
||||||
|
local_node->rti = strtoul(token, NULL, 10);
|
||||||
|
|
||||||
|
token = lsptok(NULL, &length); /* eat :info */
|
||||||
|
token = lsptok(NULL, &length); /* get :info */
|
||||||
|
local_node->info = strtoul(token, NULL, 10);
|
||||||
|
|
||||||
|
return local_node;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* _readPath
|
* _readPath
|
||||||
*
|
*
|
||||||
@ -2090,6 +2111,8 @@ parsePlanString(void)
|
|||||||
return_value = _readCaseExpr();
|
return_value = _readCaseExpr();
|
||||||
else if (!strncmp(token, "WHEN", length))
|
else if (!strncmp(token, "WHEN", length))
|
||||||
return_value = _readCaseWhen();
|
return_value = _readCaseWhen();
|
||||||
|
else if (!strncmp(token, "ROWMARK", length))
|
||||||
|
return_value = _readRowMark();
|
||||||
else
|
else
|
||||||
elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
|
elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: analyze.c,v 1.92 1999/01/18 00:09:49 momjian Exp $
|
* $Id: analyze.c,v 1.93 1999/01/21 16:08:38 vadim Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -45,6 +45,8 @@ static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
|
|||||||
static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
|
static Query *transformCursorStmt(ParseState *pstate, SelectStmt *stmt);
|
||||||
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
|
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
|
||||||
|
|
||||||
|
static void transformForUpdate(Query *qry, List *forUpdate);
|
||||||
|
|
||||||
List *extras_before = NIL;
|
List *extras_before = NIL;
|
||||||
List *extras_after = NIL;
|
List *extras_after = NIL;
|
||||||
|
|
||||||
@ -387,7 +389,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|||||||
/*
|
/*
|
||||||
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
* The INSERT INTO ... SELECT ... could have a UNION in child, so
|
||||||
* unionClause may be false
|
* unionClause may be false
|
||||||
*/
|
, */
|
||||||
qry->unionall = stmt->unionall;
|
qry->unionall = stmt->unionall;
|
||||||
|
|
||||||
/***S*I***/
|
/***S*I***/
|
||||||
@ -408,6 +410,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
|
|||||||
return (Query *) NIL;
|
return (Query *) NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stmt->forUpdate != NULL)
|
||||||
|
transformForUpdate(qry, stmt->forUpdate);
|
||||||
|
|
||||||
return (Query *) qry;
|
return (Query *) qry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -971,6 +976,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|||||||
return (Query *) NIL;
|
return (Query *) NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stmt->forUpdate != NULL)
|
||||||
|
transformForUpdate(qry, stmt->forUpdate);
|
||||||
|
|
||||||
return (Query *) qry;
|
return (Query *) qry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1121,3 +1129,59 @@ Node *A_Expr_to_Expr(Node *ptr, bool *intersect_present)
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
transformForUpdate(Query *qry, List *forUpdate)
|
||||||
|
{
|
||||||
|
List *rowMark = NULL;
|
||||||
|
RowMark *newrm;
|
||||||
|
List *l;
|
||||||
|
Index i;
|
||||||
|
|
||||||
|
if (lfirst(forUpdate) == NULL) /* all tables */
|
||||||
|
{
|
||||||
|
i = 1;
|
||||||
|
foreach (l, qry->rtable)
|
||||||
|
{
|
||||||
|
newrm = makeNode(RowMark);
|
||||||
|
newrm->rti = i++;
|
||||||
|
newrm->info = ROW_MARK_FOR_UPDATE|ROW_ACL_FOR_UPDATE;
|
||||||
|
rowMark = lappend(rowMark, newrm);
|
||||||
|
}
|
||||||
|
qry->rowMark = nconc(qry->rowMark, rowMark);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (l, forUpdate)
|
||||||
|
{
|
||||||
|
List *l2;
|
||||||
|
List *l3;
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
foreach (l2, qry->rtable)
|
||||||
|
{
|
||||||
|
if (strcmp(((RangeTblEntry*)lfirst(l2))->refname, lfirst(l)) == 0)
|
||||||
|
{
|
||||||
|
foreach (l3, rowMark)
|
||||||
|
{
|
||||||
|
if (((RowMark*)lfirst(l3))->rti == i) /* duplicate */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (l3 == NULL)
|
||||||
|
{
|
||||||
|
newrm = makeNode(RowMark);
|
||||||
|
newrm->rti = i;
|
||||||
|
newrm->info = ROW_MARK_FOR_UPDATE|ROW_ACL_FOR_UPDATE;
|
||||||
|
rowMark = lappend(rowMark, newrm);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (l2 == NULL)
|
||||||
|
elog(ERROR, "FOR UPDATE: relation %s not found in FROM clause", lfirst(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
qry->rowMark = rowMark;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.46 1999/01/20 19:48:13 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.47 1999/01/21 16:08:46 vadim Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -2496,6 +2496,7 @@ insert_rest: VALUES '(' res_target_list2 ')'
|
|||||||
$$->havingClause = n->havingClause;
|
$$->havingClause = n->havingClause;
|
||||||
$$->unionClause = n->unionClause;
|
$$->unionClause = n->unionClause;
|
||||||
$$->intersectClause = n->intersectClause;
|
$$->intersectClause = n->intersectClause;
|
||||||
|
$$->forUpdate = n->forUpdate;
|
||||||
}
|
}
|
||||||
| '(' columnList ')' VALUES '(' res_target_list2 ')'
|
| '(' columnList ')' VALUES '(' res_target_list2 ')'
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.28 1999/01/18 00:09:54 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.29 1999/01/21 16:08:48 vadim Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1799,7 +1799,8 @@ ApplyRetrieveRule(Query *parsetree,
|
|||||||
Query *rule_action = NULL;
|
Query *rule_action = NULL;
|
||||||
Node *rule_qual;
|
Node *rule_qual;
|
||||||
List *rtable,
|
List *rtable,
|
||||||
*rt;
|
*rt,
|
||||||
|
*l;
|
||||||
int nothing,
|
int nothing,
|
||||||
rt_length;
|
rt_length;
|
||||||
int badsql = FALSE;
|
int badsql = FALSE;
|
||||||
@ -1834,6 +1835,43 @@ ApplyRetrieveRule(Query *parsetree,
|
|||||||
rtable = nconc(rtable, copyObject(rule_action->rtable));
|
rtable = nconc(rtable, copyObject(rule_action->rtable));
|
||||||
parsetree->rtable = rtable;
|
parsetree->rtable = rtable;
|
||||||
|
|
||||||
|
/* FOR UPDATE of view... */
|
||||||
|
foreach (l, parsetree->rowMark)
|
||||||
|
{
|
||||||
|
if (((RowMark*)lfirst(l))->rti == rt_index)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (l != NULL) /* oh, hell -:) */
|
||||||
|
{
|
||||||
|
RowMark *newrm;
|
||||||
|
Index rti = 1;
|
||||||
|
List *l2;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We believe that rt_index is VIEW - nothing should be
|
||||||
|
* marked for VIEW, but ACL check must be done.
|
||||||
|
* As for real tables of VIEW - their rows must be marked, but
|
||||||
|
* we have to skip ACL check for them.
|
||||||
|
*/
|
||||||
|
((RowMark*)lfirst(l))->info &= ~ROW_MARK_FOR_UPDATE;
|
||||||
|
foreach (l2, rule_action->rtable)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* RTable of VIEW has two entries of VIEW itself -
|
||||||
|
* we use relid to skip them.
|
||||||
|
*/
|
||||||
|
if (relation->rd_id != ((RangeTblEntry*)lfirst(l2))->relid)
|
||||||
|
{
|
||||||
|
newrm = makeNode(RowMark);
|
||||||
|
newrm->rti = rti + rt_length;
|
||||||
|
newrm->info = ROW_MARK_FOR_UPDATE;
|
||||||
|
lnext(l) = lcons(newrm, lnext(l));
|
||||||
|
l = lnext(l);
|
||||||
|
}
|
||||||
|
rti++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rule_action->rtable = rtable;
|
rule_action->rtable = rtable;
|
||||||
OffsetVarNodes((Node *) rule_qual, rt_length, 0);
|
OffsetVarNodes((Node *) rule_qual, rt_length, 0);
|
||||||
OffsetVarNodes((Node *) rule_action, rt_length, 0);
|
OffsetVarNodes((Node *) rule_action, rt_length, 0);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.37 1999/01/17 03:28:37 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.38 1999/01/21 16:08:51 vadim Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -63,6 +63,9 @@
|
|||||||
#include "utils/builtins.h" /* for ftod() prototype */
|
#include "utils/builtins.h" /* for ftod() prototype */
|
||||||
#include "utils/palloc.h"
|
#include "utils/palloc.h"
|
||||||
|
|
||||||
|
#ifndef NAN
|
||||||
|
#define NAN (0.0/0.0)
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef SHRT_MAX
|
#ifndef SHRT_MAX
|
||||||
#define SHRT_MAX 32767
|
#define SHRT_MAX 32767
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: nodes.h,v 1.33 1998/12/18 09:09:53 vadim Exp $
|
* $Id: nodes.h,v 1.34 1999/01/21 16:08:53 vadim Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -216,7 +216,8 @@ typedef enum NodeTag
|
|||||||
T_SubSelect,
|
T_SubSelect,
|
||||||
T_JoinUsing,
|
T_JoinUsing,
|
||||||
T_CaseExpr,
|
T_CaseExpr,
|
||||||
T_CaseWhen
|
T_CaseWhen,
|
||||||
|
T_RowMark
|
||||||
} NodeTag;
|
} NodeTag;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parsenodes.h,v 1.66 1999/01/18 00:10:06 momjian Exp $
|
* $Id: parsenodes.h,v 1.67 1999/01/21 16:08:55 vadim Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -53,6 +53,7 @@ typedef struct Query
|
|||||||
List *rtable; /* list of range table entries */
|
List *rtable; /* list of range table entries */
|
||||||
List *targetList; /* target list (of TargetEntry) */
|
List *targetList; /* target list (of TargetEntry) */
|
||||||
Node *qual; /* qualifications */
|
Node *qual; /* qualifications */
|
||||||
|
List *rowMark; /* list of RowMark entries */
|
||||||
|
|
||||||
List *groupClause; /* list of columns to specified in GROUP
|
List *groupClause; /* list of columns to specified in GROUP
|
||||||
* BY */
|
* BY */
|
||||||
@ -608,9 +609,10 @@ typedef struct InsertStmt
|
|||||||
List *groupClause; /* group by clause */
|
List *groupClause; /* group by clause */
|
||||||
Node *havingClause; /* having conditional-expression */
|
Node *havingClause; /* having conditional-expression */
|
||||||
List *unionClause; /* union subselect parameters */
|
List *unionClause; /* union subselect parameters */
|
||||||
bool unionall; /* union without unique sort */
|
bool unionall; /* union without unique sort */
|
||||||
/***S*I***/
|
/***S*I***/
|
||||||
List *intersectClause;
|
List *intersectClause;
|
||||||
|
List *forUpdate; /* FOR UPDATE clause */
|
||||||
} InsertStmt;
|
} InsertStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
@ -651,10 +653,10 @@ typedef struct SelectStmt
|
|||||||
Node *whereClause; /* qualifications */
|
Node *whereClause; /* qualifications */
|
||||||
List *groupClause; /* group by clause */
|
List *groupClause; /* group by clause */
|
||||||
Node *havingClause; /* having conditional-expression */
|
Node *havingClause; /* having conditional-expression */
|
||||||
/***S*I***/
|
/***S*I***/
|
||||||
List *intersectClause;
|
List *intersectClause;
|
||||||
List *exceptClause;
|
List *exceptClause;
|
||||||
|
|
||||||
List *unionClause; /* union subselect parameters */
|
List *unionClause; /* union subselect parameters */
|
||||||
List *sortClause; /* sort clause (a list of SortGroupBy's) */
|
List *sortClause; /* sort clause (a list of SortGroupBy's) */
|
||||||
char *portalname; /* the portal (cursor) to create */
|
char *portalname; /* the portal (cursor) to create */
|
||||||
@ -958,4 +960,14 @@ typedef struct GroupClause
|
|||||||
Oid grpOpoid; /* the sort operator to use */
|
Oid grpOpoid; /* the sort operator to use */
|
||||||
} GroupClause;
|
} GroupClause;
|
||||||
|
|
||||||
|
#define ROW_MARK_FOR_UPDATE (1 << 0)
|
||||||
|
#define ROW_ACL_FOR_UPDATE (1 << 1)
|
||||||
|
|
||||||
|
typedef struct RowMark
|
||||||
|
{
|
||||||
|
NodeTag type;
|
||||||
|
Index rti; /* index in Query->rtable */
|
||||||
|
bits8 info; /* as above */
|
||||||
|
} RowMark;
|
||||||
|
|
||||||
#endif /* PARSENODES_H */
|
#endif /* PARSENODES_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user