1 /*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "plugin/x/tests/driver/processor/commands/macro.h"
26
27 #include <algorithm>
28 #include <list>
29 #include <memory>
30 #include <sstream>
31 #include <string>
32 #include <vector>
33
34 #include "plugin/x/tests/driver/common/utils_string_parsing.h"
35 #include "plugin/x/tests/driver/processor/execution_context.h"
36 #include "plugin/x/tests/driver/processor/stream_processor.h"
37
get_expanded_macro_body(const Strings & args,const Script_stack * stack,const Console & console) const38 std::string Macro::get_expanded_macro_body(const Strings &args,
39 const Script_stack *stack,
40 const Console &console) const {
41 if (!m_accepts_variadic_arguments) {
42 if (args.size() != m_accepts_args.size()) {
43 if (m_accepts_args.empty() && 1 == args.size() && args.front().empty())
44 return get_expanded_macro_body({}, stack, console);
45
46 console.print_error(*stack, "Invalid number of arguments for macro ",
47 m_name, ", expected:", m_accepts_args.size(),
48 " actual:", args.size(), '\n');
49
50 for (const auto &v : args) {
51 console.print_error(" argument: \"", v, "\"\n");
52 }
53
54 return "";
55 }
56 } else {
57 if (args.size() < m_accepts_args.size()) {
58 console.print_error(*stack, "Invalid number of arguments for macro ",
59 m_name, ", expected at last:", m_accepts_args.size(),
60 " actual:", args.size(), '\n');
61
62 for (const auto &v : args) {
63 console.print_error(" argument: \"", v, "\"\n");
64 }
65
66 return "";
67 }
68 }
69
70 std::string text = m_body;
71 auto n = m_accepts_args.begin();
72 auto v = args.begin();
73 size_t index_of_argument = 0;
74
75 for (; index_of_argument < m_accepts_args.size(); index_of_argument++) {
76 aux::replace_all(text, *(n++), *(v++));
77 }
78
79 if (m_accepts_variadic_arguments) {
80 std::string variadic_arguments;
81
82 for (; index_of_argument < args.size() - 1; index_of_argument++) {
83 variadic_arguments += *(v++) + '\t';
84 }
85
86 if (index_of_argument < args.size()) variadic_arguments += *(v++);
87
88 aux::replace_all(text, "%VAR_ARGS%", variadic_arguments);
89 }
90
91 return text;
92 }
93
add_macro(std::shared_ptr<Macro> macro)94 void Macro_container::add_macro(std::shared_ptr<Macro> macro) {
95 m_macros.push_back(macro);
96 }
97
set_compress_option(const bool compress)98 void Macro_container::set_compress_option(const bool compress) {
99 m_compress = compress;
100 }
101
get_expanded_macro(Execution_context * context,const std::string & cmd,std::string * r_name,const Script_stack * stack,const Console & console)102 std::string Macro_container::get_expanded_macro(Execution_context *context,
103 const std::string &cmd,
104 std::string *r_name,
105 const Script_stack *stack,
106 const Console &console) {
107 Strings args;
108 std::string::size_type p = std::min(cmd.find(' '), cmd.find('\t'));
109
110 if (p == std::string::npos) {
111 *r_name = cmd;
112 } else {
113 *r_name = cmd.substr(0, p);
114 std::string rest = cmd.substr(p + 1);
115 aux::split(args, rest, "\t", m_compress);
116 }
117
118 if (r_name->empty()) {
119 console.print_error(*stack, "Missing macro name for macro call\n");
120 return "";
121 }
122
123 context->m_variables->replace(r_name);
124
125 for (auto iter = m_macros.begin(); iter != m_macros.end(); ++iter) {
126 if ((*iter)->name() == *r_name) {
127 return (*iter)->get_expanded_macro_body(args, stack, console);
128 }
129 }
130
131 console.print_error(*stack, "Undefined macro ", *r_name, '\n');
132
133 return "";
134 }
135
call(Execution_context * context,const std::string & cmd)136 bool Macro_container::call(Execution_context *context, const std::string &cmd) {
137 std::string name;
138 std::string macro = get_expanded_macro(
139 context, cmd, &name, &context->m_script_stack, context->m_console);
140
141 context->m_script_stack.push({0, "macro " + name});
142
143 std::stringstream stream(macro);
144 std::vector<Block_processor_ptr> processors{
145 create_macro_block_processors(context)};
146
147 const bool r =
148 0 == process_client_input(stream, &processors, &context->m_script_stack,
149 context->m_console);
150
151 context->m_script_stack.pop();
152
153 return r;
154 }
155