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