1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
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
23  * 02110-1301  USA
24  */
25 
26 
27 #include "query_string_builder.h"
28 #include "mysql/plugin.h"
29 #include "my_sys.h" // escape_string_for_mysql
30 
31 using namespace xpl;
32 
33 charset_info_st* Query_string_builder::m_charset = NULL;
34 my_thread_once_t Query_string_builder::m_charset_initialized = MY_THREAD_ONCE_INIT;
35 
init_charset()36 void Query_string_builder::init_charset()
37 {
38   m_charset = get_charset_by_csname("utf8mb4", MY_CS_PRIMARY, MYF(MY_WME));
39 }
40 
41 
Query_string_builder(size_t reserve)42 Query_string_builder::Query_string_builder(size_t reserve)
43   : m_in_quoted(false), m_in_identifier(false)
44 {
45   my_thread_once(&m_charset_initialized, init_charset);
46   assert(m_charset != NULL);
47 
48   m_str.reserve(reserve);
49 }
50 
51 
~Query_string_builder()52 Query_string_builder::~Query_string_builder()
53 {
54 }
55 
56 
quote_identifier(const char * s,size_t length)57 Query_string_builder &Query_string_builder::quote_identifier(const char *s, size_t length)
58 {
59   m_str.append("`");
60   escape_identifier(s, length);
61   m_str.append("`");
62   return *this;
63 }
64 
65 
quote_identifier_if_needed(const char * s,size_t length)66 Query_string_builder &Query_string_builder::quote_identifier_if_needed(const char *s, size_t length)
67 {
68   bool need_quote = false;
69   if (length > 0 && isalpha(s[0]))
70   {
71     for (size_t i = 1; i < length; i++)
72       if (!isalnum(s[i]) && s[i] != '_')
73       {
74         need_quote = true;
75         break;
76       }
77   }
78   else
79     need_quote = true;
80 
81   if (need_quote)
82     return quote_identifier(s, length);
83   else
84     return put(s, length);
85 }
86 
87 
escape_identifier(const char * s,size_t length)88 Query_string_builder &Query_string_builder::escape_identifier(const char *s, size_t length)
89 {
90   size_t str_pos = m_str.size();
91   // resize the buffer to fit the original size + worst case length of s
92   m_str.resize(str_pos + length*2);
93 
94   char* cursor_out = &m_str[str_pos];
95   const char* cursor_in = s;
96 
97   for (size_t idx = 0; idx < length; ++idx)
98   {
99     if (*cursor_in == '`')
100       *cursor_out++ = '`';
101     *cursor_out++ = *cursor_in++;
102   }
103   m_str.resize(str_pos + (cursor_out - &m_str[str_pos]));
104   return *this;
105 }
106 
107 
escape_string(const char * s,size_t length)108 Query_string_builder &Query_string_builder::escape_string(const char *s, size_t length)
109 {
110   size_t str_pos = m_str.size();
111   // resize the buffer to fit the original size + worst case length of s
112   m_str.resize(str_pos + 2*length+1);
113 
114   size_t r = escape_string_for_mysql(m_charset, &m_str[str_pos], 2*length+1, s, length);
115   m_str.resize(str_pos + r);
116 
117   return *this;
118 }
119 
120 
quote_string(const char * s,size_t length)121 Query_string_builder &Query_string_builder::quote_string(const char *s, size_t length)
122 {
123   m_str.append("'");
124   escape_string(s, length);
125   m_str.append("'");
126 
127   return *this;
128 }
129 
130 
put(const char * s,size_t length)131 Query_string_builder &Query_string_builder::put(const char *s, size_t length)
132 {
133   if (m_in_quoted)
134     escape_string(s, length);
135   else if (m_in_identifier)
136     escape_identifier(s, length);
137   else
138     m_str.append(s, length);
139 
140   return *this;
141 }
142 
143 
format()144 Query_formatter Query_string_builder::format()
145 {
146   return Query_formatter(m_str, *m_charset);
147 }
148