1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-14 18:42:34 +03:00

Add support for multi-row VALUES clauses as part of INSERT statements

(e.g. "INSERT ... VALUES (...), (...), ...") and elsewhere as allowed
by the spec. (e.g. similar to a FROM clause subselect). initdb required.
Joe Conway and Tom Lane.
This commit is contained in:
Joe Conway
2006-08-02 01:59:48 +00:00
parent d307c428cb
commit 9caafda579
40 changed files with 1877 additions and 313 deletions

View File

@ -2,7 +2,7 @@
* ruleutils.c - Functions to convert stored expressions/querytrees
* back to source text
*
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.229 2006/07/27 19:52:06 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.230 2006/08/02 01:59:47 joe Exp $
**********************************************************************/
#include "postgres.h"
@ -131,6 +131,7 @@ static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags);
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
TupleDesc resultDesc, int prettyFlags, int startIndent);
static void get_values_def(List *values_lists, deparse_context *context);
static void get_select_query_def(Query *query, deparse_context *context,
TupleDesc resultDesc);
static void get_insert_query_def(Query *query, deparse_context *context);
@ -172,7 +173,8 @@ static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
deparse_context *context);
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static Node *processIndirection(Node *node, deparse_context *context,
bool printit);
static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
@ -1800,6 +1802,50 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
}
}
/* ----------
* get_values_def - Parse back a VALUES list
* ----------
*/
static void
get_values_def(List *values_lists, deparse_context *context)
{
StringInfo buf = context->buf;
bool first_list = true;
ListCell *vtl;
appendStringInfoString(buf, "VALUES ");
foreach(vtl, values_lists)
{
List *sublist = (List *) lfirst(vtl);
bool first_col = true;
ListCell *lc;
if (first_list)
first_list = false;
else
appendStringInfoString(buf, ", ");
appendStringInfoChar(buf, '(');
foreach(lc, sublist)
{
Node *col = (Node *) lfirst(lc);
if (first_col)
first_col = false;
else
appendStringInfoChar(buf, ',');
/*
* Strip any top-level nodes representing indirection assignments,
* then print the result.
*/
get_rule_expr(processIndirection(col, context, false),
context, false);
}
appendStringInfoChar(buf, ')');
}
}
/* ----------
* get_select_query_def - Parse back a SELECT parsetree
@ -1910,14 +1956,37 @@ get_basic_select_query(Query *query, deparse_context *context,
ListCell *l;
int colno;
/*
* Build up the query string - first we say SELECT
*/
if (PRETTY_INDENT(context))
{
context->indentLevel += PRETTYINDENT_STD;
appendStringInfoChar(buf, ' ');
}
/*
* If the query looks like SELECT * FROM (VALUES ...), then print just
* the VALUES part. This reverses what transformValuesClause() did at
* parse time. If the jointree contains just a single VALUES RTE,
* we assume this case applies (without looking at the targetlist...)
*/
if (list_length(query->jointree->fromlist) == 1)
{
RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist);
if (IsA(rtr, RangeTblRef))
{
RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
if (rte->rtekind == RTE_VALUES)
{
get_values_def(rte->values_lists, context);
return;
}
}
}
/*
* Build up the query string - first we say SELECT
*/
appendStringInfo(buf, "SELECT");
/* Add the DISTINCT clause if given */
@ -2191,24 +2260,37 @@ get_insert_query_def(Query *query, deparse_context *context)
{
StringInfo buf = context->buf;
RangeTblEntry *select_rte = NULL;
RangeTblEntry *values_rte = NULL;
RangeTblEntry *rte;
char *sep;
ListCell *values_cell;
ListCell *l;
List *strippedexprs;
/*
* If it's an INSERT ... SELECT there will be a single subquery RTE for
* the SELECT.
* If it's an INSERT ... SELECT or VALUES (...), (...), ...
* there will be a single RTE for the SELECT or VALUES.
*/
foreach(l, query->rtable)
{
rte = (RangeTblEntry *) lfirst(l);
if (rte->rtekind != RTE_SUBQUERY)
continue;
if (select_rte)
elog(ERROR, "too many RTEs in INSERT");
select_rte = rte;
if (rte->rtekind == RTE_SUBQUERY)
{
if (select_rte)
elog(ERROR, "too many subquery RTEs in INSERT");
select_rte = rte;
}
if (rte->rtekind == RTE_VALUES)
{
if (values_rte)
elog(ERROR, "too many values RTEs in INSERT");
values_rte = rte;
}
}
if (select_rte && values_rte)
elog(ERROR, "both subquery and values RTEs in INSERT");
/*
* Start the query with INSERT INTO relname
@ -2225,9 +2307,17 @@ get_insert_query_def(Query *query, deparse_context *context)
generate_relation_name(rte->relid));
/*
* Add the insert-column-names list, and make a list of the actual
* assignment source expressions.
* Add the insert-column-names list. To handle indirection properly,
* we need to look for indirection nodes in the top targetlist (if it's
* INSERT ... SELECT or INSERT ... single VALUES), or in the first
* expression list of the VALUES RTE (if it's INSERT ... multi VALUES).
* We assume that all the expression lists will have similar indirection
* in the latter case.
*/
if (values_rte)
values_cell = list_head((List *) linitial(values_rte->values_lists));
else
values_cell = NULL;
strippedexprs = NIL;
sep = "";
foreach(l, query->targetList)
@ -2252,23 +2342,41 @@ get_insert_query_def(Query *query, deparse_context *context)
* Print any indirection needed (subfields or subscripts), and strip
* off the top-level nodes representing the indirection assignments.
*/
strippedexprs = lappend(strippedexprs,
processIndirection((Node *) tle->expr,
context));
if (values_cell)
{
/* we discard the stripped expression in this case */
processIndirection((Node *) lfirst(values_cell), context, true);
values_cell = lnext(values_cell);
}
else
{
/* we keep a list of the stripped expressions in this case */
strippedexprs = lappend(strippedexprs,
processIndirection((Node *) tle->expr,
context, true));
}
}
appendStringInfo(buf, ") ");
/* Add the VALUES or the SELECT */
if (select_rte == NULL)
if (select_rte)
{
/* Add the SELECT */
get_query_def(select_rte->subquery, buf, NIL, NULL,
context->prettyFlags, context->indentLevel);
}
else if (values_rte)
{
/* Add the multi-VALUES expression lists */
get_values_def(values_rte->values_lists, context);
}
else
{
/* Add the single-VALUES expression list */
appendContextKeyword(context, "VALUES (",
-PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
get_rule_expr((Node *) strippedexprs, context, false);
appendStringInfoChar(buf, ')');
}
else
get_query_def(select_rte->subquery, buf, NIL, NULL,
context->prettyFlags, context->indentLevel);
}
@ -2323,7 +2431,7 @@ get_update_query_def(Query *query, deparse_context *context)
* Print any indirection needed (subfields or subscripts), and strip
* off the top-level nodes representing the indirection assignments.
*/
expr = processIndirection((Node *) tle->expr, context);
expr = processIndirection((Node *) tle->expr, context, true);
appendStringInfo(buf, " = ");
@ -2612,11 +2720,12 @@ get_name_for_var_field(Var *var, int fieldno,
switch (rte->rtekind)
{
case RTE_RELATION:
case RTE_VALUES:
/*
* This case should not occur: a column of a table shouldn't have
* type RECORD. Fall through and fail (most likely) at the
* bottom.
* This case should not occur: a column of a table or values list
* shouldn't have type RECORD. Fall through and fail
* (most likely) at the bottom.
*/
break;
case RTE_SUBQUERY:
@ -4232,6 +4341,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
/* Function RTE */
get_rule_expr(rte->funcexpr, context, true);
break;
case RTE_VALUES:
/* Values list RTE */
get_values_def(rte->values_lists, context);
break;
default:
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
break;
@ -4576,12 +4689,12 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
* processIndirection - take care of array and subfield assignment
*
* We strip any top-level FieldStore or assignment ArrayRef nodes that
* appear in the input, printing out the appropriate decoration for the
* base column name (that the caller just printed). We return the
* subexpression that's to be assigned.
* appear in the input, and return the subexpression that's to be assigned.
* If printit is true, we also print out the appropriate decoration for the
* base column name (that the caller just printed).
*/
static Node *
processIndirection(Node *node, deparse_context *context)
processIndirection(Node *node, deparse_context *context, bool printit)
{
StringInfo buf = context->buf;
@ -4602,15 +4715,16 @@ processIndirection(Node *node, deparse_context *context)
format_type_be(fstore->resulttype));
/*
* Get the field name. Note we assume here that there's only one
* field being assigned to. This is okay in stored rules but
* Print the field name. Note we assume here that there's only
* one field being assigned to. This is okay in stored rules but
* could be wrong in executable target lists. Presently no
* problem since explain.c doesn't print plan targetlists, but
* someday may have to think of something ...
*/
fieldname = get_relid_attribute_name(typrelid,
linitial_int(fstore->fieldnums));
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
if (printit)
appendStringInfo(buf, ".%s", quote_identifier(fieldname));
/*
* We ignore arg since it should be an uninteresting reference to
@ -4624,7 +4738,8 @@ processIndirection(Node *node, deparse_context *context)
if (aref->refassgnexpr == NULL)
break;
printSubscripts(aref, context);
if (printit)
printSubscripts(aref, context);
/*
* We ignore refexpr since it should be an uninteresting reference