+ return hints;
+}
+
+/*
+ * Get client-supplied query string. Addtion to that the jumbled query is
+ * supplied if the caller requested. From the restriction of JumbleQuery, some
+ * kind of query needs special amendments. Reutrns NULL if the current hint
+ * string is still valid.
+ */
+static const char *
+get_query_string(ParseState *pstate, Query *query, Query **jumblequery)
+{
+ const char *p = debug_query_string;
+
+ if (jumblequery != NULL)
+ *jumblequery = query;
+
+ if (query->commandType == CMD_UTILITY)
+ {
+ Query *target_query = query;
+
+ /* Use the target query if EXPLAIN */
+ if (IsA(query->utilityStmt, ExplainStmt))
+ {
+ ExplainStmt *stmt = (ExplainStmt *)(query->utilityStmt);
+
+ Assert(IsA(stmt->query, Query));
+ target_query = (Query *)stmt->query;
+
+ /* strip out the top-level query for further processing */
+ if (target_query->commandType == CMD_UTILITY &&
+ target_query->utilityStmt != NULL)
+ target_query = (Query *)target_query->utilityStmt;
+ }
+
+ if (IsA(target_query, CreateTableAsStmt))
+ {
+ /*
+ * Use the the body query for CREATE AS. The Query for jumble also
+ * replaced with the corresponding one.
+ */
+ CreateTableAsStmt *stmt = (CreateTableAsStmt *) target_query;
+ PreparedStatement *entry;
+ Query *tmp_query;
+
+ Assert(IsA(stmt->query, Query));
+ tmp_query = (Query *) stmt->query;
+
+ if (tmp_query->commandType == CMD_UTILITY &&
+ IsA(tmp_query->utilityStmt, ExecuteStmt))
+ {
+ ExecuteStmt *estmt = (ExecuteStmt *) tmp_query->utilityStmt;
+ entry = FetchPreparedStatement(estmt->name, true);
+ p = entry->plansource->query_string;
+ target_query = (Query *) linitial (entry->plansource->query_list);
+ }
+ }
+ else
+ if (IsA(target_query, ExecuteStmt))
+ {
+ /*
+ * Use the prepared query for EXECUTE. The Query for jumble also
+ * replaced with the corresponding one.
+ */
+ ExecuteStmt *stmt = (ExecuteStmt *)target_query;
+ PreparedStatement *entry;
+
+ entry = FetchPreparedStatement(stmt->name, true);
+ p = entry->plansource->query_string;
+ target_query = (Query *) linitial (entry->plansource->query_list);
+ }
+
+ /* We don't accept other than a Query other than a CMD_UTILITY */
+ if (!IsA(target_query, Query) ||
+ target_query->commandType == CMD_UTILITY)
+ target_query = NULL;
+
+ if (jumblequery)
+ *jumblequery = target_query;
+ }
+ /* Return NULL if the pstate is not identical to the top-level query */
+ else if (strcmp(pstate->p_sourcetext, p) != 0)
+ p = NULL;
+
+ return p;
+}
+
+/*
+ * Get hints from the head block comment in client-supplied query string.
+ */
+static const char *
+get_hints_from_comment(const char *p)
+{
+ const char *hint_head;
+ char *head;
+ char *tail;
+ int len;
+
+ if (p == NULL)
+ return NULL;
+
+ /* extract query head comment. */
+ hint_head = strstr(p, HINT_START);
+ if (hint_head == NULL)
+ return NULL;
+ for (;p < hint_head; p++)
+ {
+ /*
+ * Allow these characters precedes hint comment:
+ * - digits
+ * - alphabets which are in ASCII range
+ * - space, tabs and new-lines
+ * - underscores, for identifier
+ * - commas, for SELECT clause, EXPLAIN and PREPARE
+ * - parentheses, for EXPLAIN and PREPARE
+ *
+ * Note that we don't use isalpha() nor isalnum() in ctype.h here to
+ * avoid behavior which depends on locale setting.
+ */
+ if (!(*p >= '0' && *p <= '9') &&
+ !(*p >= 'A' && *p <= 'Z') &&
+ !(*p >= 'a' && *p <= 'z') &&
+ !isspace(*p) &&
+ *p != '_' &&
+ *p != ',' &&
+ *p != '(' && *p != ')')
+ return NULL;
+ }
+
+ len = strlen(HINT_START);
+ head = (char *) p;
+ p += len;
+ skip_space(p);
+
+ /* find hint end keyword. */
+ if ((tail = strstr(p, HINT_END)) == NULL)
+ {
+ hint_ereport(head, ("Unterminated block comment."));
+ return NULL;
+ }
+
+ /* We don't support nested block comments. */
+ if ((head = strstr(p, BLOCK_COMMENT_START)) != NULL && head < tail)
+ {
+ hint_ereport(head, ("Nested block comments are not supported."));
+ return NULL;
+ }
+
+ /* Make a copy of hint. */