You can subscribe to this list here.
2010 |
Jan
|
Feb
|
Mar
|
Apr
(4) |
May
(28) |
Jun
(12) |
Jul
(11) |
Aug
(12) |
Sep
(5) |
Oct
(19) |
Nov
(14) |
Dec
(12) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2011 |
Jan
(18) |
Feb
(30) |
Mar
(115) |
Apr
(89) |
May
(50) |
Jun
(44) |
Jul
(22) |
Aug
(13) |
Sep
(11) |
Oct
(30) |
Nov
(28) |
Dec
(39) |
2012 |
Jan
(38) |
Feb
(18) |
Mar
(43) |
Apr
(91) |
May
(108) |
Jun
(46) |
Jul
(37) |
Aug
(44) |
Sep
(33) |
Oct
(29) |
Nov
(36) |
Dec
(15) |
2013 |
Jan
(35) |
Feb
(611) |
Mar
(5) |
Apr
(55) |
May
(30) |
Jun
(28) |
Jul
(458) |
Aug
(34) |
Sep
(9) |
Oct
(39) |
Nov
(22) |
Dec
(32) |
2014 |
Jan
(16) |
Feb
(16) |
Mar
(42) |
Apr
(179) |
May
(7) |
Jun
(6) |
Jul
(9) |
Aug
|
Sep
(4) |
Oct
|
Nov
(3) |
Dec
|
2015 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
(4) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
S | M | T | W | T | F | S |
---|---|---|---|---|---|---|
|
|
|
|
|
|
1
|
2
|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
(2) |
12
(5) |
13
(3) |
14
|
15
|
16
|
17
|
18
|
19
|
20
(2) |
21
|
22
|
23
|
24
|
25
(1) |
26
(1) |
27
(2) |
28
(2) |
29
|
30
|
31
|
|
|
|
|
|
From: Michael P. <mic...@us...> - 2011-01-26 07:07:23
|
Project "Postgres-XC". The branch, master has been updated via fe557aa50bcd7401a1f5cf9acec4285904bb4d24 (commit) from c19b46835e586023fbaaf93c3c84e4898f4fe6bd (commit) - Log ----------------------------------------------------------------- commit fe557aa50bcd7401a1f5cf9acec4285904bb4d24 Author: Michael P <mic...@us...> Date: Wed Jan 26 16:01:00 2011 +0900 The patch implements multiple insert syntax in PGXC. Multiple insert means using a single insert statement to insert multiple rows into a table using the syntax e.g. insert into students(rno, class, pos) values (1, 10, 5), (2, 10, 6), (3, 10, 7), (4, 10, 8); Without the patch statements like these pass, but actually do not insert any thing in the table. The main code changes are in re-writer. The patch checks to see if the insert statement has more than one sets in the provided list of values (FOUR in the above example), and in that case rewrites the insert statement. The insert rewriter separates the sets in the provided list of values into independent lists depending on the distribution of the table, the distribution column and the value provided for the distribution column. Next the main re-writer is separated into two possible paths, one without a for loop and if we have a separated list of insert values, we run a for loop on the list and create an insert statement for each of the data nodes providing it that sub-group of the original list that is supposed to run on this particular data node. Main work is done now, all that is left is to handle multiple command result tags from the data nodes. HandleCmdComplete does this, it simply keeps adding into the insert row count until all data nodes are done. With this patch, multi insert does not work for replicated tables. Additional comments are also necessary. diff --git a/src/backend/pgxc/plan/planner.c b/src/backend/pgxc/plan/planner.c index ace4635..1cbf587 100644 --- a/src/backend/pgxc/plan/planner.c +++ b/src/backend/pgxc/plan/planner.c @@ -2754,6 +2754,7 @@ pgxc_planner(Query *query, int cursorOptions, ParamListInfo boundParams) query_step = makeRemoteQuery(); + query_step->exec_nodes = query->execNodes; /* Optimize multi-node handling */ query_step->read_only = query->commandType == CMD_SELECT; @@ -2785,7 +2786,8 @@ pgxc_planner(Query *query, int cursorOptions, ParamListInfo boundParams) if (query->commandType != CMD_SELECT) result->resultRelations = list_make1_int(query->resultRelation); - get_plan_nodes_command(query_step, root); + if (query_step->exec_nodes == NULL) + get_plan_nodes_command(query_step, root); /* standard planner handles correlated UPDATE or DELETE */ if ((query->commandType == CMD_UPDATE || query->commandType == CMD_DELETE) @@ -3244,3 +3246,39 @@ is_pgxc_safe_func(Oid funcid) ReleaseSysCache(tp); return ret_val; } + +/* code is borrowed from get_plan_nodes_insert */ +void +GetHashExecNodes(RelationLocInfo *rel_loc_info, ExecNodes **exec_nodes, const Expr *expr) +{ + /* We may have a cast, try and handle it */ + Expr *checkexpr; + Expr *eval_expr = NULL; + Const *constant; + long part_value; + long *part_value_ptr = NULL; + + eval_expr = (Expr *) eval_const_expressions(NULL, (Node *)expr); + checkexpr = get_numeric_constant(eval_expr); + + if (checkexpr == NULL) + return; + + constant = (Const *) checkexpr; + + if (constant->consttype == INT4OID || + constant->consttype == INT2OID || + constant->consttype == INT8OID) + { + part_value = (long) constant->constvalue; + part_value_ptr = &part_value; + } + + /* single call handles both replicated and partitioned types */ + *exec_nodes = GetRelationNodes(rel_loc_info, part_value_ptr, + RELATION_ACCESS_INSERT); + if (eval_expr) + pfree(eval_expr); + +} + diff --git a/src/backend/pgxc/pool/execRemote.c b/src/backend/pgxc/pool/execRemote.c index 3803fa5..7621c15 100644 --- a/src/backend/pgxc/pool/execRemote.c +++ b/src/backend/pgxc/pool/execRemote.c @@ -815,6 +815,73 @@ HandleError(RemoteQueryState *combiner, char *msg_body, size_t len) combiner->command_complete_count++; } +/* combine deparsed sql statements execution results */ +void +HandleCmdComplete(CmdType commandType, combineTag *combine, + const char *msg_body, size_t len) +{ + int digits = 0; + uint64 originrowcount = 0; + uint64 rowcount = 0; + uint64 total = 0; + + if (msg_body == NULL) + return; + + /* if there's nothing in combine, just copy the msg_body */ + if (strlen(combine->data) == 0) + { + strcpy(combine->data, msg_body); + combine->cmdType = commandType; + return; + } + else + { + /* commandType is conflict */ + if (combine->cmdType != commandType) + return; + + /* get the processed row number from msg_body */ + digits = parse_row_count(msg_body, len + 1, &rowcount); + elog(DEBUG1, "digits is %d\n", digits); + Assert(digits >= 0); + + /* no need to combine */ + if (digits == 0) + return; + + /* combine the processed row number */ + parse_row_count(combine->data, strlen(combine->data) + 1, &originrowcount); + elog(DEBUG1, "originrowcount is %lu, rowcount is %lu\n", originrowcount, rowcount); + total = originrowcount + rowcount; + + } + + /* output command completion tag */ + switch (commandType) + { + case CMD_SELECT: + strcpy(combine->data, "SELECT"); + break; + case CMD_INSERT: + snprintf(combine->data, COMPLETION_TAG_BUFSIZE, + "INSERT %u %lu", 0, total); + break; + case CMD_UPDATE: + snprintf(combine->data, COMPLETION_TAG_BUFSIZE, + "UPDATE %lu", total); + break; + case CMD_DELETE: + snprintf(combine->data, COMPLETION_TAG_BUFSIZE, + "DELETE %lu", total); + break; + default: + strcpy(combine->data, ""); + break; + } + +} + /* * Examine the specified combiner state and determine if command was completed * successfully diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 4fa533f..ca3ec53 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -27,6 +27,11 @@ #include "utils/lsyscache.h" #include "commands/trigger.h" +#ifdef PGXC +#include "pgxc/pgxc.h" +#include "pgxc/poolmgr.h" +#endif + /* We use a list of these to detect recursion in RewriteQuery */ typedef struct rewrite_event @@ -56,7 +61,15 @@ static void markQueryForLocking(Query *qry, Node *jtnode, static List *matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree); static Query *fireRIRrules(Query *parsetree, List *activeRIRs); - +#ifdef PGXC +static int GetRelPartColPos(const Query *query, const char *partColName); +static void ProcessHashValue(List **valuesList, const List *subList, const int node); +static void InitValuesList(List **valuesList[], int size); +static void DestroyValuesList(List **valuesList[]); +static void ProcessRobinValue(Oid relid, List **valuesList, + int size, const RangeTblEntry *values_rte); +static List *RewriteInsertStmt(Query *parsetree, RangeTblEntry *values_rte); +#endif /* * AcquireRewriteLocks - @@ -1619,6 +1632,12 @@ RewriteQuery(Query *parsetree, List *rewrite_events) Query *qual_product = NULL; List *rewritten = NIL; +#ifdef PGXC + List *parsetree_list = NIL; + List *qual_product_list = NIL; + ListCell *pt_cell = NULL; +#endif + /* * If the statement is an update, insert or delete - fire rules on it. * @@ -1681,6 +1700,11 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rewriteTargetList(parsetree, rt_entry_relation, &attrnos); /* ... and the VALUES expression lists */ rewriteValuesRTE(values_rte, rt_entry_relation, attrnos); +#ifdef PGXC + if (IS_PGXC_COORDINATOR && + list_length(values_rte->values_lists) > 1) + parsetree_list = RewriteInsertStmt(parsetree, values_rte); +#endif } else { @@ -1688,7 +1712,11 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rewriteTargetList(parsetree, rt_entry_relation, NULL); } } - + +#ifdef PGXC + if (parsetree_list == NIL) + { +#endif /* * Collect and apply the appropriate rules. */ @@ -1787,8 +1815,142 @@ RewriteQuery(Query *parsetree, List *rewrite_events) } heap_close(rt_entry_relation, NoLock); - } +#ifdef PGXC + } + else + { + foreach(pt_cell, parsetree_list) + { + Query *query; + + query = (Query *)lfirst(pt_cell); + + locks = matchLocks(event, rt_entry_relation->rd_rules, + result_relation, query); + + /* + * Collect and apply the appropriate rules. + */ + locks = matchLocks(event, rt_entry_relation->rd_rules, + result_relation, parsetree); + if (locks != NIL) + { + List *product_queries; + + + product_queries = fireRules(query, + result_relation, + event, + locks, + &instead, + &returning, + &qual_product); + + qual_product_list = lappend(qual_product_list, qual_product); + + /* + * If we got any product queries, recursively rewrite them --- but + * first check for recursion! + */ + if (product_queries != NIL) + { + ListCell *n; + rewrite_event *rev; + + foreach(n, rewrite_events) + { + rev = (rewrite_event *) lfirst(n); + if (rev->relation == RelationGetRelid(rt_entry_relation) && + rev->event == event) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("infinite recursion detected in rules for relation \"%s\"", + RelationGetRelationName(rt_entry_relation)))); + } + + rev = (rewrite_event *) palloc(sizeof(rewrite_event)); + rev->relation = RelationGetRelid(rt_entry_relation); + rev->event = event; + rewrite_events = lcons(rev, rewrite_events); + + foreach(n, product_queries) + { + Query *pt = (Query *) lfirst(n); + List *newstuff; + + newstuff = RewriteQuery(pt, rewrite_events); + rewritten = list_concat(rewritten, newstuff); + } + + rewrite_events = list_delete_first(rewrite_events); + } + } + + /* + * If there is an INSTEAD, and the original query has a RETURNING, we + * have to have found a RETURNING in the rule(s), else fail. (Because + * DefineQueryRewrite only allows RETURNING in unconditional INSTEAD + * rules, there's no need to worry whether the substituted RETURNING + * will actually be executed --- it must be.) + */ + if ((instead || qual_product != NULL) && + query->returningList && + !returning) + { + switch (event) + { + case CMD_INSERT: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot perform INSERT RETURNING on relation \"%s\"", + RelationGetRelationName(rt_entry_relation)), + errhint("You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause."))); + break; + case CMD_UPDATE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot perform UPDATE RETURNING on relation \"%s\"", + RelationGetRelationName(rt_entry_relation)), + errhint("You need an unconditional ON UPDATE DO INSTEAD rule with a RETURNING clause."))); + break; + case CMD_DELETE: + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot perform DELETE RETURNING on relation \"%s\"", + RelationGetRelationName(rt_entry_relation)), + errhint("You need an unconditional ON DELETE DO INSTEAD rule with a RETURNING clause."))); + break; + default: + elog(ERROR, "unrecognized commandType: %d", + (int) event); + break; + } + } + } + heap_close(rt_entry_relation, NoLock); + } + } +#endif + + + + /* + * For INSERTs, the original query is done first; for UPDATE/DELETE, it is + * done last. This is needed because update and delete rule actions might + * not do anything if they are invoked after the update or delete is + * performed. The command counter increment between the query executions + * makes the deleted (and maybe the updated) tuples disappear so the scans + * for them in the rule actions cannot find them. + * + * If we found any unqualified INSTEAD, the original query is not done at + * all, in any form. Otherwise, we add the modified form if qualified + * INSTEADs were found, else the unmodified form. + */ +#ifdef PGXC + if (parsetree_list == NIL) + { +#endif /* * For INSERTs, the original query is done first; for UPDATE/DELETE, it is * done last. This is needed because update and delete rule actions might @@ -1818,11 +1980,52 @@ RewriteQuery(Query *parsetree, List *rewrite_events) rewritten = lappend(rewritten, parsetree); } } +#ifdef PGXC + } + else + { + int query_no = -1; + + foreach(pt_cell, parsetree_list) + { - return rewritten; -} + Query *query; + Query *qual; + query_no ++; + + query = (Query *)lfirst(pt_cell); + if (!instead) + { + if (query->commandType == CMD_INSERT) + { + if (qual_product_list != NIL) + { + qual = (Query *)list_nth(qual_product_list, + query_no); + rewritten = lcons(qual, rewritten); + } + else + rewritten = lcons(query, rewritten); + } + } + else + { + if (qual_product_list != NIL) + { + qual = (Query *)list_nth(qual_product_list, + query_no); + rewritten = lcons(qual, rewritten); + } + else + rewritten = lappend(rewritten, query); + } + } + } +#endif + return rewritten; +} /* * QueryRewrite - * Primary entry point to the query rewriter. @@ -1928,7 +2131,9 @@ QueryRewrite(Query *parsetree) if (query->querySource == QSRC_ORIGINAL) { Assert(query->canSetTag); +#ifndef PGXC Assert(!foundOriginalQuery); +#endif foundOriginalQuery = true; #ifndef USE_ASSERT_CHECKING break; @@ -1949,3 +2154,198 @@ QueryRewrite(Query *parsetree) return results; } + +#ifdef PGXC +static int +GetRelPartColPos(const Query *query, const char *partColName) +{ + ListCell *lc; + int rescol = -1; + + foreach(lc, query->targetList) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + + if (tle->resjunk) + continue; + + rescol += 1; + + /* + * See if we have a constant expression comparing against the + * designated partitioned column + */ + if (strcmp(tle->resname, partColName) == 0) + break; + } + + if (rescol == -1) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("Can't find partition column"))); + + return rescol; +} + +static void +ProcessHashValue(List **valuesList, const List *subList, const int node) +{ + valuesList[node - 1] = lappend(valuesList[node - 1], subList); +} + +static void +InitValuesList(List **valuesList[], int size) +{ + *valuesList = palloc0(size * sizeof(List *)); +} + +static void +DestroyValuesList(List **valuesList[]) +{ + pfree(*valuesList); + *valuesList = NIL; +} + +static void +ProcessRobinValue(Oid relid, List **valuesList, + int size, const RangeTblEntry *values_rte) +{ + List *values = values_rte->values_lists; + int length = values->length; + int dist; + int i, j; + int processNum = 0; + int node; + + /* get average insert value number of each node */ + if (length > NumDataNodes) + dist = length/NumDataNodes; + else + dist = 1; + + for(i = 0; i < size && processNum < length; i++) + { + node = GetRoundRobinNode(relid); + + /* assign insert value */ + for(j = 0; j < dist; j++) + { + processNum += 1; + valuesList[node - 1] = lappend(valuesList[node - 1], + list_nth(values, processNum - 1)); + } + } + + /* assign remained value */ + while(processNum < length) + { + processNum += 1; + node = GetRoundRobinNode(relid); + valuesList[node - 1] = lappend(valuesList[node - 1], + list_nth(values, processNum - 1)); + } +} + +static List * +RewriteInsertStmt(Query *query, RangeTblEntry *values_rte) +{ + ListCell *values_lc; + List *rwInsertList = NIL; + Query *element = NULL; + StringInfoData buf; + RangeTblRef *rtr = (RangeTblRef *) linitial(query->jointree->fromlist); + RangeTblEntry *rte; + RelationLocInfo *rte_loc_info; + char locatorType; + char *partColName; + List **valuesList; + int i; + + rte = (RangeTblEntry *) list_nth(query->rtable, query->resultRelation - 1); + rte_loc_info = GetRelationLocInfo(rte->relid); + locatorType = rte_loc_info->locatorType; + partColName = rte_loc_info->partAttrName; + + /* + * Do this first so that string is alloc'd in outer context not SPI's. + */ + initStringInfo(&buf); + + switch(locatorType) + { + case LOCATOR_TYPE_HASH: + { + bool first = true; + int partColno; + ExecNodes *exec_nodes; + + InitValuesList(&valuesList, NumDataNodes); + + foreach(values_lc, values_rte->values_lists) + { + List *sublist = (List *)lfirst(values_lc); + + if (first) + { + partColno = GetRelPartColPos(query, partColName); + first = false; + } + + /* get the exec node according to partition column value */ + GetHashExecNodes(rte_loc_info, &exec_nodes, + (Expr *)list_nth(sublist, partColno)); + + Assert(exec_nodes->nodelist->length == 1); + + /* assign valueList to specified exec node */ + ProcessHashValue(valuesList, sublist, list_nth_int(exec_nodes->nodelist, 0)); + } + } + + goto collect; + + case LOCATOR_TYPE_RROBIN: + + InitValuesList(&valuesList, NumDataNodes); + /* assign valueList to specified exec node */ + ProcessRobinValue(rte->relid, valuesList, NumDataNodes, values_rte); + +collect: + /* produce query for relative datanodes */ + for(i = 0; i < NumDataNodes; i++) + { + if (valuesList[i] != NIL) + { + ExecNodes *execNodes = makeNode(ExecNodes); + execNodes->baselocatortype = rte_loc_info->locatorType; + execNodes->nodelist = lappend_int(execNodes->nodelist, i + 1); + + element = copyObject(query); + + rte = (RangeTblEntry *)list_nth(element->rtable, rtr->rtindex - 1); + rte->values_lists = valuesList[i]; + + get_query_def_from_valuesList(element, &buf); + element->sql_statement = pstrdup(buf.data); + element->execNodes = execNodes; + elog(DEBUG1, "deparsed sql statement is %s\n", element->sql_statement); + + resetStringInfo(&buf); + + rwInsertList = lappend(rwInsertList, element); + } + } + + DestroyValuesList(&valuesList); + break; + + default: /* distribute by replication: just do it as usual */ + rwInsertList = lappend(rwInsertList, query); + break; + } + + + return rwInsertList; +} +#endif + diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index e61d444..87ccde5 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -648,16 +648,18 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string, querytree_list = pg_rewrite_query(query); #ifdef PGXC - if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) - { - ListCell *lc; - - foreach(lc, querytree_list) - { - Query *query = (Query *) lfirst(lc); - query->sql_statement = pstrdup(query_string); - } - } + if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) + { + ListCell *lc; + + foreach(lc, querytree_list) + { + Query *query = (Query *) lfirst(lc); + + if (query->sql_statement == NULL) + query->sql_statement = pstrdup(query_string); + } + } #endif TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string); diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index eb704ce..247ca83 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -23,6 +23,7 @@ #include "pg_trace.h" #ifdef PGXC #include "pgxc/pgxc.h" +#include "pgxc/execRemote.h" #endif #include "tcop/pquery.h" #include "tcop/tcopprot.h" @@ -1239,6 +1240,12 @@ PortalRunMulti(Portal portal, bool isTopLevel, char *completionTag) { ListCell *stmtlist_item; +#ifdef PGXC + combineTag combine; + + combine.cmdType = CMD_UNKNOWN; + combine.data[0] = '\0'; +#endif /* * If the destination is DestRemoteExecute, change to DestNone. The @@ -1288,6 +1295,13 @@ PortalRunMulti(Portal portal, bool isTopLevel, portal->sourceText, portal->portalParams, dest, completionTag); +#ifdef PGXC + /* it's special for INSERT */ + if (IS_PGXC_COORDINATOR && + pstmt->commandType == CMD_INSERT) + HandleCmdComplete(pstmt->commandType, &combine, + completionTag, strlen(completionTag)); +#endif } else { @@ -1340,6 +1354,12 @@ PortalRunMulti(Portal portal, bool isTopLevel, * counts, so fake something up if necessary. (This could happen if the * original query was replaced by a DO INSTEAD rule.) */ + +#ifdef PGXC + if (IS_PGXC_COORDINATOR && combine.data[0] != '\0') + strcpy(completionTag, combine.data); +#endif + if (completionTag && completionTag[0] == '\0') { if (portal->commandTag) @@ -1654,3 +1674,4 @@ DoPortalRewind(Portal portal) portal->portalPos = 0; portal->posOverflow = false; } + diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 5c521c1..507c643 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -2302,8 +2302,160 @@ deparse_query(Query *query, StringInfo buf, List *parentnamespace) { get_query_def(query, buf, parentnamespace, NULL, 0, 0); } -#endif +/* code borrowed from get_insert_query_def */ +void +get_query_def_from_valuesList(Query *query, StringInfo buf) +{ + + RangeTblEntry *select_rte = NULL; + RangeTblEntry *values_rte = NULL; + RangeTblEntry *rte; + char *sep; + ListCell *values_cell; + ListCell *l; + List *strippedexprs; + deparse_context context; + deparse_namespace dpns; + + /* + * Before we begin to examine the query, acquire locks on referenced + * relations, and fix up deleted columns in JOIN RTEs. This ensures + * consistent results. Note we assume it's OK to scribble on the passed + * querytree! + */ + AcquireRewriteLocks(query); + + context.buf = buf; + context.namespaces = NIL; + context.windowClause = NIL; + context.windowTList = NIL; + context.varprefix = (list_length(query->rtable) != 1); + context.prettyFlags = 0; + context.indentLevel = 0; + + dpns.rtable = query->rtable; + dpns.ctes = query->cteList; + dpns.subplans = NIL; + dpns.outer_plan = dpns.inner_plan = NULL; + dpns.remotequery = false; + + /* + * 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) + { + 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 + */ + rte = rt_fetch(query->resultRelation, query->rtable); + Assert(rte->rtekind == RTE_RELATION); + + appendStringInfo(buf, "INSERT INTO %s (", + generate_relation_name(rte->relid, NIL)); + + /* + * 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) + { + TargetEntry *tle = (TargetEntry *) lfirst(l); + + elog(DEBUG1, "targetEntry type is %d\n)", tle->expr->type); + if (tle->resjunk || !IsA(tle->expr, Var)) + continue; /* ignore junk entries */ + + appendStringInfoString(buf, sep); + sep = ", "; + + /* + * Put out name of target column; look in the catalogs, not at + * tle->resname, since resname will fail to track RENAME. + */ + appendStringInfoString(buf,quote_identifier(get_relid_attribute_name(rte->relid, tle->resno))); + + /* + * Print any indirection needed (subfields or subscripts), and strip + * off the top-level nodes representing the indirection assignments. + */ + 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, ") "); + + if (select_rte) + { + /* Add the SELECT */ + get_query_def(select_rte->subquery, buf, NIL, NULL, + context.prettyFlags, context.indentLevel); + } + else if (values_rte) + { + /* A WITH clause is possible here */ + get_with_clause(query, &context); + /* Add the multi-VALUES expression lists */ + get_values_def(values_rte->values_lists, &context); + } + else + { + /* A WITH clause is possible here */ + get_with_clause(query, &context); + /* Add the single-VALUES expression list */ + appendContextKeyword(&context, "VALUES (", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); + get_rule_expr((Node *) strippedexprs, &context, false); + appendStringInfoChar(buf, ')'); + } + + /* Add RETURNING if present */ + if (query->returningList) + { + appendContextKeyword(&context, " RETURNING", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); + get_target_list(query->returningList, &context, NULL); + } +} +#endif /* ---------- * get_query_def - Parse back one query parsetree * diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 7f4b20c..e2357b0 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -26,6 +26,7 @@ #include "nodes/value.h" #ifdef PGXC #include "access/tupdesc.h" +#include "pgxc/locator.h" #endif /* Possible sources of a Query */ @@ -152,6 +153,7 @@ typedef struct Query #ifdef PGXC /* need this info for PGXC Planner, may be temporary */ char *sql_statement; /* original query */ + ExecNodes *execNodes; /* execute nodes */ #endif } Query; diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index 182310e..e25ca45 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -36,5 +36,9 @@ extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo **subroot); extern Expr *expression_planner(Expr *expr); +#ifdef PGXC +extern void GetHashExecNodes(RelationLocInfo *rel_loc_info, + ExecNodes **exec_nodes, const Expr *expr); +#endif #endif /* PLANNER_H */ diff --git a/src/include/pgxc/execRemote.h b/src/include/pgxc/execRemote.h index 8d7a348..8f6c10a 100644 --- a/src/include/pgxc/execRemote.h +++ b/src/include/pgxc/execRemote.h @@ -25,6 +25,9 @@ #include "nodes/pg_list.h" #include "tcop/dest.h" #include "utils/snapshot.h" +#ifdef PGXC +#include "tcop/pquery.h" +#endif /* Outputs of handle_response() */ #define RESPONSE_EOF EOF @@ -138,6 +141,10 @@ extern void ExecEndRemoteQuery(RemoteQueryState *step); extern void ExecRemoteUtility(RemoteQuery *node); extern int handle_response(PGXCNodeHandle * conn, RemoteQueryState *combiner); +#ifdef PGXC +extern void HandleCmdComplete(CmdType commandType, combineTag *combine, const char *msg_body, + size_t len); +#endif extern bool FetchTuple(RemoteQueryState *combiner, TupleTableSlot *slot); extern void BufferConnection(PGXCNodeHandle *conn); diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h index 4919dc5..e947989 100644 --- a/src/include/tcop/pquery.h +++ b/src/include/tcop/pquery.h @@ -17,6 +17,13 @@ #include "nodes/parsenodes.h" #include "utils/portal.h" +#ifdef PGXC +typedef struct combineTag +{ + CmdType cmdType; + char data[COMPLETION_TAG_BUFSIZE]; +} combineTag; +#endif extern PGDLLIMPORT Portal ActivePortal; diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index b8f8a9d..30cd971 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -17,7 +17,9 @@ #include "fmgr.h" #include "lib/stringinfo.h" #include "nodes/parsenodes.h" - +#ifdef PGXC +#include "lib/stringinfo.h" +#endif /* * Defined in adt/ */ @@ -599,6 +601,7 @@ extern char *deparse_expression(Node *expr, List *dpcontext, #ifdef PGXC extern List *deparse_context_for_remotequery(const char *aliasname, Oid relid); extern List *deparse_context_for(const char *aliasname, Oid relid); +extern void get_query_def_from_valuesList(Query *query, StringInfo buf); extern void deparse_query(Query *query, StringInfo buf, List *parentnamespace); #endif extern List *deparse_context_for_plan(Node *plan, Node *outer_plan, ----------------------------------------------------------------------- Summary of changes: src/backend/pgxc/plan/planner.c | 40 ++++- src/backend/pgxc/pool/execRemote.c | 67 ++++++ src/backend/rewrite/rewriteHandler.c | 410 +++++++++++++++++++++++++++++++++- src/backend/tcop/postgres.c | 22 +- src/backend/tcop/pquery.c | 21 ++ src/backend/utils/adt/ruleutils.c | 154 +++++++++++++- src/include/nodes/parsenodes.h | 2 + src/include/optimizer/planner.h | 4 + src/include/pgxc/execRemote.h | 7 + src/include/tcop/pquery.h | 7 + src/include/utils/builtins.h | 5 +- 11 files changed, 721 insertions(+), 18 deletions(-) hooks/post-receive -- Postgres-XC |