1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-17 06:41:09 +03:00

Initial SQL/XML support: xml data type and initial set of functions.

This commit is contained in:
Peter Eisentraut
2006-12-21 16:05:16 +00:00
parent ed1e9cd501
commit 8c1de5fb00
39 changed files with 2446 additions and 128 deletions

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.568 2006/11/05 22:42:09 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.569 2006/12/21 16:05:14 petere Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -106,6 +106,7 @@ static void insertSelectOptions(SelectStmt *stmt,
static Node *makeSetOp(SetOperation op, bool all, Node *larg, Node *rarg);
static Node *doNegate(Node *n, int location);
static void doNegateFloat(Value *v);
static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args);
%}
@ -345,6 +346,11 @@ static void doNegateFloat(Value *v);
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version
%type <ival> opt_xml_root_standalone document_or_content xml_whitespace_option
/*
* If you make any token changes, update the keyword table in
@ -365,13 +371,13 @@ static void doNegateFloat(Value *v);
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
COMMITTED CONCURRENTLY CONNECTION CONSTRAINT CONSTRAINTS
CONVERSION_P CONVERT COPY CREATE CREATEDB
CONTENT CONVERSION_P CONVERT COPY CREATE CREATEDB
CREATEROLE CREATEUSER CROSS CSV CURRENT_DATE CURRENT_ROLE CURRENT_TIME
CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISABLE_P DISTINCT DO DOMAIN_P DOUBLE_P DROP
DESC DISABLE_P DISTINCT DO DOCUMENT DOMAIN_P DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
@ -398,7 +404,7 @@ static void doNegateFloat(Value *v);
MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB
NAME NAMES NATIONAL NATURAL NCHAR NEW NEXT NO NOCREATEDB
NOCREATEROLE NOCREATEUSER NOINHERIT NOLOGIN_P NONE NOSUPERUSER
NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NUMERIC
@ -417,8 +423,8 @@ static void doNegateFloat(Value *v);
SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SUPERUSER_P SYMMETRIC
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE START STATEMENT
STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP SUBSTRING SUPERUSER_P SYMMETRIC
SYSID SYSTEM_P
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
@ -428,12 +434,15 @@ static void doNegateFloat(Value *v);
UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USER USING
VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
VERBOSE VIEW VOLATILE
VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING
VERBOSE VERSION VIEW VOLATILE
WHEN WHERE WITH WITHOUT WORK WRITE
WHEN WHERE WHITESPACE WITH WITHOUT WORK WRITE
YEAR_P
XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLFOREST XMLPARSE
XMLPI XMLROOT XMLSERIALIZE
YEAR_P YES
ZONE
@ -484,6 +493,7 @@ static void doNegateFloat(Value *v);
* left-associativity among the JOIN rules themselves.
*/
%left JOIN CROSS LEFT FULL RIGHT INNER_P NATURAL
%right PRESERVE STRIP
%%
/*
@ -7868,6 +7878,146 @@ func_expr: func_name '(' ')'
v->op = IS_LEAST;
$$ = (Node *)v;
}
| XMLCONCAT '(' expr_list ')'
{
$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NULL, $3);
}
| XMLELEMENT '(' NAME ColLabel ')'
{
$$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, NULL);
}
| XMLELEMENT '(' NAME ColLabel ',' xml_attributes ')'
{
$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, NULL);
}
| XMLELEMENT '(' NAME ColLabel ',' expr_list ')'
{
$$ = makeXmlExpr(IS_XMLELEMENT, $4, NULL, $6);
}
| XMLELEMENT '(' NAME ColLabel ',' xml_attributes ',' expr_list ')'
{
$$ = makeXmlExpr(IS_XMLELEMENT, $4, $6, $8);
}
| XMLFOREST '(' xml_attribute_list ')'
{
$$ = makeXmlExpr(IS_XMLFOREST, NULL, $3, NULL);
}
| XMLPARSE '(' document_or_content a_expr xml_whitespace_option ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = SystemFuncName("xmlparse");
n->args = list_make3(makeBoolAConst($3 == DOCUMENT), $4, makeBoolAConst($5 == PRESERVE));
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| XMLPI '(' NAME ColLabel ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = SystemFuncName("xmlpi");
n->args = list_make1(makeStringConst($4, NULL));
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| XMLPI '(' NAME ColLabel ',' a_expr ')'
{
FuncCall *n = makeNode(FuncCall);
n->funcname = SystemFuncName("xmlpi");
n->args = list_make2(makeStringConst($4, NULL), $6);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| XMLROOT '(' a_expr ',' xml_root_version opt_xml_root_standalone ')'
{
FuncCall *n = makeNode(FuncCall);
Node *ver;
A_Const *sa;
if ($5)
ver = $5;
else
{
A_Const *val;
val = makeNode(A_Const);
val->val.type = T_Null;
ver = (Node *) val;
}
if ($6)
sa = makeBoolAConst($6 == 1);
else
{
sa = makeNode(A_Const);
sa->val.type = T_Null;
}
n->funcname = SystemFuncName("xmlroot");
n->args = list_make3($3, ver, sa);
n->agg_star = FALSE;
n->agg_distinct = FALSE;
n->location = @1;
$$ = (Node *)n;
}
| XMLSERIALIZE '(' document_or_content a_expr AS Typename ')'
{
/*
* FIXME: This should be made distinguishable from
* CAST (for reverse compilation at least).
*/
$$ = makeTypeCast($4, $6);
}
;
/*
* SQL/XML support
*/
xml_root_version: VERSION a_expr { $$ = $2; }
| VERSION NO VALUE { $$ = NULL; }
;
opt_xml_root_standalone: ',' STANDALONE YES { $$ = 1; }
| ',' STANDALONE NO { $$ = -1; }
| ',' STANDALONE NO VALUE { $$ = 0; }
| /*EMPTY*/ { $$ = 0; }
;
xml_attributes: XMLATTRIBUTES '(' xml_attribute_list ')' { $$ = $3; }
;
xml_attribute_list: xml_attribute_el { $$ = list_make1($1); }
| xml_attribute_list ',' xml_attribute_el { $$ = lappend($1, $3); }
;
xml_attribute_el: a_expr AS ColLabel
{
$$ = makeNode(ResTarget);
$$->name = $3;
$$->indirection = NULL;
$$->val = (Node *) $1;
}
| a_expr
{
$$ = makeNode(ResTarget);
$$->name = NULL;
$$->indirection = NULL;
$$->val = (Node *) $1;
}
;
document_or_content: DOCUMENT { $$ = DOCUMENT; }
| CONTENT { $$ = CONTENT; }
;
xml_whitespace_option: PRESERVE WHITESPACE { $$ = PRESERVE; }
| STRIP WHITESPACE { $$ = STRIP; }
| /*EMPTY*/ { $$ = STRIP; }
;
/*
@ -8562,6 +8712,7 @@ unreserved_keyword:
| CONCURRENTLY
| CONNECTION
| CONSTRAINTS
| CONTENT
| CONVERSION_P
| COPY
| CREATEDB
@ -8581,6 +8732,7 @@ unreserved_keyword:
| DELIMITER
| DELIMITERS
| DISABLE_P
| DOCUMENT
| DOMAIN_P
| DOUBLE_P
| DROP
@ -8640,6 +8792,7 @@ unreserved_keyword:
| MODE
| MONTH_P
| MOVE
| NAME
| NAMES
| NEXT
| NO
@ -8700,12 +8853,14 @@ unreserved_keyword:
| SHOW
| SIMPLE
| STABLE
| STANDALONE
| START
| STATEMENT
| STATISTICS
| STDIN
| STDOUT
| STORAGE
| STRIP
| SUPERUSER_P
| SYSID
| SYSTEM_P
@ -8729,13 +8884,17 @@ unreserved_keyword:
| VALID
| VALIDATOR
| VARYING
| VERSION
| VIEW
| VALUE
| VOLATILE
| WHITESPACE
| WITH
| WITHOUT
| WORK
| WRITE
| YEAR_P
| YES
| ZONE
;
@ -8788,6 +8947,14 @@ col_name_keyword:
| TRIM
| VALUES
| VARCHAR
| XMLATTRIBUTES
| XMLELEMENT
| XMLCONCAT
| XMLFOREST
| XMLPARSE
| XMLPI
| XMLROOT
| XMLSERIALIZE
;
/* Function identifier --- keywords that can be function names.
@ -9322,6 +9489,17 @@ doNegateFloat(Value *v)
}
}
static Node *
makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
{
XmlExpr *x = makeNode(XmlExpr);
x->op = op;
x->name = name;
x->named_args = named_args;
x->args = args;
return (Node *) x;
}
/*
* Must undefine base_yylex before including scan.c, since we want it
* to create the function base_yylex not filtered_base_yylex.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.177 2006/10/07 21:51:02 petere Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.178 2006/12/21 16:05:14 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -89,6 +89,7 @@ static const ScanKeyword ScanKeywords[] = {
{"connection", CONNECTION},
{"constraint", CONSTRAINT},
{"constraints", CONSTRAINTS},
{"content", CONTENT},
{"conversion", CONVERSION_P},
{"convert", CONVERT},
{"copy", COPY},
@ -123,6 +124,7 @@ static const ScanKeyword ScanKeywords[] = {
{"disable", DISABLE_P},
{"distinct", DISTINCT},
{"do", DO},
{"document", DOCUMENT},
{"domain", DOMAIN_P},
{"double", DOUBLE_P},
{"drop", DROP},
@ -218,6 +220,7 @@ static const ScanKeyword ScanKeywords[] = {
{"mode", MODE},
{"month", MONTH_P},
{"move", MOVE},
{"name", NAME},
{"names", NAMES},
{"national", NATIONAL},
{"natural", NATURAL},
@ -314,6 +317,7 @@ static const ScanKeyword ScanKeywords[] = {
{"smallint", SMALLINT},
{"some", SOME},
{"stable", STABLE},
{"standalone", STANDALONE},
{"start", START},
{"statement", STATEMENT},
{"statistics", STATISTICS},
@ -321,6 +325,7 @@ static const ScanKeyword ScanKeywords[] = {
{"stdout", STDOUT},
{"storage", STORAGE},
{"strict", STRICT_P},
{"strip", STRIP},
{"substring", SUBSTRING},
{"superuser", SUPERUSER_P},
{"symmetric", SYMMETRIC},
@ -357,19 +362,31 @@ static const ScanKeyword ScanKeywords[] = {
{"vacuum", VACUUM},
{"valid", VALID},
{"validator", VALIDATOR},
{"value", VALUE},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},
{"verbose", VERBOSE},
{"version", VERSION},
{"view", VIEW},
{"volatile", VOLATILE},
{"when", WHEN},
{"where", WHERE},
{"whitespace", WHITESPACE},
{"with", WITH},
{"without", WITHOUT},
{"work", WORK},
{"write", WRITE},
{"xmlattributes", XMLATTRIBUTES},
{"xmlconcat", XMLCONCAT},
{"xmlelement", XMLELEMENT},
{"xmlforest", XMLFOREST},
{"xmlparse", XMLPARSE},
{"xmlpi", XMLPI},
{"xmlroot", XMLROOT},
{"xmlserialize", XMLSERIALIZE},
{"year", YEAR_P},
{"yes", YES},
{"zone", ZONE},
};

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.147 2006/12/10 22:13:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.148 2006/12/21 16:05:14 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -919,6 +919,46 @@ coerce_to_bigint(ParseState *pstate, Node *node,
return node;
}
/*
* coerce_to_xml()
* Coerce an argument of a construct that requires xml input.
* Also check that input is not a set.
*
* Returns the possibly-transformed node tree.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_xml(ParseState *pstate, Node *node,
const char *constructName)
{
Oid inputTypeId = exprType(node);
if (inputTypeId != XMLOID)
{
node = coerce_to_target_type(pstate, node, inputTypeId,
XMLOID, -1,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST);
if (node == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
/* translator: first %s is name of a SQL construct, eg LIMIT */
errmsg("argument of %s must be type xml, not type %s",
constructName, format_type_be(inputTypeId))));
}
if (expression_returns_set(node))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
/* translator: %s is name of a SQL construct, eg LIMIT */
errmsg("argument of %s must not return a set",
constructName)));
return node;
}
/* select_common_type()
* Determine the common supertype of a list of input expression types.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.199 2006/12/10 22:13:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.200 2006/12/21 16:05:14 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,6 +33,7 @@
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/xml.h"
bool Transform_null_equals = false;
@ -55,6 +56,7 @@ static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
@ -232,6 +234,10 @@ transformExpr(ParseState *pstate, Node *expr)
result = transformBooleanTest(pstate, (BooleanTest *) expr);
break;
case T_XmlExpr:
result = transformXmlExpr(pstate, (XmlExpr *) expr);
break;
/*********************************************
* Quietly accept node types that may be presented when we are
* called on an already-transformed tree.
@ -1409,6 +1415,56 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b)
return (Node *) b;
}
static Node *
transformXmlExpr(ParseState *pstate, XmlExpr *x)
{
ListCell *lc;
XmlExpr *newx = makeNode(XmlExpr);
newx->op = x->op;
if (x->name)
newx->name = map_sql_identifier_to_xml_name(x->name, false);
else
newx->name = NULL;
foreach(lc, x->named_args)
{
ResTarget *r = (ResTarget *) lfirst(lc);
Node *expr = transformExpr(pstate, r->val);
char *argname = NULL;
if (r->name)
argname = map_sql_identifier_to_xml_name(r->name, false);
else if (IsA(r->val, ColumnRef))
argname = map_sql_identifier_to_xml_name(FigureColname(r->val), true);
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
x->op == IS_XMLELEMENT
? errmsg("unnamed attribute value must be a column reference")
: errmsg("unnamed element value must be a column reference")));
newx->named_args = lappend(newx->named_args,
makeTargetEntry((Expr *) expr, 0, argname, false));
}
foreach(lc, x->args)
{
Node *e = (Node *) lfirst(lc);
Node *newe;
newe = coerce_to_xml(pstate, transformExpr(pstate, e),
(x->op == IS_XMLCONCAT
? "XMLCONCAT"
: (x->op == IS_XMLELEMENT
? "XMLELEMENT"
: "XMLFOREST")));
newx->args = lappend(newx->args, newe);
}
return (Node *) newx;
}
/*
* Construct a whole-row reference to represent the notation "relation.*".
*
@ -1668,6 +1724,9 @@ exprType(Node *expr)
case T_BooleanTest:
type = BOOLOID;
break;
case T_XmlExpr:
type = XMLOID;
break;
case T_CoerceToDomain:
type = ((CoerceToDomain *) expr)->resulttype;
break;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.149 2006/10/04 00:29:56 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.150 2006/12/21 16:05:14 petere Exp $
*
*-------------------------------------------------------------------------
*/
@ -1315,6 +1315,21 @@ FigureColnameInternal(Node *node, char **name)
return 2;
}
break;
case T_XmlExpr:
/* make SQL/XML functions act like a regular function */
switch (((XmlExpr*) node)->op)
{
case IS_XMLCONCAT:
*name = "xmlconcat";
return 2;
case IS_XMLELEMENT:
*name = "xmlelement";
return 2;
case IS_XMLFOREST:
*name = "xmlforest";
return 2;
}
break;
default:
break;
}