diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 169a385..a6334b6 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -10099,6 +10099,47 @@ SELECT xmlroot(xmlparse(document '<?xml version="1.1"?><content>abc</content>'),
     </para>
    </sect3>
 
+   <sect3>
+    <title><literal>xmltable</literal></title>
+
+   <indexterm>
+    <primary>xmltable</primary>
+   </indexterm>
+
+<synopsis>
+<function>xmltable</function>(<optional>xmlnamespaces(<replaceable>namespace uri</replaceable> AS <replaceable>namespace name</replaceable>|DEFAULT <replaceable>namespace uri</replaceable> <optional>, ...</optional>)</optional> <replaceable>rowexpr</replaceable> PASSING <optional>BY REF</optional> <replaceable>xml</replaceable> <optional>BY REF</optional> <optional>COLUMNS <replaceable>name</replaceable> <replaceable>type</replaceable> <optional>PATH <replaceable>columnexpr</replaceable></optional> <optional>DEFAULT <replaceable>expr</replaceable></optional> <optional>NOT NULL|NULL</optional> <optional>, ...</optional></optional>)
+</synopsis>
+
+    <para>
+      The <function>xmltable</function> produces table based on passed XML value.
+    </para>
+
+    <para>
+<screen><![CDATA[
+SELECT  xmltable.*
+  FROM (SELECT data FROM xmldata) x,
+        LATERAL xmltable('//ROWS/ROW'
+                         PASSING data
+                         COLUMNS id int PATH '@id',
+                                  country_name text PATH 'COUNTRY_NAME',
+                                  country_id text PATH 'COUNTRY_ID',
+                                  region_id int PATH 'REGION_ID',
+                                  size float PATH 'SIZE[@unit = "km"]/text()',
+                                  unit text PATH 'SIZE/@unit',
+                                  premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified');
+
+ id | country_name | country_id | region_id | size | unit | premier_name  
+----+--------------+------------+-----------+------+------+---------------
+  1 | Australia    | AU         |         3 |      |      | not specified
+  2 | China        | CN         |         3 |      |      | not specified
+  3 | HongKong     | HK         |         3 |      |      | not specified
+  4 | India        | IN         |         3 |      |      | not specified
+  5 | Japan        | JP         |         3 |      |      | Sinzo Abe
+  6 | Singapore    | SG         |         3 |  791 | km   | not specified
+]]></screen>
+    </para>
+   </sect3>
+
    <sect3 id="functions-xml-xmlagg">
     <title><literal>xmlagg</literal></title>
 
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..77a06da 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -189,6 +189,9 @@ static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
 						 ExprContext *econtext,
 						 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalTableExpr(TableExprState *tstate,
+						ExprContext *econtext,
+						bool *isnull, ExprDoneCond *isDone);
 
 
 /* ----------------------------------------------------------------
@@ -4500,6 +4503,203 @@ ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
 	return 0;					/* keep compiler quiet */
 }
 
+/* ----------------------------------------------------------------
+ *		ExecEvalTableExpr
+ *
+ * ----------------------------------------------------------------
+ */
+
+#define XMLTABLE_DEFAULT_NAMESPACE			"pgdefxmlns"
+
+static Datum
+execEvalTableExpr(TableExprState *tstate,
+						ExprContext *econtext,
+						bool *isNull, ExprDoneCond *isDone)
+{
+	TupleDesc tupdesc;
+	HeapTuple		tuple;
+	HeapTupleHeader		result;
+	int					i;
+	Datum		*values;
+	bool		*nulls;
+	Datum	value;
+	bool	isnull;
+	xmltype		   *xmlval;
+	text		   *row_path;
+
+	tupdesc = tstate->tupdesc;
+
+	if (tstate->firstRow)
+	{
+		ListCell	*res;
+
+		/* Evaluate expression first */
+		value = ExecEvalExpr(tstate->expr, econtext, &isnull, NULL);
+		if (isnull)
+		{
+			*isDone = ExprSingleResult;
+			*isNull = true;
+			return (Datum) 0;
+		}
+		xmlval = DatumGetXmlP(value);
+
+		value = ExecEvalExpr(tstate->row_path_expr, econtext, &isnull, NULL);
+		if (isnull)
+				ereport(ERROR,
+						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					  errmsg("row query must not be null")));
+		row_path = DatumGetTextP(value);
+
+		Assert(tstate->xmltableCxt == NULL);
+		tstate->xmltableCxt = initXmlTableContext(xmlval,
+													tstate->used_dns ?
+										XMLTABLE_DEFAULT_NAMESPACE : NULL,
+													tstate->ncols,
+															  tstate->in_functions,
+															  tstate->typioparams,
+													econtext->ecxt_per_query_memory);
+
+		foreach(res, tstate->namespaces)
+		{
+			ResTarget *rt = (ResTarget *) lfirst(res);
+			char	*ns_uri;
+
+			value = ExecEvalExpr((ExprState *) rt->val, econtext, &isnull, NULL);
+			if (isnull)
+				ereport(ERROR,
+						(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+					  errmsg("namespace uri must not be null")));
+			ns_uri = text_to_cstring(DatumGetTextP(value));
+
+			XmlTableSetNs(tstate->xmltableCxt, rt->name, ns_uri);
+		}
+
+		XmlTableSetRowPath(tstate->xmltableCxt, row_path);
+
+		/* Path caclulation */
+		for (i = 0; i < tstate->ncols; i++)
+		{
+			char *col_path;
+
+			if (tstate->col_path_expr[i] != NULL)
+			{
+				value = ExecEvalExpr(tstate->col_path_expr[i], econtext, &isnull, NULL);
+				if (isnull)
+					ereport(ERROR,
+							(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+						  errmsg("column path for column \"%s\" must not be null",
+											  NameStr(tupdesc->attrs[i]->attname))));
+				col_path = text_to_cstring(DatumGetTextP(value));
+			}
+			else
+				col_path = NameStr(tupdesc->attrs[i]->attname);
+
+			XmlTableSetColumnPath(tstate->xmltableCxt, i,
+											tupdesc->attrs[i]->atttypid, col_path);
+		}
+		tstate->firstRow = false;
+	}
+
+	values = tstate->values;
+	nulls = tstate->nulls;
+
+	if (XmlTableFetchRow(tstate->xmltableCxt))
+	{
+		if (tstate->ncols > 0)
+		{
+			for (i = 0; i < tstate->ncols; i++)
+			{
+				if (i != tstate->for_ordinality_col - 1)
+				{
+					values[i] = XmlTableGetValue(tstate->xmltableCxt, i,
+												  tupdesc->attrs[i]->atttypid,
+												  tupdesc->attrs[i]->atttypmod,
+																			  &isnull);
+					if (isnull && tstate->def_expr[i] != NULL)
+						values[i] = ExecEvalExpr(tstate->def_expr[i], econtext, &isnull, NULL);
+
+					if (isnull && tstate->not_null[i])
+						ereport(ERROR,
+								(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+								 errmsg("null is not allowed in column \"%s\"",
+									  NameStr(tupdesc->attrs[i]->attname))));
+					nulls[i] = isnull;
+				}
+				else
+				{
+					values[i] = Int32GetDatum(++tstate->rownum);
+					nulls[i] = false;
+				}
+			}
+		}
+		else
+			values[0] = XmlTableGetRowValue(tstate->xmltableCxt, &nulls[0]);
+
+		tuple = heap_form_tuple(tupdesc, values, nulls);
+		result = (HeapTupleHeader) palloc(tuple->t_len);
+		memcpy(result, tuple->t_data, tuple->t_len);
+
+		/*
+		 * Label the datum with the composite type info we identified before.
+		 */
+		HeapTupleHeaderSetTypeId(result, tupdesc->tdtypeid);
+		HeapTupleHeaderSetTypMod(result, tupdesc->tdtypmod);
+
+		heap_freetuple(tuple);
+	}
+	else
+	{
+		/* no more rows */
+		XmlTableFreeContext(tstate->xmltableCxt);
+		tstate->xmltableCxt = NULL;
+
+		/* ensure releasing all memory */
+		MemoryContextReset(tstate->per_rowgroup_memory);
+
+		/* next row will be first again */
+		tstate->firstRow = true;
+
+		*isNull = true;
+		*isDone = ExprEndResult;
+
+		return (Datum) 0;
+	}
+
+	*isNull = false;
+	*isDone = ExprMultipleResult;
+
+	return HeapTupleHeaderGetDatum(result);
+}
+
+static Datum
+ExecEvalTableExpr(TableExprState *tstate,
+						ExprContext *econtext,
+						bool *isNull, ExprDoneCond *isDone)
+{
+	/* Ensure releasing context every exception */
+	Datum		result;
+
+	PG_TRY();
+	{
+		result = execEvalTableExpr(tstate, econtext, isNull, isDone);
+	}
+	PG_CATCH();
+	{
+		if (tstate->xmltableCxt != NULL)
+		{
+			XmlTableFreeContext(tstate->xmltableCxt);
+			tstate->xmltableCxt = NULL;
+		}
+
+		MemoryContextDelete(tstate->per_rowgroup_memory);
+		tstate->per_rowgroup_memory = NULL;
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	return result;
+}
 
 /*
  * ExecEvalExprSwitchContext
@@ -5262,6 +5462,132 @@ ExecInitExpr(Expr *node, PlanState *parent)
 				/* Don't fall through to the "common" code below */
 				return (ExprState *) outlist;
 			}
+		case T_TableExpr:
+			{
+				TableExpr	   *te = (TableExpr *) node;
+				TableExprState *tstate = makeNode(TableExprState);
+				int				ncols;
+				ListCell	   *col;
+				TupleDesc		tupdesc;
+				int				i;
+
+				tstate->firstRow = true;
+				tstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalTableExpr;
+
+				/* when typmod is not valid, refresh it */
+				if (te->typmod == -1)
+				{
+					TupleDesc tupdesc = TableExprGetTupleDesc(te);
+					tstate->typid = tupdesc->tdtypeid;
+					tstate->typmod = tupdesc->tdtypmod;
+				}
+				else
+				{
+					tstate->typid = te->typid;
+					tstate->typmod = te->typmod;
+				}
+
+				tupdesc = lookup_rowtype_tupdesc_copy(tstate->typid, tstate->typmod);
+				ncols = tupdesc->natts;
+				tstate->tupdesc = tupdesc;
+
+				/* result is one more columns every time */
+				Assert(ncols > 0);
+
+				tstate->values = palloc(sizeof(Datum) * ncols);
+				tstate->nulls = palloc(sizeof(bool) * ncols);
+				tstate->in_functions = palloc(sizeof(FmgrInfo) * ncols);
+				tstate->typioparams = palloc(sizeof(Oid) * ncols);
+
+				for (i = 0; i < ncols; i++)
+				{
+					Oid		in_funcid;
+
+					getTypeInputInfo(tupdesc->attrs[i]->atttypid, &in_funcid,
+														&tstate->typioparams[i]);
+					fmgr_info(in_funcid, &tstate->in_functions[i]);
+				}
+
+				tstate->row_path_expr = ExecInitExpr((Expr *) te->row_path, parent);
+				tstate->expr = ExecInitExpr((Expr *) te->expr, parent);
+
+				if (te->cols)
+				{
+					Assert(ncols == list_length(te->cols));
+
+					tstate->def_expr = palloc0(sizeof(ExprState *) * ncols);
+					tstate->col_path_expr = palloc0(sizeof(ExprState *) * ncols);
+					tstate->not_null = palloc0(sizeof(bool) * ncols);
+					tstate->ncols = ncols;
+
+					i = 0;
+					foreach(col, te->cols)
+					{
+						ColumnDef *column = (ColumnDef *) lfirst(col);
+
+						if (column->typeName != NULL)
+						{
+							if (column->cooked_default != NULL)
+								tstate->def_expr[i] = ExecInitExpr((Expr *) column->cooked_default,
+																	parent);
+
+							if (column->cooked_path != NULL)
+								tstate->col_path_expr[i] = ExecInitExpr((Expr *) column->cooked_path,
+																	parent);
+
+							tstate->not_null[i] = column->is_not_null;
+						}
+						else
+						{
+							/* For ordinality column found */
+							tstate->for_ordinality_col = i + 1;
+						}
+						i++;
+					}
+					tstate->rownum = 0;
+				}
+				else
+				{
+					/* There are not any related data */
+					tstate->def_expr = NULL;
+					tstate->col_path_expr = NULL;
+					tstate->not_null = NULL;
+					tstate->ncols = 0;
+				}
+
+				if (te->namespaces)
+				{
+					List		*preparedlist = NIL;
+					ListCell	*res;
+
+					foreach(res, te->namespaces)
+					{
+						ResTarget *rt = (ResTarget *) lfirst(res);
+						ResTarget *prt = makeNode(ResTarget);
+
+						if (rt->name == NULL)
+							tstate->used_dns = true;
+
+						prt->name = rt->name;
+						prt->val = (Node *) ExecInitExpr((Expr *) rt->val, parent);
+						prt->location = rt->location;
+						preparedlist = lappend(preparedlist, prt);
+					}
+					tstate->namespaces = preparedlist;
+				}
+				else
+					tstate->namespaces = NIL;
+
+				tstate->xmltableCxt = NULL;
+				tstate->per_rowgroup_memory = AllocSetContextCreate(CurrentMemoryContext,
+																		"XmlTable per rowgroup context",
+																		ALLOCSET_DEFAULT_MINSIZE,
+																		ALLOCSET_DEFAULT_INITSIZE,
+																		ALLOCSET_DEFAULT_MAXSIZE);
+
+				state = (ExprState *) tstate;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c7a0644..f1bbc4c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1986,6 +1986,25 @@ _copyOnConflictExpr(const OnConflictExpr *from)
 	return newnode;
 }
 
+/*
+ * _copyTableExpr
+ */
+static TableExpr *
+_copyTableExpr(const TableExpr *from)
+{
+	TableExpr *newnode = makeNode(TableExpr);
+
+	COPY_SCALAR_FIELD(typid);
+	COPY_SCALAR_FIELD(typmod);
+	COPY_NODE_FIELD(row_path);
+	COPY_NODE_FIELD(expr);
+	COPY_NODE_FIELD(cols);
+	COPY_NODE_FIELD(namespaces);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *						relation.h copy functions
  *
@@ -2623,6 +2642,8 @@ _copyColumnDef(const ColumnDef *from)
 	COPY_SCALAR_FIELD(storage);
 	COPY_NODE_FIELD(raw_default);
 	COPY_NODE_FIELD(cooked_default);
+	COPY_NODE_FIELD(raw_path);
+	COPY_NODE_FIELD(cooked_path);
 	COPY_NODE_FIELD(collClause);
 	COPY_SCALAR_FIELD(collOid);
 	COPY_NODE_FIELD(constraints);
@@ -4583,6 +4604,9 @@ copyObject(const void *from)
 		case T_OnConflictExpr:
 			retval = _copyOnConflictExpr(from);
 			break;
+		case T_TableExpr:
+			retval = _copyTableExpr(from);
+			break;
 
 			/*
 			 * RELATION NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 448e1a9..6a1b9a0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2376,6 +2376,8 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
 	COMPARE_SCALAR_FIELD(storage);
 	COMPARE_NODE_FIELD(raw_default);
 	COMPARE_NODE_FIELD(cooked_default);
+	COMPARE_NODE_FIELD(raw_path);
+	COMPARE_NODE_FIELD(cooked_path);
 	COMPARE_NODE_FIELD(collClause);
 	COMPARE_SCALAR_FIELD(collOid);
 	COMPARE_NODE_FIELD(constraints);
@@ -2630,6 +2632,20 @@ _equalRoleSpec(const RoleSpec *a, const RoleSpec *b)
 	return true;
 }
 
+static bool
+_equalTableExpr(const TableExpr *a, const TableExpr *b)
+{
+	COMPARE_SCALAR_FIELD(typid);
+	COMPARE_SCALAR_FIELD(typmod);
+	COMPARE_NODE_FIELD(row_path);
+	COMPARE_NODE_FIELD(expr);
+	COMPARE_NODE_FIELD(cols);
+	COMPARE_NODE_FIELD(namespaces);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -2895,6 +2911,9 @@ equal(const void *a, const void *b)
 		case T_JoinExpr:
 			retval = _equalJoinExpr(a, b);
 			break;
+		case T_TableExpr:
+			retval = _equalTableExpr(a, b);
+			break;
 
 			/*
 			 * RELATION NODES
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index d72a85e..47cc936 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -497,6 +497,8 @@ makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
 	n->storage = 0;
 	n->raw_default = NULL;
 	n->cooked_default = NULL;
+	n->raw_path = NULL;
+	n->cooked_path = NULL;
 	n->collClause = NULL;
 	n->collOid = collOid;
 	n->constraints = NIL;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..522aa93 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -257,6 +257,9 @@ exprType(const Node *expr)
 		case T_PlaceHolderVar:
 			type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_TableExpr:
+			type = ((const TableExpr *) expr)->typid;
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			type = InvalidOid;	/* keep compiler quiet */
@@ -492,6 +495,8 @@ exprTypmod(const Node *expr)
 			return ((const SetToDefault *) expr)->typeMod;
 		case T_PlaceHolderVar:
 			return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr);
+		case T_TableExpr:
+			return ((const TableExpr *) expr)->typmod;
 		default:
 			break;
 	}
@@ -727,6 +732,8 @@ expression_returns_set_walker(Node *node, void *context)
 		return false;
 	if (IsA(node, XmlExpr))
 		return false;
+	if (IsA(node, TableExpr))
+		return true;
 
 	return expression_tree_walker(node, expression_returns_set_walker,
 								  context);
@@ -929,6 +936,9 @@ exprCollation(const Node *expr)
 		case T_PlaceHolderVar:
 			coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr);
 			break;
+		case T_TableExpr:
+			coll = InvalidOid;	/* result is composite or XML or JSON */
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			coll = InvalidOid;	/* keep compiler quiet */
@@ -1127,6 +1137,9 @@ exprSetCollation(Node *expr, Oid collation)
 		case T_CurrentOfExpr:
 			Assert(!OidIsValid(collation));		/* result is always boolean */
 			break;
+		case T_TableExpr:
+			Assert(!OidIsValid(collation));		/* result is always composite or XML, .. */
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
 			break;
@@ -1552,6 +1565,9 @@ exprLocation(const Node *expr)
 			/* just use nested expr's location */
 			loc = exprLocation((Node *) ((const InferenceElem *) expr)->expr);
 			break;
+		case T_TableExpr:
+			loc = ((const TableExpr *) expr)->location;
+			break;
 		default:
 			/* for any other node type it's just unknown... */
 			loc = -1;
@@ -2211,6 +2227,36 @@ expression_tree_walker(Node *node,
 					return true;
 			}
 			break;
+		case T_TableExpr:
+			{
+				TableExpr *te = (TableExpr *) node;
+				ListCell	*col;
+				ListCell	*res;
+
+				if (walker(te->row_path, context))
+					return true;
+				if (walker(te->expr, context))
+					return true;
+
+				foreach(col, te->cols)
+				{
+					ColumnDef	*column = (ColumnDef *) lfirst(col);
+
+					if (walker(column->cooked_default, context))
+						return true;
+					if (walker(column->cooked_path, context))
+						return true;
+				}
+
+				foreach(res, te->namespaces)
+				{
+					ResTarget *rt = (ResTarget *) lfirst(res);
+
+					if (walker(rt->val, context))
+						return true;
+				}
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3007,6 +3053,44 @@ expression_tree_mutator(Node *node,
 				return (Node *) newnode;
 			}
 			break;
+		case T_TableExpr:
+			{
+				TableExpr *te = (TableExpr *) node;
+				TableExpr *newnode;
+				ListCell *col;
+				ListCell *res;
+				List	*resultlist = NIL;
+
+				FLATCOPY(newnode, te, TableExpr);
+				MUTATE(newnode->row_path, te->row_path, Node *);
+				MUTATE(newnode->expr, te->expr, Node *);
+
+				foreach(col, te->cols)
+				{
+					ColumnDef *column = (ColumnDef *) lfirst(col);
+					ColumnDef *newcol;
+
+					FLATCOPY(newcol, column, ColumnDef);
+					MUTATE(newcol->cooked_default, column->cooked_default, Node *);
+					MUTATE(newcol->cooked_path, column->cooked_path, Node *);
+					resultlist = lappend(resultlist, newcol);
+				}
+				newnode->cols = resultlist;
+
+				resultlist = NIL;
+				foreach(res, te->namespaces)
+				{
+					ResTarget *rt = (ResTarget *) lfirst(res);
+					ResTarget *newrt;
+
+					FLATCOPY(newrt, rt, ResTarget);
+					MUTATE(newrt->val, rt->val, Node *);
+					resultlist = lappend(resultlist, newrt);
+				}
+				newnode->namespaces = resultlist;
+
+				return (Node *) newnode;
+			}
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
@@ -3568,6 +3652,8 @@ raw_expression_tree_walker(Node *node,
 					return true;
 				if (walker(coldef->raw_default, context))
 					return true;
+				if (walker(coldef->raw_path, context))
+					return true;
 				if (walker(coldef->collClause, context))
 					return true;
 				/* for now, constraints are ignored */
@@ -3622,6 +3708,20 @@ raw_expression_tree_walker(Node *node,
 			break;
 		case T_CommonTableExpr:
 			return walker(((CommonTableExpr *) node)->ctequery, context);
+		case T_TableExpr:
+			{
+				TableExpr *te = (TableExpr *) node;
+
+				if (walker(te->row_path, context))
+					return true;
+				if (walker(te->expr, context))
+					return true;
+				if (walker(te->cols, context))
+					return true;
+				if (walker(te->namespaces, context))
+					return true;
+			}
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) nodeTag(node));
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1fab807..851ae17 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1587,6 +1587,20 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node)
 	WRITE_NODE_FIELD(exclRelTlist);
 }
 
+static void
+_outTableExpr(StringInfo str, const TableExpr *node)
+{
+	WRITE_NODE_TYPE("TABLEEXPR");
+
+	WRITE_OID_FIELD(typid);
+	/* skip typmod, should not be persistent for dynamic RECORD */
+	WRITE_NODE_FIELD(row_path);
+	WRITE_NODE_FIELD(expr);
+	WRITE_NODE_FIELD(cols);
+	WRITE_NODE_FIELD(namespaces);
+	WRITE_LOCATION_FIELD(location);
+}
+
 /*****************************************************************************
  *
  *	Stuff from relation.h.
@@ -2586,6 +2600,8 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
 	WRITE_CHAR_FIELD(storage);
 	WRITE_NODE_FIELD(raw_default);
 	WRITE_NODE_FIELD(cooked_default);
+	WRITE_NODE_FIELD(raw_path);
+	WRITE_NODE_FIELD(cooked_path);
 	WRITE_NODE_FIELD(collClause);
 	WRITE_OID_FIELD(collOid);
 	WRITE_NODE_FIELD(constraints);
@@ -3862,6 +3878,9 @@ outNode(StringInfo str, const void *obj)
 			case T_ForeignKeyCacheInfo:
 				_outForeignKeyCacheInfo(str, obj);
 				break;
+			case T_TableExpr:
+				_outTableExpr(str, obj);
+				break;
 
 			default:
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index c83063e..0abef07 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -2263,6 +2263,109 @@ _readExtensibleNode(void)
 }
 
 /*
+ * _readTableExprNode
+ */
+static TableExpr *
+_readTableExprNode(void)
+{
+	READ_LOCALS(TableExpr);
+
+	READ_OID_FIELD(typid);
+
+	/* Enforce fresh TupleDesc */
+	local_node->typmod = -1;
+
+	READ_NODE_FIELD(row_path);
+	READ_NODE_FIELD(expr);
+	READ_NODE_FIELD(cols);
+	READ_NODE_FIELD(namespaces);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+/*
+ * _readColumnDefNode
+ */
+static ColumnDef *
+_readColumnDefNode(void)
+{
+	READ_LOCALS(ColumnDef);
+
+	READ_STRING_FIELD(colname);
+	READ_NODE_FIELD(typeName);
+	READ_INT_FIELD(inhcount);
+	READ_BOOL_FIELD(is_local);
+	READ_BOOL_FIELD(is_not_null);
+	READ_BOOL_FIELD(is_from_type);
+	READ_CHAR_FIELD(storage);
+	READ_NODE_FIELD(raw_default);
+	READ_NODE_FIELD(cooked_default);
+	READ_NODE_FIELD(raw_path);
+	READ_NODE_FIELD(cooked_path);
+	READ_NODE_FIELD(collClause);
+	READ_OID_FIELD(collOid);
+	READ_NODE_FIELD(constraints);
+	READ_NODE_FIELD(fdwoptions);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+static TypeName *
+_readTypeNameNode(void)
+{
+	READ_LOCALS(TypeName);
+
+	READ_NODE_FIELD(names);
+	READ_OID_FIELD(typeOid);
+	READ_BOOL_FIELD(setof);
+	READ_BOOL_FIELD(pct_type);
+	READ_NODE_FIELD(typmods);
+	READ_INT_FIELD(typemod);
+	READ_NODE_FIELD(arrayBounds);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+static A_Const *
+_readA_ConstNode(void)
+{
+	Value *val;
+
+	READ_LOCALS(A_Const);
+
+	token = pg_strtok(&length);		/* skip: fldname */
+	val = nodeRead(NULL, 0);
+	memcpy(&(local_node->val), val, sizeof(Value));
+
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+static FuncCall *
+_readFuncCallNode(void)
+{
+	READ_LOCALS(FuncCall);
+
+	READ_NODE_FIELD(funcname);
+	READ_NODE_FIELD(args);
+	READ_NODE_FIELD(agg_order);
+	READ_NODE_FIELD(agg_filter);
+	READ_BOOL_FIELD(agg_within_group);
+	READ_BOOL_FIELD(agg_star);
+	READ_BOOL_FIELD(agg_distinct);
+	READ_BOOL_FIELD(func_variadic);
+	READ_NODE_FIELD(over);
+	READ_LOCATION_FIELD(location);
+
+	READ_DONE();
+}
+
+
+/*
  * parseNodeString
  *
  * Given a character string representing a node tree, parseNodeString creates
@@ -2494,6 +2597,16 @@ parseNodeString(void)
 		return_value = _readAlternativeSubPlan();
 	else if (MATCH("EXTENSIBLENODE", 14))
 		return_value = _readExtensibleNode();
+	else if (MATCH("TABLEEXPR", 9))
+		return_value = _readTableExprNode();
+	else if (MATCH("COLUMNDEF", 9))
+		return_value = _readColumnDefNode();
+	else if (MATCH("TYPENAME", 8))
+		return_value = _readTypeNameNode();
+	else if (MATCH("A_CONST", 7))
+		return_value = _readA_ConstNode();
+	else if (MATCH("FUNCCALL", 8))
+		return_value = _readFuncCallNode();
 	else
 	{
 		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index a40ad40..808dbd8 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -818,6 +818,12 @@ expression_returns_set_rows_walker(Node *node, double *count)
 			*count *= get_func_rows(expr->opfuncid);
 		}
 	}
+	if (IsA(node, TableExpr))
+	{
+		/* we have not any method how to estimate it, use default */
+		*count = 1000;
+		return false;
+	}
 
 	/* Avoid recursion for some cases that can't return a set */
 	if (IsA(node, Aggref))
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index cb5cfc4..56dba08 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -229,6 +229,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	struct ImportQual	*importqual;
 	InsertStmt			*istmt;
 	VariableSetStmt		*vsetstmt;
+	TableExprColOption	*te_colopt;
 }
 
 %type <node>	stmt schema_stmt
@@ -542,6 +543,13 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <str>		opt_existing_window_name
 %type <boolean> opt_if_not_exists
 
+%type <list>	TableExprColList TableExprColOptions TableExprColOptionsOpt
+%type <node>	TableExprCol
+%type <te_colopt>	TableExprColOption
+%type <boolean>	IsNotNull
+%type <list>	XmlNamespaceList
+%type <target>	XmlNamespace
+
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
  * They must be listed first so that their numeric codes do not depend on
@@ -573,10 +581,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
 	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
-	CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT
-	COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
-	CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE
-	CROSS CSV CUBE CURRENT_P
+	CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS
+	COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION
+	CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST
+	CREATE CROSS CSV CUBE CURRENT_P
 	CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
 
@@ -617,7 +625,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR
 	ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
 
-	PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
+	PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PATH PLACING PLANS POLICY
 	POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
 	PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM
 
@@ -646,8 +654,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
 	WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
 
-	XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE
-	XMLPI XMLROOT XMLSERIALIZE
+	XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLNAMESPACES
+	XMLPARSE XMLPI XMLROOT XMLSERIALIZE XMLTABLE
 
 	YEAR_P YES_P
 
@@ -12539,6 +12547,150 @@ func_expr_common_subexpr:
 					n->location = @1;
 					$$ = (Node *)n;
 				}
+			| XMLTABLE '(' c_expr xmlexists_argument ')'
+				{
+					TableExpr *n = makeNode(TableExpr);
+					n->row_path = $3;
+					n->expr = $4;
+					n->cols = NIL;
+					n->namespaces = NIL;
+					n->location = @1;
+					$$ = (Node *)n;
+				}
+			| XMLTABLE '(' c_expr xmlexists_argument COLUMNS TableExprColList ')'
+				{
+					TableExpr *n = makeNode(TableExpr);
+					n->row_path = $3;
+					n->expr = $4;
+					n->cols = $6;
+					n->namespaces = NIL;
+					n->location = @1;
+					$$ = (Node *)n;
+				}
+			| XMLTABLE '(' XMLNAMESPACES '(' XmlNamespaceList ')' c_expr xmlexists_argument ')'
+				{
+					TableExpr *n = makeNode(TableExpr);
+					n->row_path = $7;
+					n->expr = $8;
+					n->cols = NIL;
+					n->namespaces = $5;
+					n->location = @1;
+					$$ = (Node *)n;
+				}
+			| XMLTABLE '(' XMLNAMESPACES '(' XmlNamespaceList ')' c_expr xmlexists_argument COLUMNS TableExprColList ')'
+				{
+					TableExpr *n = makeNode(TableExpr);
+					n->row_path = $7;
+					n->expr = $8;
+					n->cols = $10;
+					n->namespaces = $5;
+					n->location = @1;
+					$$ = (Node *)n;
+				}
+		;
+
+TableExprColList: TableExprCol							{ $$ = list_make1($1); }
+			| TableExprColList ',' TableExprCol			{ $$ = lappend($1, $3); }
+		;
+
+TableExprCol:
+			ColId Typename TableExprColOptionsOpt IsNotNull
+				{
+					ColumnDef	*column = makeNode(ColumnDef);
+					ListCell	*l;
+
+					column->colname = $1;
+					column->typeName = $2;
+					column->is_not_null = $4;
+					column->location = @1;
+
+					foreach(l, $3)
+					{
+						TableExprColOption *co = (TableExprColOption *) lfirst(l);
+
+						if (co->typ == TABLE_EXPR_COLOPT_DEFAULT)
+						{
+							if (column->raw_default != NULL)
+								ereport(ERROR,
+										(errcode(ERRCODE_SYNTAX_ERROR),
+										 errmsg("only one DEFAULT value per column is allowed"),
+													parser_errposition(co->location)));
+							column->raw_default = co->val;
+						}
+						else if (co->typ == TABLE_EXPR_COLOPT_PATH)
+						{
+							if (column->raw_path != NULL)
+								ereport(ERROR,
+										(errcode(ERRCODE_SYNTAX_ERROR),
+										 errmsg("only one PATH value per column is allowed"),
+													parser_errposition(co->location)));
+							column->raw_path = co->val;
+						}
+					}
+					$$ = (Node *) column;
+				}
+			| ColId FOR ORDINALITY
+				{
+					ColumnDef	*column = makeNode(ColumnDef);
+
+					column->colname = $1;
+					column->location = @1;
+
+					$$ = (Node *) column;
+				}
+		;
+
+TableExprColOptionsOpt: TableExprColOptions				{ $$ = $1; }
+			| /* EMPTY */								{ $$ = NIL; }
+		;
+
+TableExprColOptions: TableExprColOption					{ $$ = list_make1($1); }
+			| TableExprColOptions TableExprColOption	{ $$ = lappend($1, $2); }
+		;
+
+TableExprColOption:
+			DEFAULT c_expr
+				{
+					$$ = palloc(sizeof(TableExprColOption));
+					$$->typ = TABLE_EXPR_COLOPT_DEFAULT;
+					$$->val = $2;
+					$$->location = @1;
+				}
+			| PATH c_expr
+				{
+					$$ = palloc(sizeof(TableExprColOption));
+					$$->typ = TABLE_EXPR_COLOPT_PATH;
+					$$->val = $2;
+					$$->location = @1;
+				}
+		;
+
+IsNotNull: NOT NULL_P									{ $$ = true; }
+			| NULL_P									{ $$ = false; }
+			| /* EMPTY */								{ $$ = false; }
+		;
+
+XmlNamespaceList: XmlNamespace							{ $$ = list_make1($1); }
+			| XmlNamespaceList ',' XmlNamespace			{ $$ = lappend($1, $3); }
+		;
+
+XmlNamespace:
+			a_expr AS ColLabel
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = $3;
+					$$->indirection = NIL;
+					$$->val = (Node *) $1;
+					$$->location = @1;
+				}
+			| DEFAULT a_expr
+				{
+					$$ = makeNode(ResTarget);
+					$$->name = NULL;
+					$$->indirection = NIL;
+					$$->val = (Node *) $2;
+					$$->location = @1;
+				}
 		;
 
 /*
@@ -13677,6 +13829,7 @@ unreserved_keyword:
 			| CLASS
 			| CLOSE
 			| CLUSTER
+			| COLUMNS
 			| COMMENT
 			| COMMENTS
 			| COMMIT
@@ -13810,6 +13963,7 @@ unreserved_keyword:
 			| PARTITION
 			| PASSING
 			| PASSWORD
+			| PATH
 			| PLANS
 			| POLICY
 			| PRECEDING
@@ -13975,10 +14129,12 @@ col_name_keyword:
 			| XMLELEMENT
 			| XMLEXISTS
 			| XMLFOREST
+			| XMLNAMESPACES
 			| XMLPARSE
 			| XMLPI
 			| XMLROOT
 			| XMLSERIALIZE
+			| XMLTABLE
 		;
 
 /* Type/function identifier --- keywords that can be type or function names.
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index d277fd6..0686e07 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1105,8 +1105,8 @@ coerce_to_boolean(ParseState *pstate, Node *node,
  * processing is wanted.
  */
 Node *
-coerce_to_specific_type(ParseState *pstate, Node *node,
-						Oid targetTypeId,
+coerce_to_specific_type_typmod(ParseState *pstate, Node *node,
+						Oid targetTypeId, int32 targetTypmod,
 						const char *constructName)
 {
 	Oid			inputTypeId = exprType(node);
@@ -1116,7 +1116,7 @@ coerce_to_specific_type(ParseState *pstate, Node *node,
 		Node	   *newnode;
 
 		newnode = coerce_to_target_type(pstate, node, inputTypeId,
-										targetTypeId, -1,
+										targetTypeId, targetTypmod,
 										COERCION_ASSIGNMENT,
 										COERCE_IMPLICIT_CAST,
 										-1);
@@ -1143,6 +1143,15 @@ coerce_to_specific_type(ParseState *pstate, Node *node,
 	return node;
 }
 
+Node *
+coerce_to_specific_type(ParseState *pstate, Node *node,
+						Oid targetTypeId,
+						const char *constructName)
+{
+	return coerce_to_specific_type_typmod(pstate, node,
+								targetTypeId, -1,
+								constructName);
+}
 
 /*
  * parser_coercion_errposition - report coercion error location, if possible
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 63f7965..ec817cb 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -37,6 +37,7 @@
 #include "utils/date.h"
 #include "utils/lsyscache.h"
 #include "utils/timestamp.h"
+#include "utils/typcache.h"
 #include "utils/xml.h"
 
 
@@ -122,6 +123,7 @@ static Node *transformIndirection(ParseState *pstate, Node *basenode,
 					 List *indirection);
 static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
 static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
+static Node *transformTableExpr(ParseState *pstate, TableExpr *te);
 static Node *make_row_comparison_op(ParseState *pstate, List *opname,
 					   List *largs, List *rargs, int location);
 static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -365,6 +367,10 @@ transformExprRecurse(ParseState *pstate, Node *expr)
 				break;
 			}
 
+		case T_TableExpr:
+			result = transformTableExpr(pstate, (TableExpr *) expr);
+			break;
+
 		default:
 			/* should not reach here */
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
@@ -2678,6 +2684,168 @@ transformCollateClause(ParseState *pstate, CollateClause *c)
 }
 
 /*
+ * Transform a TableExpr
+ */
+static Node *
+transformTableExpr(ParseState *pstate, TableExpr *te)
+{
+	TableExpr *newte = makeNode(TableExpr);
+	TupleDesc tupdesc;
+
+	Assert(te->row_path != NULL);
+	Assert(te->expr != NULL);
+
+	newte->row_path = coerce_to_specific_type(pstate,
+								transformExprRecurse(pstate, te->row_path),
+														 TEXTOID,
+														 "XMLTABLE");
+	newte->expr = coerce_to_specific_type(pstate,
+								transformExprRecurse(pstate, te->expr),
+														 XMLOID,
+														 "XMLTABLE");
+
+	if (te->cols != NIL)
+	{
+		ListCell	   *col;
+		int				i = 1;
+		bool			for_ordinality = false;
+
+		tupdesc = CreateTemplateTupleDesc(list_length(te->cols), false);
+
+		foreach(col, te->cols)
+		{
+			ColumnDef  *column = (ColumnDef *) lfirst(col);
+			Oid		typid;
+			int32	typmod;
+			int			j;
+
+			if (column->typeName)
+			{
+				typenameTypeIdAndMod(NULL, column->typeName, &typid, &typmod);
+				TupleDescInitEntry(tupdesc, (AttrNumber) i, pstrdup(column->colname), typid, typmod, 0);
+
+				if (column->raw_default)
+					column->cooked_default = coerce_to_specific_type_typmod(pstate,
+								transformExprRecurse(pstate, column->raw_default),
+														 typid, typmod,
+														 "XMLTABLE");
+
+				if (column->raw_path)
+					column->cooked_path = coerce_to_specific_type(pstate,
+								transformExprRecurse(pstate, column->raw_path),
+														 TEXTOID,
+														 "XMLTABLE");
+			}
+			else
+			{
+				/* FOR ORDINALITY column has not defined typename */
+				if (for_ordinality)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("only one column FOR ORDINALITY is allowed"),
+											parser_errposition(pstate, column->location)));
+
+				TupleDescInitEntry(tupdesc, (AttrNumber) i, pstrdup(column->colname), INT4OID, -1, 0);
+			}
+
+			/*
+			 * Storage is not used here, but zero value breaks out/read node functions.
+			 * So set default.
+			 */
+			column->storage = 'p';
+
+			/* the name should be unique */
+			for (j = 0; j < i - 1; j++)
+				if (strcmp(NameStr(tupdesc->attrs[j]->attname), column->colname) == 0)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("the column name \"%s\" is not unique",
+													column->colname),
+								parser_errposition(pstate, column->location)));
+			i++;
+		}
+	}
+	else
+	{
+		/*
+		 * When columsn are not defined, then output is XML column.
+		 * ANSI/SQL standard doesn't specify the name of this column,
+		 * and there are not conformity between databases. Postgres
+		 * uses function name like default. This implementation
+		 * respects it.
+		 */
+		tupdesc = CreateTemplateTupleDesc(1, false);
+
+		/* Generate tupdesc with one auto XML attribute */
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xmltable", XMLOID, -1, 0);
+	}
+
+	newte->cols = te->cols;
+
+	if (te->namespaces != NIL)
+	{
+		List	   *transformlist = NIL;
+		ListCell   *res;
+		char	  **names;
+		bool		found_dns = false;
+		int			nnames = 0;
+
+		names = palloc(sizeof(char *) * list_length(te->namespaces));
+
+		foreach(res, te->namespaces)
+		{
+			ResTarget *rt = (ResTarget *) lfirst(res);
+			ResTarget *newrt = makeNode(ResTarget);
+
+			/* Check unique name, and uniq default namespace */
+			if (rt->name != NULL)
+			{
+				int		i;
+
+				for (i = 0; i < nnames; i++)
+					if (strcmp(names[i], rt->name) == 0)
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("the namespace name \"%s\" is not unique",
+														rt->name),
+									parser_errposition(pstate, rt->location)));
+				names[nnames++] = rt->name;
+			}
+			else
+			{
+				if (found_dns)
+					ereport(ERROR,
+							(errcode(ERRCODE_SYNTAX_ERROR),
+							 errmsg("only one default namespace is allowed"),
+									parser_errposition(pstate, rt->location)));
+				found_dns = true;
+			}
+
+			newrt->name = rt->name;
+			newrt->val = coerce_to_specific_type(pstate,
+									transformExprRecurse(pstate, rt->val),
+												 TEXTOID,
+												 "XMLTABLE");
+			newrt->location = rt->location;
+			transformlist = lappend(transformlist, newrt);
+		}
+		newte->namespaces = transformlist;
+		pfree(names);
+	}
+	else
+		newte->namespaces = NIL;
+
+	assign_record_type_typmod(tupdesc);
+
+	newte->typid = tupdesc->tdtypeid;
+	newte->typmod = tupdesc->tdtypmod;
+
+	newte->location = te->location;
+
+	return (Node *) newte;
+}
+
+/*
  * Transform a "row compare-op row" construct
  *
  * The inputs are lists of already-transformed expressions.
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b7b82bf..7ce209d 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -1837,6 +1837,9 @@ FigureColnameInternal(Node *node, char **name)
 		case T_XmlSerialize:
 			*name = "xmlserialize";
 			return 2;
+		case T_TableExpr:
+			*name = "xmltable";
+			return 2;
 		default:
 			break;
 	}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..6dd69cc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -50,6 +50,7 @@
 #include "parser/parse_agg.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
+#include "parser/parse_type.h"
 #include "parser/parser.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
@@ -8280,6 +8281,100 @@ get_rule_expr(Node *node, deparse_context *context,
 			}
 			break;
 
+		case T_TableExpr:
+			{
+				TableExpr *te = (TableExpr *) node;
+
+				/* c_expr shoud be closed in brackets */
+				appendStringInfoString(buf, "XMLTABLE(");
+
+				if (te->namespaces != NIL)
+				{
+					ListCell *res;
+					bool		first = true;
+
+					appendStringInfoString(buf, "XMLNAMESPACES(");
+					foreach(res, te->namespaces)
+					{
+						ResTarget *rt = (ResTarget *) lfirst(res);
+
+						if (!first)
+							appendStringInfoString(buf, ", ");
+						else
+							first = false;
+
+						if (rt->name != NULL)
+						{
+							get_rule_expr((Node *) rt->val, context, true);
+							appendStringInfo(buf, " AS %s", quote_identifier(rt->name));
+						}
+						else
+						{
+							appendStringInfoString(buf, "DEFAULT ");
+							get_rule_expr((Node *) rt->val, context, true);
+						}
+					}
+					appendStringInfoChar(buf, ')');
+				}
+
+				appendStringInfoChar(buf, '(');
+				get_rule_expr((Node *) te->row_path, context, true);
+				appendStringInfoString(buf, ") PASSING (");
+				get_rule_expr((Node *) te->expr, context, true);
+				appendStringInfoChar(buf, ')');
+
+				if (te->cols != NIL)
+				{
+					ListCell	*col;
+					bool		first = true;
+
+					appendStringInfoString(buf, " COLUMNS ");
+
+					foreach(col, te->cols)
+					{
+						ColumnDef *column = (ColumnDef *) lfirst(col);
+						Oid		typid;
+						int32	typmod;
+
+						if (!first)
+							appendStringInfoString(buf, ", ");
+						else
+							first = false;
+
+						appendStringInfoString(buf, quote_identifier(column->colname));
+						appendStringInfoChar(buf, ' ');
+
+						if (column->typeName != NULL)
+						{
+							typenameTypeIdAndMod(NULL, column->typeName, &typid, &typmod);
+							appendStringInfoString(buf, format_type_with_typemod(typid, typmod));
+
+							if (column->cooked_default != NULL)
+							{
+								appendStringInfoString(buf, " DEFAULT (");
+								get_rule_expr((Node *) column->cooked_default, context, true);
+								appendStringInfoChar(buf, ')');
+							}
+							if (column->cooked_path != NULL)
+							{
+								appendStringInfoString(buf, " PATH (");
+								get_rule_expr((Node *) column->cooked_path, context, true);
+								appendStringInfoChar(buf, ')');
+							}
+							if (column->is_not_null)
+								appendStringInfoString(buf, " NOT NULL");
+						}
+						else
+						{
+							appendStringInfoString(buf, "FOR ORDINALITY");
+						}
+					}
+				}
+
+				appendStringInfoChar(buf, ')');
+			}
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
 			break;
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 7ed5bcb..287dcd9 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4073,3 +4073,753 @@ xml_is_well_formed_content(PG_FUNCTION_ARGS)
 	return 0;
 #endif   /* not USE_LIBXML */
 }
+
+/*
+ * support functions for XMLTABLE function
+ *
+ */
+#ifdef USE_LIBXML
+
+/*
+ * We need to work with XPath expression tokens. When expression
+ * starting or finishing with nodenames, then we can use prefix
+ * and suffix. When default namespace is defined, then we should to
+ * enhance any nodename and attribute without namespace by default
+ * namespace. The procession of XPath expression is linear.
+ */
+
+typedef enum
+{
+	XPATH_TOKEN_NONE,
+	XPATH_TOKEN_NAME,
+	XPATH_TOKEN_STRING,
+	XPATH_TOKEN_NUMBER,
+	XPATH_TOKEN_OTHER
+} XPathTokenType;
+
+typedef struct TokenInfo
+{
+	XPathTokenType		ttype;
+	char		   *start;
+	int				length;
+} XPathTokenInfo;
+
+#define TOKEN_STACK_SIZE		10
+
+typedef struct ParserData
+{
+	char		   *str;
+	char		   *cur;
+	XPathTokenInfo	stack[TOKEN_STACK_SIZE];
+	int				stack_length;
+} XPathParserData;
+
+#define NODENAME_FIRSTCHAR(c)	 ((c) == '_' || (c) == '-' || \
+								 ((c) >= 'A' && (c) <= 'Z') || \
+								 ((c) >= 'a' && (c) <= 'z') || \
+								 ((c) >= '0' && (c) <= '9'))
+
+#define IS_NODENAME_CHAR(c)		(NODENAME_FIRSTCHAR(c) || (c) == '.')
+
+/*
+ * Returns next char after last char of token
+ */
+static char *
+getXPathToken(char *str, XPathTokenInfo *ti)
+{
+
+	/* skip initial spaces */
+	while (*str == ' ')
+		str++;
+
+	if (*str != '\0')
+	{
+		char		c = *str;
+
+		ti->start = str++;
+
+		if (c >= '0' && c <= '9')
+		{
+			while (*str >= '0' && *str <= '9')
+				str++;
+			if (*str == '.')
+			{
+				str++;
+				while (*str >= '0' && *str <= '9')
+					str++;
+			}
+			ti->ttype = XPATH_TOKEN_NUMBER;
+		}
+		else if (NODENAME_FIRSTCHAR(c))
+		{
+			while (IS_NODENAME_CHAR(*str))
+				str++;
+
+			ti->ttype = XPATH_TOKEN_NAME;
+		}
+		else if (c == '"')
+		{
+			while (*str != '\0')
+				if (*str++ == '"')
+					break;
+
+			ti->ttype = XPATH_TOKEN_STRING;
+		}
+		else
+			ti->ttype = XPATH_TOKEN_OTHER;
+
+		ti->length = str - ti->start;
+	}
+	else
+	{
+		ti->start = NULL;
+		ti->length = 0;
+
+		ti->ttype = XPATH_TOKEN_NONE;
+	}
+
+	return str;
+}
+
+/*
+ * reset XPath parser stack
+ */
+static void
+initXPathParser(XPathParserData *parser, char *str)
+{
+	parser->str = str;
+	parser->cur = str;
+	parser->stack_length = 0;
+}
+
+static void
+nextXPathToken(XPathParserData *parser, XPathTokenInfo *ti)
+{
+	if (parser->stack_length > 0)
+		memcpy(ti, &parser->stack[--parser->stack_length],
+										  sizeof(XPathTokenInfo));
+	else
+		parser->cur = getXPathToken(parser->cur, ti);
+}
+
+static void
+pushXPathToken(XPathParserData *parser, XPathTokenInfo *ti)
+{
+	if (parser->stack_length == TOKEN_STACK_SIZE)
+		elog(ERROR, "internal error");
+	memcpy(&parser->stack[parser->stack_length++], ti,
+										  sizeof(XPathTokenInfo));
+}
+
+static void
+writeXPathToken(StringInfo str, XPathTokenInfo *ti)
+{
+	Assert(ti->ttype != XPATH_TOKEN_NONE);
+
+	if (ti->ttype != XPATH_TOKEN_OTHER)
+		appendBinaryStringInfo(str, ti->start, ti->length);
+	else
+		appendStringInfoChar(str, *ti->start);
+}
+
+/*
+ * Working horse for XPath transformation. When XPath starting by node name,
+ * then prefix have to be applied. When XPath ending by node name, then
+ * suffix will be used. Any unqualified node name should be qualified by
+ * default namespace.
+ */
+static void
+_transformXPath(StringInfo str, XPathParserData *parser,
+				  bool inside_predicate,
+				  char *prefix, char *suffix, char *default_ns_name)
+{
+	XPathTokenInfo	t1, t2;
+	bool			is_first_token = true;
+	bool			last_token_is_name = false;
+
+	nextXPathToken(parser, &t1);
+
+	while (t1.ttype != XPATH_TOKEN_NONE)
+	{
+		switch (t1.ttype)
+		{
+			case XPATH_TOKEN_NUMBER:
+			case XPATH_TOKEN_STRING:
+				last_token_is_name = false;
+				is_first_token = false;
+				writeXPathToken(str, &t1);
+				nextXPathToken(parser, &t1);
+				break;
+
+			case XPATH_TOKEN_NAME:
+				{
+					bool	is_qual_name = false;
+
+					/* inside predicate ignore keywords "and" "or" */
+					if (inside_predicate)
+					{
+						if ((strncmp(t1.start, "and", 3) == 0 && t1.length == 3) ||
+							(strncmp(t1.start, "or", 2) == 0 && t1.length == 2))
+						{
+							writeXPathToken(str, &t1);
+							nextXPathToken(parser, &t1);
+							break;
+						}
+					}
+
+					last_token_is_name = true;
+					nextXPathToken(parser, &t2);
+					if (t2.ttype == XPATH_TOKEN_OTHER)
+					{
+						if (*t2.start == '(')
+							last_token_is_name = false;
+						else if (*t2.start == ':')
+							is_qual_name = true;
+					}
+
+					if (is_first_token && last_token_is_name && prefix != NULL)
+						appendStringInfoString(str, prefix);
+
+					if (last_token_is_name && !is_qual_name && default_ns_name != NULL)
+						appendStringInfo(str, "%s:", default_ns_name);
+
+					writeXPathToken(str, &t1);
+					is_first_token = false;
+
+					if (is_qual_name)
+					{
+						writeXPathToken(str, &t2);
+						nextXPathToken(parser, &t1);
+						if (t1.ttype == XPATH_TOKEN_NAME)
+							writeXPathToken(str, &t1);
+						else
+							pushXPathToken(parser, &t1);
+					}
+					else
+						pushXPathToken(parser, &t2);
+
+					nextXPathToken(parser, &t1);
+				}
+				break;
+
+			case XPATH_TOKEN_OTHER:
+				{
+					char c = *t1.start;
+
+					is_first_token = false;
+
+					writeXPathToken(str, &t1);
+
+					if (c == '[')
+						_transformXPath(str, parser, true, NULL, NULL, default_ns_name);
+					else
+					{
+						last_token_is_name = false;
+
+						if (c == ']' && inside_predicate)
+							return;
+
+						else if (c == '@')
+						{
+							nextXPathToken(parser, &t1);
+							if (t1.ttype == XPATH_TOKEN_NAME)
+							{
+								bool		is_qual_name = false;
+
+								nextXPathToken(parser, &t2);
+								if (t2.ttype == XPATH_TOKEN_OTHER && *t2.start == ':')
+									is_qual_name = true;
+
+								if (!is_qual_name && default_ns_name != NULL)
+									appendStringInfo(str, "%s:", default_ns_name);
+
+								writeXPathToken(str, &t1);
+								if (is_qual_name)
+								{
+									writeXPathToken(str, &t2);
+									nextXPathToken(parser, &t1);
+									if (t1.ttype == XPATH_TOKEN_NAME)
+										writeXPathToken(str, &t1);
+									else
+										pushXPathToken(parser, &t1);
+								}
+								else
+									pushXPathToken(parser, &t2);
+							}
+							else
+								pushXPathToken(parser, &t1);
+						}
+					}
+					nextXPathToken(parser, &t1);
+				}
+				break;
+
+			case XPATH_TOKEN_NONE:
+				elog(ERROR, "should not be here");
+		}
+	}
+
+	if (last_token_is_name && suffix != NULL)
+		appendStringInfoString(str, suffix);
+}
+
+static void
+transformXPath(StringInfo str, char *xpath,
+					  char *prefix, char *suffix, char *default_ns_name)
+{
+	XPathParserData		parser;
+
+	initStringInfo(str);
+	initXPathParser(&parser, xpath);
+	_transformXPath(str, &parser, false, prefix, suffix, default_ns_name);
+}
+
+
+struct XmlTableContext
+{
+	MemoryContext			per_rowgroup_memory;
+	int						ncols;
+	PgXmlErrorContext	   *xmlerrcxt;
+	xmlParserCtxtPtr		ctxt;
+	xmlDocPtr				doc;
+	xmlXPathContextPtr		xpathcxt;
+	xmlXPathCompExprPtr		xpathcomp;
+	xmlXPathObjectPtr		xpathobj;
+	xmlXPathCompExprPtr	   *xpathscomp;
+	FmgrInfo			   *in_functions;
+	Oid					   *typioparams;
+	char				   *default_ns_name;
+	long int				rc;
+};
+
+/*
+ * Convert XML node to cstring (dump subtree in case of element,
+ * return value otherwise)
+ */
+static char *
+xml_xmlnodetostr(xmlNodePtr cur, PgXmlErrorContext *xmlerrcxt)
+{
+	char *result;
+
+	if (cur->type == XML_ELEMENT_NODE)
+	{
+		xmlBufferPtr buf;
+		xmlNodePtr	cur_copy;
+
+		buf = xmlBufferCreate();
+
+		/*
+		 * The result of xmlNodeDump() won't contain namespace definitions
+		 * from parent nodes, but xmlCopyNode() duplicates a node along with
+		 * its required namespace definitions.
+		 */
+		cur_copy = xmlCopyNode(cur, 1);
+
+		if (cur_copy == NULL)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+						"could not copy node");
+
+		PG_TRY();
+		{
+			xmlNodeDump(buf, NULL, cur_copy, 0, 1);
+			result = pstrdup((const char*) xmlBufferContent(buf));
+		}
+		PG_CATCH();
+		{
+			xmlFreeNode(cur_copy);
+			xmlBufferFree(buf);
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+		xmlFreeNode(cur_copy);
+		xmlBufferFree(buf);
+	}
+	else
+	{
+		xmlChar    *str;
+
+		str = xmlXPathCastNodeToString(cur);
+		PG_TRY();
+		{
+			/* Here we rely on XML having the same representation as TEXT */
+			char	   *escaped = escape_xml((char *) str);
+
+			result = escaped;
+		}
+		PG_CATCH();
+		{
+			xmlFree(str);
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+		xmlFree(str);
+	}
+
+	return result;
+}
+
+#endif
+
+struct XmlTableContext *
+initXmlTableContext(xmltype *xmlval, char *default_ns_name,
+				int ncols, FmgrInfo *in_functions, Oid *typioparams,
+				MemoryContext per_rowgroup_memory)
+{
+#ifdef USE_LIBXML
+	MemoryContext				oldcxt;
+	struct XmlTableContext	   *result = NULL;
+	PgXmlErrorContext		   *xmlerrcxt = NULL;
+	int32						len;
+	xmlChar			*xmlval_str;
+
+	volatile xmlParserCtxtPtr	ctxt = NULL;
+	volatile xmlDocPtr			doc = NULL;
+	volatile xmlXPathContextPtr	xpathcxt = NULL;
+
+	oldcxt = MemoryContextSwitchTo(per_rowgroup_memory);
+
+	len = VARSIZE(xmlval) - VARHDRSZ;
+	xmlval_str = palloc((len + 1) * sizeof(xmlChar));
+	memcpy(xmlval_str, VARDATA(xmlval), len);
+	xmlval_str[len] = '\0';
+
+	result = palloc0(sizeof(struct XmlTableContext));
+	result->per_rowgroup_memory = per_rowgroup_memory;
+	result->in_functions = in_functions;
+	result->typioparams = typioparams;
+	result->default_ns_name = default_ns_name;
+	result->xpathscomp = palloc0(sizeof(xmlXPathCompExprPtr) * ncols);
+
+	xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
+
+	PG_TRY();
+	{
+		xmlInitParser();
+
+		ctxt = xmlNewParserCtxt();
+		if (ctxt == NULL || xmlerrcxt->err_occurred)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+				"could not allocate parser context");
+		doc = xmlCtxtReadMemory(ctxt, (char *) xmlval_str, len, NULL, NULL, 0);
+		if (doc == NULL || xmlerrcxt->err_occurred)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INVALID_XML_DOCUMENT,
+				"could not parse XML document");
+		xpathcxt = xmlXPathNewContext(doc);
+		if (xpathcxt == NULL || xmlerrcxt->err_occurred)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_OUT_OF_MEMORY,
+				"could not allocate XPath context");
+		xpathcxt->node = xmlDocGetRootElement(doc);
+		if (xpathcxt->node == NULL  || xmlerrcxt->err_occurred)
+			xml_ereport(xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+				"could not find root XML element");
+	}
+	PG_CATCH();
+	{
+		if (xpathcxt != NULL)
+			xmlXPathFreeContext(xpathcxt);
+		if (doc != NULL)
+			xmlFreeDoc(doc);
+		if (ctxt != NULL)
+			xmlFreeParserCtxt(ctxt);
+
+		pg_xml_done(xmlerrcxt, true);
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	result->ncols = ncols;
+	result->xmlerrcxt = xmlerrcxt;
+	result->ctxt = ctxt;
+	result->doc = doc;
+	result->xpathcxt = xpathcxt;
+
+	MemoryContextSwitchTo(oldcxt);
+
+	return (struct XmlTableContext *) result;
+
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+
+	return NULL;
+};
+
+void
+XmlTableSetRowPath(struct XmlTableContext *xtCxt, text *row_path)
+{
+#ifdef USE_LIBXML
+	xmlChar			*row_path_str;
+	MemoryContext	oldcxt;
+	StringInfoData			str;
+	char		*path_str;
+
+	path_str = text_to_cstring(row_path);
+	if (*path_str == '\0')
+		ereport(ERROR,
+					(errcode(ERRCODE_DATA_EXCEPTION),
+					 errmsg("row path filter must not be empty string")));
+
+	transformXPath(&str, path_str, NULL, NULL, xtCxt->default_ns_name);
+
+	oldcxt = MemoryContextSwitchTo(xtCxt->per_rowgroup_memory);
+
+	row_path_str = (xmlChar *) palloc((str.len + 1) * sizeof(xmlChar));
+	memcpy(row_path_str, str.data, str.len);
+	row_path_str[str.len] = '\0';
+
+	xtCxt->xpathcomp = xmlXPathCompile(row_path_str);
+	if (xtCxt->xpathcomp == NULL || xtCxt->xmlerrcxt->err_occurred)
+		xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
+			"invalid XPath expression");
+
+	MemoryContextSwitchTo(oldcxt);
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+};
+
+void
+XmlTableFreeContext(struct XmlTableContext *xtCxt)
+{
+#ifdef USE_LIBXML
+	if (xtCxt->xpathscomp != NULL)
+	{
+		int		i;
+
+		for (i = 0; i < xtCxt->ncols; i++)
+			if (xtCxt->xpathscomp[i] != NULL)
+				xmlXPathFreeCompExpr(xtCxt->xpathscomp[i]);
+	}
+
+	if (xtCxt->xpathobj != NULL)
+		xmlXPathFreeObject(xtCxt->xpathobj);
+	if (xtCxt->xpathcomp != NULL)
+		xmlXPathFreeCompExpr(xtCxt->xpathcomp);
+	if (xtCxt->xpathcxt != NULL)
+		xmlXPathFreeContext(xtCxt->xpathcxt);
+	if (xtCxt->doc != NULL)
+		xmlFreeDoc(xtCxt->doc);
+	if (xtCxt->ctxt != NULL)
+		xmlFreeParserCtxt(xtCxt->ctxt);
+
+	pg_xml_done(xtCxt->xmlerrcxt, true);
+
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+};
+
+void
+XmlTableSetNs(struct XmlTableContext *xtCxt, char *name, char *uri)
+{
+#ifdef USE_LIBXML
+
+	if (xmlXPathRegisterNs(xtCxt->xpathcxt,
+								(xmlChar *)(name ? name : xtCxt->default_ns_name),
+								(xmlChar *) uri))
+		xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
+			"could not set XML namespace");
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+};
+
+void
+XmlTableSetColumnPath(struct XmlTableContext *xtCxt, int i, Oid typid, char *path)
+{
+#ifdef USE_LIBXML
+	MemoryContext oldcxt;
+	StringInfoData		str;
+	xmlChar			*xmlstr;
+
+	transformXPath(&str, path,
+					  "./", typid != XMLOID ? "/text()" : NULL,
+					  xtCxt->default_ns_name);
+
+	oldcxt = MemoryContextSwitchTo(xtCxt->per_rowgroup_memory);
+
+	xmlstr = palloc((str.len + 1) * sizeof(xmlChar));
+	memcpy(xmlstr, str.data, str.len);
+	xmlstr[str.len] = '\0';
+
+	xtCxt->xpathscomp[i] = xmlXPathCompile(xmlstr);
+	if (xtCxt->xpathscomp[i] == NULL || xtCxt->xmlerrcxt->err_occurred)
+		xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_DATA_EXCEPTION,
+			"invalid XPath expression");
+
+	MemoryContextSwitchTo(oldcxt);
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+};
+
+bool
+XmlTableFetchRow(struct XmlTableContext *xtCxt)
+{
+#ifdef USE_LIBXML
+
+	if (xtCxt->xpathobj == NULL)
+	{
+		MemoryContext oldcxt = MemoryContextSwitchTo(xtCxt->per_rowgroup_memory);
+
+		xtCxt->xpathobj = xmlXPathCompiledEval(xtCxt->xpathcomp, xtCxt->xpathcxt);
+		if (xtCxt->xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
+			xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+				"could not create XPath object");
+
+		xtCxt->rc = 0;
+
+		MemoryContextSwitchTo(oldcxt);
+	}
+
+	if (xtCxt->xpathobj->type == XPATH_NODESET)
+	{
+		if (xtCxt->xpathobj->nodesetval != NULL)
+		{
+			if (xtCxt->rc++ < xtCxt->xpathobj->nodesetval->nodeNr)
+				return true;
+		}
+	}
+
+	return false;
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+
+	return false;
+};
+
+Datum
+XmlTableGetValue(struct XmlTableContext *xtCxt, int ncol, Oid typid, int32 typmod, bool *isnull)
+{
+#ifdef USE_LIBXML
+	Datum			result = (Datum) 0;
+	xmlNodePtr		cur;
+	char		   *cstr;
+	volatile xmlXPathObjectPtr		column_xpathobj = NULL;
+
+	Assert(xtCxt->xpathobj && xtCxt->xpathobj->type == XPATH_NODESET &&
+			  xtCxt->xpathobj->nodesetval != NULL);
+
+	cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->rc - 1];
+
+	if (cur->type == XML_ELEMENT_NODE)
+	{
+		PG_TRY();
+		{
+			xmlXPathSetContextNode(cur, xtCxt->xpathcxt);
+			column_xpathobj = xmlXPathCompiledEval(xtCxt->xpathscomp[ncol], xtCxt->xpathcxt);
+			if (column_xpathobj == NULL || xtCxt->xmlerrcxt->err_occurred)
+				xml_ereport(xtCxt->xmlerrcxt, ERROR, ERRCODE_INTERNAL_ERROR,
+					"could not create XPath object");
+
+			if (column_xpathobj->type == XPATH_NODESET)
+			{
+				int count;
+
+				if (column_xpathobj->nodesetval != NULL)
+					count = column_xpathobj->nodesetval->nodeNr;
+				else
+					count = 0;
+
+				if (count > 0)
+				{
+					if (count == 1)
+					{
+						cstr = xml_xmlnodetostr(column_xpathobj->nodesetval->nodeTab[0],
+															xtCxt->xmlerrcxt);
+					}
+					else
+					{
+						StringInfoData		str;
+						int					i;
+
+						/*
+						 * more values, target must be XML.
+						 * Note: possibly any array can be there.
+						 */
+						if (typid != XMLOID)
+							ereport(ERROR,
+										(errcode(ERRCODE_CARDINALITY_VIOLATION),
+										 errmsg("more than one value returned by column XPath expression")));
+
+						initStringInfo(&str);
+						for (i = 0; i < count; i++)
+						{
+							appendStringInfoString(&str,
+										  xml_xmlnodetostr(column_xpathobj->nodesetval->nodeTab[i],
+														   xtCxt->xmlerrcxt));
+						}
+						cstr = str.data;
+					}
+
+					result = InputFunctionCall(&xtCxt->in_functions[ncol],
+													  cstr,
+													  xtCxt->typioparams[ncol],
+													  typmod);
+					*isnull = false;
+				}
+				else
+					*isnull = true;
+
+			}
+			else if (column_xpathobj->type == XPATH_STRING)
+			{
+				result = InputFunctionCall(&xtCxt->in_functions[ncol],
+													  (char *) column_xpathobj->stringval,
+													  xtCxt->typioparams[ncol],
+													  typmod);
+				*isnull = false;
+			}
+			else
+				elog(ERROR, "unexpected XPath object type");
+		}
+		PG_CATCH();
+		{
+			if (column_xpathobj != NULL)
+				xmlXPathFreeObject(column_xpathobj);
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+	}
+	else
+		elog(ERROR, "unexpected xmlNode type");
+
+	return (Datum) result;
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+
+	*isnull = true;
+	return (Datum) 0;
+};
+
+Datum
+XmlTableGetRowValue(struct XmlTableContext *xtCxt, bool *isnull)
+{
+#ifdef USE_LIBXML
+
+	Datum			result = (Datum) 0;
+	xmlNodePtr		cur;
+	char		   *cstr;
+
+	Assert(xtCxt->xpathobj && xtCxt->xpathobj->type == XPATH_NODESET &&
+			  xtCxt->xpathobj->nodesetval != NULL);
+
+	cur = xtCxt->xpathobj->nodesetval->nodeTab[xtCxt->rc - 1];
+	cstr = xml_xmlnodetostr(cur, xtCxt->xmlerrcxt);
+
+	result = InputFunctionCall(&xtCxt->in_functions[0],
+									cstr,
+									xtCxt->typioparams[0],
+									-1);						/* target type is XML always */
+	*isnull = false;
+	return result;
+#else
+	NO_XML_SUPPORT();
+#endif   /* not USE_LIBXML */
+
+	*isnull = true;
+	return (Datum) 0;
+};
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d179ae..c8ccf1f 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -20,6 +20,7 @@
 #include "funcapi.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_type.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -221,6 +222,53 @@ get_call_result_type(FunctionCallInfo fcinfo,
 }
 
 /*
+ * When we skip transform stage (in view), then TableExpr's
+ * TupleDesc should not be valid. Refresh is necessary.
+ */
+TupleDesc
+TableExprGetTupleDesc(TableExpr *te)
+{
+	TupleDesc tupdesc;
+
+	if (te->cols != NIL)
+	{
+		ListCell	   *col;
+		int				i = 1;
+
+		tupdesc = CreateTemplateTupleDesc(list_length(te->cols), false);
+
+		foreach(col, te->cols)
+		{
+			ColumnDef  *column = (ColumnDef *) lfirst(col);
+			Oid		typid;
+			int32	typmod;
+
+			if (column->typeName)
+			{
+				typenameTypeIdAndMod(NULL, column->typeName, &typid, &typmod);
+				TupleDescInitEntry(tupdesc, (AttrNumber) i, pstrdup(column->colname), typid, typmod, 0);
+			}
+			else
+				TupleDescInitEntry(tupdesc, (AttrNumber) i, pstrdup(column->colname), INT4OID, -1, 0);
+
+			i++;
+		}
+	}
+	else
+	{
+		tupdesc = CreateTemplateTupleDesc(1, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xmltable", XMLOID, -1, 0);
+	}
+
+	assign_record_type_typmod(tupdesc);
+	te->typmod = tupdesc->tdtypmod;
+
+	Assert(te->typid = tupdesc->tdtypeid);
+
+	return tupdesc;
+}
+
+/*
  * get_expr_result_type
  *		As above, but work from a calling expression node tree
  */
@@ -243,6 +291,22 @@ get_expr_result_type(Node *expr,
 										  NULL,
 										  resultTypeId,
 										  resultTupleDesc);
+	else if (expr && IsA(expr, TableExpr))
+	{
+		TableExpr *te = (TableExpr *) expr;
+
+		if (resultTypeId)
+			*resultTypeId = te->typid;
+
+		/* Enforce fresh RECORD tupledesc */
+		if (te->typmod == -1)
+			TableExprGetTupleDesc(te);
+
+		if (resultTupleDesc)
+			*resultTupleDesc = lookup_rowtype_tupdesc_copy(te->typid, te->typmod);
+
+		return TYPEFUNC_COMPOSITE;
+	}
 	else
 	{
 		/* handle as a generic expression; no chance to resolve RECORD */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..0417a60 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -184,6 +184,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
 								Datum proargnames);
 extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
 
+extern TupleDesc TableExprGetTupleDesc(TableExpr *te);
 
 /*----------
  *	Support to ease writing functions returning composite types
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..46a8ab4 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1004,6 +1004,31 @@ typedef struct DomainConstraintState
 	ExprState  *check_expr;		/* for CHECK, a boolean expression */
 } DomainConstraintState;
 
+typedef struct TableExprState
+{
+	ExprState	xprstate;
+	bool		firstRow;			/* true, when first tuple from call should be returned */
+	List	   *namespaces;			/* list of prepared ResTarget fields */
+	bool		used_dns;			/* true, when default namespace is used */
+	Oid			typid;
+	int32		typmod;
+	TupleDesc	tupdesc;			/* cache */
+	int		ncols;					/* number of declared columns */
+	int		for_ordinality_col;		/* number of oridinality column, started by 1 */
+	int		rownum;					/* row counter - for ordinality column */
+	ExprState      *row_path_expr;	/* row xpath expression */
+	ExprState      *expr;			/* processed data */
+	ExprState     **def_expr;		/* array of expressions for default value */
+	ExprState     **col_path_expr;	/* array of expressions for path value */
+	bool	       *not_null;		/* for any column info if NULL is allowed or not */
+	Datum	       *values;								/* prealloc buffer */
+	bool	       *nulls;								/* prealloc buffer */
+	FmgrInfo	  *in_functions;	/* array of infunction for any column */
+	Oid			  *typioparams;		/* array of typIOParam for any column */
+	struct XmlTableContext		*xmltableCxt;
+	MemoryContext				per_rowgroup_memory;
+} TableExprState;
+
 
 /* ----------------------------------------------------------------
  *				 Executor State Trees
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2f7efa8..4877901 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -181,6 +181,7 @@ typedef enum NodeTag
 	T_FromExpr,
 	T_OnConflictExpr,
 	T_IntoClause,
+	T_TableExpr,
 
 	/*
 	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
@@ -216,6 +217,7 @@ typedef enum NodeTag
 	T_NullTestState,
 	T_CoerceToDomainState,
 	T_DomainConstraintState,
+	T_TableExprState,
 
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1481fff..ad0f700 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -595,6 +595,8 @@ typedef struct ColumnDef
 	char		storage;		/* attstorage setting, or 0 for default */
 	Node	   *raw_default;	/* default value (untransformed parse tree) */
 	Node	   *cooked_default; /* default value (transformed expr tree) */
+	Node	   *raw_path;		/* path value for TableExpr (untransformed parse tree) */
+	Node	   *cooked_path;	/* path value for TableExpr (transformed expr tree) */
 	CollateClause *collClause;	/* untransformed COLLATE spec, if any */
 	Oid			collOid;		/* collation OID (InvalidOid if not set) */
 	List	   *constraints;	/* other constraints on column */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..69e8932 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -1467,4 +1467,35 @@ typedef struct OnConflictExpr
 	List	   *exclRelTlist;	/* tlist of the EXCLUDED pseudo relation */
 } OnConflictExpr;
 
+/*----------
+ * TableExpr - used for XMLTABLE function
+ *
+ * This can be used for json_table, jsonb_table functions in future
+ *----------
+ */
+typedef enum
+{
+	TABLE_EXPR_COLOPT_DEFAULT,
+	TABLE_EXPR_COLOPT_PATH
+} TableExprColOptionType;
+
+typedef struct
+{
+	TableExprColOptionType	typ;
+	Node			*val;
+	int			location;
+} TableExprColOption;
+
+typedef struct TableExpr
+{
+	NodeTag		type;
+	Oid				typid;
+	int32			typmod;
+	Node	   *row_path;		/* row xpath query */
+	Node	   *expr;			/* processed data */
+	List	   *cols;			/* columns definitions */
+	List	   *namespaces;		/* list of namespaces */
+	int			location;
+} TableExpr;
+
 #endif   /* PRIMNODES_H */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 17ffef5..de83895 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -81,6 +81,7 @@ PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD)
 PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD)
 PG_KEYWORD("collation", COLLATION, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD)
+PG_KEYWORD("columns", COLUMNS, UNRESERVED_KEYWORD)
 PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
 PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
@@ -289,6 +290,7 @@ PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
 PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD)
 PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD)
 PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD)
+PG_KEYWORD("path", PATH, UNRESERVED_KEYWORD)
 PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD)
 PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD)
 PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD)
@@ -438,10 +440,12 @@ PG_KEYWORD("xmlconcat", XMLCONCAT, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlelement", XMLELEMENT, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlexists", XMLEXISTS, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlforest", XMLFOREST, COL_NAME_KEYWORD)
+PG_KEYWORD("xmlnamespaces", XMLNAMESPACES, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlparse", XMLPARSE, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlpi", XMLPI, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlroot", XMLROOT, COL_NAME_KEYWORD)
 PG_KEYWORD("xmlserialize", XMLSERIALIZE, COL_NAME_KEYWORD)
+PG_KEYWORD("xmltable", XMLTABLE, COL_NAME_KEYWORD)
 PG_KEYWORD("year", YEAR_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("yes", YES_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("zone", ZONE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 66519e6..1218bf3 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -58,6 +58,10 @@ extern Node *coerce_to_specific_type(ParseState *pstate, Node *node,
 						Oid targetTypeId,
 						const char *constructName);
 
+extern Node *coerce_to_specific_type_typmod(ParseState *pstate, Node *node,
+						Oid targetTypeId, int32 targetTypmod,
+						const char *constructName);
+
 extern int parser_coercion_errposition(ParseState *pstate,
 							int coerce_location,
 							Node *input_expr);
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 2eab8a5..5cf94f9 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -109,4 +109,17 @@ extern int	xmlbinary;			/* XmlBinaryType, but int for guc enum */
 
 extern int	xmloption;			/* XmlOptionType, but int for guc enum */
 
+extern struct XmlTableContext *initXmlTableContext(xmltype *xmlval,
+				char *default_ns_name,
+				int ncols,
+				FmgrInfo *in_functions, Oid *typioparams,
+				MemoryContext	per_rowgroup_memory);
+extern void XmlTableFreeContext(struct XmlTableContext *xtCxt);
+extern void XmlTableSetNs(struct XmlTableContext *xtCxt, char *name, char *uri);
+extern void XmlTableSetRowPath(struct XmlTableContext *xtCxt, text *row_path);
+extern void XmlTableSetColumnPath(struct XmlTableContext *xtCxt, int i, Oid typid, char *path);
+extern bool XmlTableFetchRow(struct XmlTableContext *xtCxt);
+extern Datum XmlTableGetValue(struct XmlTableContext *xtCxt, int ncol, Oid typid, int32 typmod, bool *isnull);
+extern Datum XmlTableGetRowValue(struct XmlTableContext *xtCxt, bool *isnull);
+
 #endif   /* XML_H */
diff --git a/src/test/regress/expected/xml.out b/src/test/regress/expected/xml.out
index f21e119..d94f20b 100644
--- a/src/test/regress/expected/xml.out
+++ b/src/test/regress/expected/xml.out
@@ -948,3 +948,105 @@ SELECT XMLPARSE(DOCUMENT '<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4
  <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"><chapter>&nbsp;</chapter>
 (1 row)
 
+-- XMLPATH tests
+CREATE TABLE xmldata(data xml);
+INSERT INTO xmldata VALUES('<ROWS>
+<ROW id="1">
+  <COUNTRY_ID>AU</COUNTRY_ID>
+  <COUNTRY_NAME>Australia</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="2">
+  <COUNTRY_ID>CN</COUNTRY_ID>
+  <COUNTRY_NAME>China</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="3">
+  <COUNTRY_ID>HK</COUNTRY_ID>
+  <COUNTRY_NAME>HongKong</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="4">
+  <COUNTRY_ID>IN</COUNTRY_ID>
+  <COUNTRY_NAME>India</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="5">
+  <COUNTRY_ID>JP</COUNTRY_ID>
+  <COUNTRY_NAME>Japan</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><PREMIER_NAME>Sinzo Abe</PREMIER_NAME>
+</ROW>
+<ROW id="6">
+  <COUNTRY_ID>SG</COUNTRY_ID>
+  <COUNTRY_NAME>Singapore</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><SIZE unit="km">791</SIZE>
+</ROW>
+</ROWS>');
+-- XMLTABLE without columns
+SELECT * FROM XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+  xmltable   
+-------------
+ <row>      +
+   <a>10</a>+
+   <a>20</a>+
+ </row>
+(1 row)
+
+SELECT XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+  xmltable   
+-------------
+ ("<row>    +
+   <a>10</a>+
+   <a>20</a>+
+ </row>")
+(1 row)
+
+-- XMLTABLE with columns
+SELECT  xmltable.*
+   FROM (SELECT data FROM xmldata) x,
+        LATERAL XMLTABLE('/ROWS/ROW'
+                         PASSING data
+                         COLUMNS id int PATH '@id',
+                                  _id FOR ORDINALITY,
+                                  country_name text PATH 'COUNTRY_NAME' NOT NULL,
+                                  country_id text PATH 'COUNTRY_ID',
+                                  region_id int PATH 'REGION_ID',
+                                  size float PATH 'SIZE',
+                                  unit text PATH 'SIZE/@unit',
+                                  premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified');
+ id | _id | country_name | country_id | region_id | size | unit | premier_name  
+----+-----+--------------+------------+-----------+------+------+---------------
+  1 |   1 | Australia    | AU         |         3 |      |      | not specified
+  2 |   2 | China        | CN         |         3 |      |      | not specified
+  3 |   3 | HongKong     | HK         |         3 |      |      | not specified
+  4 |   4 | India        | IN         |         3 |      |      | not specified
+  5 |   5 | Japan        | JP         |         3 |      |      | Sinzo Abe
+  6 |   6 | Singapore    | SG         |         3 |  791 | km   | not specified
+(6 rows)
+
+-- XMLNAMESPACES tests
+SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz)
+                      '/zz:rows/zz:row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'zz:a');
+ a  
+----
+ 10
+(1 row)
+
+SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y')
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ a  
+----
+ 10
+(1 row)
+
+SELECT * FROM XMLTABLE('/rows/row/a/text()' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+ xmltable 
+----------
+ 10
+ 20
+(2 rows)
+
diff --git a/src/test/regress/expected/xml_1.out b/src/test/regress/expected/xml_1.out
index d702703..86a1e6f 100644
--- a/src/test/regress/expected/xml_1.out
+++ b/src/test/regress/expected/xml_1.out
@@ -827,3 +827,97 @@ SELECT XMLPARSE(DOCUMENT '<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4
 ERROR:  unsupported XML feature
 DETAIL:  This functionality requires the server to be built with libxml support.
 HINT:  You need to rebuild PostgreSQL using --with-libxml.
+-- XMLPATH tests
+CREATE TABLE xmldata(data xml);
+INSERT INTO xmldata VALUES('<ROWS>
+<ROW id="1">
+  <COUNTRY_ID>AU</COUNTRY_ID>
+  <COUNTRY_NAME>Australia</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="2">
+  <COUNTRY_ID>CN</COUNTRY_ID>
+  <COUNTRY_NAME>China</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="3">
+  <COUNTRY_ID>HK</COUNTRY_ID>
+  <COUNTRY_NAME>HongKong</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="4">
+  <COUNTRY_ID>IN</COUNTRY_ID>
+  <COUNTRY_NAME>India</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="5">
+  <COUNTRY_ID>JP</COUNTRY_ID>
+  <COUNTRY_NAME>Japan</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><PREMIER_NAME>Sinzo Abe</PREMIER_NAME>
+</ROW>
+<ROW id="6">
+  <COUNTRY_ID>SG</COUNTRY_ID>
+  <COUNTRY_NAME>Singapore</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><SIZE unit="km">791</SIZE>
+</ROW>
+</ROWS>');
+ERROR:  unsupported XML feature
+LINE 1: INSERT INTO xmldata VALUES('<ROWS>
+                                   ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+-- XMLTABLE without columns
+SELECT * FROM XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+ERROR:  unsupported XML feature
+LINE 1: SELECT * FROM XMLTABLE('/rows/row' PASSING '<rows><row><a>10...
+                                                   ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+ERROR:  unsupported XML feature
+LINE 1: SELECT XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>...
+                                            ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+-- XMLTABLE with columns
+SELECT  xmltable.*
+   FROM (SELECT data FROM xmldata) x,
+        LATERAL XMLTABLE('/ROWS/ROW'
+                         PASSING data
+                         COLUMNS id int PATH '@id',
+                                  _id FOR ORDINALITY,
+                                  country_name text PATH 'COUNTRY_NAME' NOT NULL,
+                                  country_id text PATH 'COUNTRY_ID',
+                                  region_id int PATH 'REGION_ID',
+                                  size float PATH 'SIZE',
+                                  unit text PATH 'SIZE/@unit',
+                                  premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified');
+ id | _id | country_name | country_id | region_id | size | unit | premier_name 
+----+-----+--------------+------------+-----------+------+------+--------------
+(0 rows)
+
+-- XMLNAMESPACES tests
+SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz)
+                      '/zz:rows/zz:row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'zz:a');
+ERROR:  unsupported XML feature
+LINE 3:                       PASSING '<rows xmlns="http://x.y"><row...
+                                      ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y')
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ERROR:  unsupported XML feature
+LINE 3:                       PASSING '<rows xmlns="http://x.y"><row...
+                                      ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
+SELECT * FROM XMLTABLE('/rows/row/a/text()' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+ERROR:  unsupported XML feature
+LINE 1: ...LECT * FROM XMLTABLE('/rows/row/a/text()' PASSING '<rows><ro...
+                                                             ^
+DETAIL:  This functionality requires the server to be built with libxml support.
+HINT:  You need to rebuild PostgreSQL using --with-libxml.
diff --git a/src/test/regress/expected/xml_2.out b/src/test/regress/expected/xml_2.out
index 530faf5..fdddd28 100644
--- a/src/test/regress/expected/xml_2.out
+++ b/src/test/regress/expected/xml_2.out
@@ -928,3 +928,105 @@ SELECT XMLPARSE(DOCUMENT '<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4
  <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"><chapter>&nbsp;</chapter>
 (1 row)
 
+-- XMLPATH tests
+CREATE TABLE xmldata(data xml);
+INSERT INTO xmldata VALUES('<ROWS>
+<ROW id="1">
+  <COUNTRY_ID>AU</COUNTRY_ID>
+  <COUNTRY_NAME>Australia</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="2">
+  <COUNTRY_ID>CN</COUNTRY_ID>
+  <COUNTRY_NAME>China</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="3">
+  <COUNTRY_ID>HK</COUNTRY_ID>
+  <COUNTRY_NAME>HongKong</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="4">
+  <COUNTRY_ID>IN</COUNTRY_ID>
+  <COUNTRY_NAME>India</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="5">
+  <COUNTRY_ID>JP</COUNTRY_ID>
+  <COUNTRY_NAME>Japan</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><PREMIER_NAME>Sinzo Abe</PREMIER_NAME>
+</ROW>
+<ROW id="6">
+  <COUNTRY_ID>SG</COUNTRY_ID>
+  <COUNTRY_NAME>Singapore</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><SIZE unit="km">791</SIZE>
+</ROW>
+</ROWS>');
+-- XMLTABLE without columns
+SELECT * FROM XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+  xmltable   
+-------------
+ <row>      +
+   <a>10</a>+
+   <a>20</a>+
+ </row>
+(1 row)
+
+SELECT XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+  xmltable   
+-------------
+ ("<row>    +
+   <a>10</a>+
+   <a>20</a>+
+ </row>")
+(1 row)
+
+-- XMLTABLE with columns
+SELECT  xmltable.*
+   FROM (SELECT data FROM xmldata) x,
+        LATERAL XMLTABLE('/ROWS/ROW'
+                         PASSING data
+                         COLUMNS id int PATH '@id',
+                                  _id FOR ORDINALITY,
+                                  country_name text PATH 'COUNTRY_NAME' NOT NULL,
+                                  country_id text PATH 'COUNTRY_ID',
+                                  region_id int PATH 'REGION_ID',
+                                  size float PATH 'SIZE',
+                                  unit text PATH 'SIZE/@unit',
+                                  premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified');
+ id | _id | country_name | country_id | region_id | size | unit | premier_name  
+----+-----+--------------+------------+-----------+------+------+---------------
+  1 |   1 | Australia    | AU         |         3 |      |      | not specified
+  2 |   2 | China        | CN         |         3 |      |      | not specified
+  3 |   3 | HongKong     | HK         |         3 |      |      | not specified
+  4 |   4 | India        | IN         |         3 |      |      | not specified
+  5 |   5 | Japan        | JP         |         3 |      |      | Sinzo Abe
+  6 |   6 | Singapore    | SG         |         3 |  791 | km   | not specified
+(6 rows)
+
+-- XMLNAMESPACES tests
+SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz)
+                      '/zz:rows/zz:row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'zz:a');
+ a  
+----
+ 10
+(1 row)
+
+SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y')
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+ a  
+----
+ 10
+(1 row)
+
+SELECT * FROM XMLTABLE('/rows/row/a/text()' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+ xmltable 
+----------
+ 10
+ 20
+(2 rows)
+
diff --git a/src/test/regress/sql/xml.sql b/src/test/regress/sql/xml.sql
index 08a0b30..8599084 100644
--- a/src/test/regress/sql/xml.sql
+++ b/src/test/regress/sql/xml.sql
@@ -270,3 +270,69 @@ SELECT XMLPARSE(DOCUMENT '<!DOCTYPE foo [<!ENTITY c SYSTEM "/etc/passwd">]><foo>
 SELECT XMLPARSE(DOCUMENT '<!DOCTYPE foo [<!ENTITY c SYSTEM "/etc/no.such.file">]><foo>&c;</foo>');
 -- This might or might not load the requested DTD, but it mustn't throw error.
 SELECT XMLPARSE(DOCUMENT '<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd"><chapter>&nbsp;</chapter>');
+
+-- XMLPATH tests
+CREATE TABLE xmldata(data xml);
+INSERT INTO xmldata VALUES('<ROWS>
+<ROW id="1">
+  <COUNTRY_ID>AU</COUNTRY_ID>
+  <COUNTRY_NAME>Australia</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="2">
+  <COUNTRY_ID>CN</COUNTRY_ID>
+  <COUNTRY_NAME>China</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="3">
+  <COUNTRY_ID>HK</COUNTRY_ID>
+  <COUNTRY_NAME>HongKong</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="4">
+  <COUNTRY_ID>IN</COUNTRY_ID>
+  <COUNTRY_NAME>India</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID>
+</ROW>
+<ROW id="5">
+  <COUNTRY_ID>JP</COUNTRY_ID>
+  <COUNTRY_NAME>Japan</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><PREMIER_NAME>Sinzo Abe</PREMIER_NAME>
+</ROW>
+<ROW id="6">
+  <COUNTRY_ID>SG</COUNTRY_ID>
+  <COUNTRY_NAME>Singapore</COUNTRY_NAME>
+  <REGION_ID>3</REGION_ID><SIZE unit="km">791</SIZE>
+</ROW>
+</ROWS>');
+
+-- XMLTABLE without columns
+SELECT * FROM XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+SELECT XMLTABLE('/rows/row' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
+
+-- XMLTABLE with columns
+SELECT  xmltable.*
+   FROM (SELECT data FROM xmldata) x,
+        LATERAL XMLTABLE('/ROWS/ROW'
+                         PASSING data
+                         COLUMNS id int PATH '@id',
+                                  _id FOR ORDINALITY,
+                                  country_name text PATH 'COUNTRY_NAME' NOT NULL,
+                                  country_id text PATH 'COUNTRY_ID',
+                                  region_id int PATH 'REGION_ID',
+                                  size float PATH 'SIZE',
+                                  unit text PATH 'SIZE/@unit',
+                                  premier_name text PATH 'PREMIER_NAME' DEFAULT 'not specified');
+
+-- XMLNAMESPACES tests
+SELECT * FROM XMLTABLE(XMLNAMESPACES('http://x.y' AS zz)
+                      '/zz:rows/zz:row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'zz:a');
+
+SELECT * FROM XMLTABLE(XMLNAMESPACES(DEFAULT 'http://x.y')
+                      '/rows/row'
+                      PASSING '<rows xmlns="http://x.y"><row><a>10</a></row></rows>'
+                      COLUMNS a int PATH 'a');
+
+SELECT * FROM XMLTABLE('/rows/row/a/text()' PASSING '<rows><row><a>10</a><a>20</a></row></rows>');
