* parse_analyze() or the rewriter, but instead we need to pass them
* through parse_utilcmd.c to make them ready for execution.
*/
- raw_parsetree_list = raw_parser(cmd);
+ raw_parsetree_list = raw_parser(cmd, NULL);
querytree_list = NIL;
foreach(list_item, raw_parsetree_list)
{
List *table_names;
} ImportQual;
+typedef struct StmtMulti
+{
+ List *parsetrees;
+ List *queries;
+ int offset;
+ char *lastQuery;
+} StmtMulti;
+
/* ConstraintAttributeSpec yields an integer bitmask of these flags: */
#define CAS_NOT_DEFERRABLE 0x01
#define CAS_DEFERRABLE 0x02
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
/* PGXC_BEGIN */
+ struct StmtMulti *stmtmulti;
DistributeBy *distby;
PGXCSubCluster *subclus;
/* PGXC_END */
%type <ival> import_qualification_type
%type <importqual> import_qualification
-%type <list> stmtblock stmtmulti
+%type <stmtmulti> stmtmulti
+%type <list> stmtblock
OptTableElementList TableElementList OptInherit definition
OptTypedTableElementList TypedTableElementList
reloptions opt_reloptions
*/
stmtblock: stmtmulti
{
- pg_yyget_extra(yyscanner)->parsetree = $1;
+ pg_yyget_extra(yyscanner)->parsetree = $1 ? $1->parsetrees : NIL;
+ pg_yyget_extra(yyscanner)->queries = $1 ? $1->queries : NIL;
}
;
stmtmulti: stmtmulti ';' stmt
{
if ($3 != NULL)
- $$ = lappend($1, $3);
+ {
+ char *query = scanner_get_query(@3, -1, yyscanner);
+ /*
+ * Because of the way multi-commands are parsed by the
+ * parser, when the earlier command was parsed and
+ * reduced to a 'stmtmulti', we did not have the
+ * end-of-the-query marker. But now that we have seen
+ * the ';' token, add '\0' at the corresponding offset
+ * to get a separated command.
+ */
+ if ($1->lastQuery)
+ $1->lastQuery[@2 - $1->offset] = '\0';
+ $1->offset = @2;
+ $1->parsetrees = lappend($1->parsetrees, $3);
+ $1->queries = lappend($1->queries, makeString(query));
+ $1->lastQuery = query;
+ $$ = $1;
+ }
else
$$ = $1;
}
| stmt
{
if ($1 != NULL)
- $$ = list_make1($1);
+ {
+ StmtMulti *n = (StmtMulti *) palloc0(sizeof (StmtMulti));
+ char *query = scanner_get_query(@1, -1, yyscanner);
+ n->lastQuery = query;
+
+ /*
+ * Keep track of the offset where $1 started. We don't
+ * have the offset where it ends so we copy the entire
+ * query to the end. If later, we find a ';' followed
+ * by another command, we'll add the '\0' at the
+ * appropriate offset
+ *
+ * XXX May be there is a better way to get the matching
+ * portion of the query string, but this does the trick
+ * for regression as well as the problem we are trying
+ * to solve with multi-command queries
+ */
+ n->offset = @1;
+
+ /*
+ * Collect both parsetree as well as the original query
+ * that resulted in the parsetree
+ */
+ n->parsetrees = list_make1($1);
+ n->queries = list_make1(makeString(query));
+ $$ = n;
+ }
else
- $$ = NIL;
+ $$ = NULL;
}
;
ptserrcontext.previous = error_context_stack;
error_context_stack = &ptserrcontext;
- raw_parsetree_list = raw_parser(buf.data);
+ raw_parsetree_list = raw_parser(buf.data, NULL);
error_context_stack = ptserrcontext.previous;
* Returns a list of raw (un-analyzed) parse trees.
*/
List *
-raw_parser(const char *str)
+raw_parser(const char *str, List **queries)
{
core_yyscan_t yyscanner;
base_yy_extra_type yyextra;
if (yyresult) /* error */
return NIL;
+ if (queries)
+ *queries = yyextra.queries;
+
return yyextra.parsetree;
}
yyext->keywords = keywords;
yyext->num_keywords = num_keywords;
+#ifdef XCP
+ yyext->query = pstrdup(str);
+#endif
+
yyext->backslash_quote = backslash_quote;
yyext->escape_string_warning = escape_string_warning;
yyext->standard_conforming_strings = standard_conforming_strings;
if (ptr)
pfree(ptr);
}
+
+/*
+ * Return a copy of a substring of the original query string, starting at
+ * 'start' offset and 'len' bytes long, Be mindful of the invalid arguments
+ * being passed by the caller
+ */
+char *
+scanner_get_query(int start, int len, core_yyscan_t yyscanner)
+{
+ char *query;
+
+ /*
+ * If the caller passes a wrong offset, just assume 0
+ */
+ if (start == -1)
+ start = 0;
+ if (start > strlen(yyextra->query))
+ return NULL;
+ /*
+ * Similarly, if the passed-in length is more than remaining
+ * bytes in the string, just return whatever is available
+ */
+ if (len == -1)
+ len = strlen(yyextra->query) - start;
+ else if (len + start > strlen(yyextra->query))
+ return NULL;
+
+ query = palloc0(len + 1);
+ memcpy(query, yyextra->query + start, len);
+ query[len] = '\0';
+ return query;
+}
* commands are not processed any further than the raw parse stage.
*/
List *
-pg_parse_query(const char *query_string)
+pg_parse_query_internal(const char *query_string, List **querysource_list)
{
List *raw_parsetree_list;
if (log_parser_stats)
ResetUsage();
- raw_parsetree_list = raw_parser(query_string);
+ raw_parsetree_list = raw_parser(query_string, querysource_list);
if (log_parser_stats)
ShowUsage("PARSER STATISTICS");
return raw_parsetree_list;
}
+List *
+pg_parse_query(const char *query_string)
+{
+ return pg_parse_query_internal(query_string, NULL);
+}
+
+List *
+pg_parse_query_get_source(const char *query_string, List **querysource_list)
+{
+ return pg_parse_query_internal(query_string, querysource_list);
+}
+
/*
* Given a raw parsetree (gram.y output), and optionally information about
* types of parameter symbols ($n), perform parse analysis and rule rewriting.
MemoryContext oldcontext;
List *parsetree_list;
ListCell *parsetree_item;
+ List *querysource_list;
+ ListCell *querysource_item;
bool save_log_statement_stats = log_statement_stats;
bool was_logged = false;
bool isTopLevel;
* Do basic parsing of the query or queries (this should be safe even if
* we are in aborted transaction state!)
*/
- parsetree_list = pg_parse_query(query_string);
+ parsetree_list = pg_parse_query_get_source(query_string, &querysource_list);
#ifdef XCP
if (IS_PGXC_LOCAL_COORDINATOR && list_length(parsetree_list) > 1)
/*
* Run through the raw parsetree(s) and process each one.
*/
- foreach(parsetree_item, parsetree_list)
+ forboth(parsetree_item, parsetree_list, querysource_item, querysource_list)
{
Node *parsetree = (Node *) lfirst(parsetree_item);
+ char *querysource = ((Value *) lfirst(querysource_item))->val.str;
bool snapshot_set = false;
const char *commandTag;
char completionTag[COMPLETION_TAG_BUFSIZE];
* We don't have to copy anything into the portal, because everything
* we are passing here is in MessageContext, which will outlive the
* portal anyway.
+ *
+ * The query_string may contain multiple commands separated by ';' and
+ * we have a separate parsetree corresponding to each such command.
+ * Since we later may send down the query to the remote nodes
+ * (especially for utility queries), using the query_string is a
+ * problem because the same query will be sent out multiple times, one
+ * for each command processed. So we taught the parser to return the
+ * portion of the query_string along with the parsetree and use that
+ * while defining a portal below.
+ *
+ * XXX Since the portal expects to see a valid query_string, if the
+ * substring is available, use the original query_string. Not elegant,
+ * but far better than what we were doing earlier
*/
PortalDefineQuery(portal,
NULL,
- query_string,
+ querysource ? querysource : query_string,
commandTag,
plantree_list,
NULL);
* State variables that belong to the grammar.
*/
List *parsetree; /* final parse result is delivered here */
+ List *queries;
} base_yy_extra_type;
/*
/* Primary entry point for the raw parsing functions */
-extern List *raw_parser(const char *str);
+extern List *raw_parser(const char *str, List **queries);
/* Utility functions exported by gram.y (perhaps these should be elsewhere) */
extern List *SystemFuncName(char *name);
char *scanbuf;
Size scanbuflen;
+#ifdef XCP
+ /*
+ * Pointer to the original query string
+ */
+ char *query;
+#endif
+
/*
* The keyword list to use.
*/
core_yyscan_t yyscanner);
extern int scanner_errposition(int location, core_yyscan_t yyscanner);
extern void scanner_yyerror(const char *message, core_yyscan_t yyscanner) pg_attribute_noreturn();
+extern char *scanner_get_query(int start, int len, core_yyscan_t yyscanner);
#endif /* SCANNER_H */
extern int log_statement;
extern List *pg_parse_query(const char *query_string);
+extern List *pg_parse_query_get_source(const char *query_string, List **queries);
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
Oid *paramTypes, int numParams);
extern List *pg_analyze_and_rewrite_params(Node *parsetree,
error_context_stack = &syntax_errcontext;
oldCxt = MemoryContextSwitchTo(compile_tmp_cxt);
- (void) raw_parser(stmt);
+ (void) raw_parser(stmt, NULL);
MemoryContextSwitchTo(oldCxt);
/* Restore former ereport callback */