mirror of https://github.com/sysown/proxysql
commit
832aa48a4d
@ -0,0 +1,16 @@
|
||||
#ifndef __CLASS_SET_PARSER_H
|
||||
#define __CLASS_SET_PARSER_H
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class SetParser {
|
||||
private:
|
||||
std::string query;
|
||||
public:
|
||||
SetParser(std::string q);
|
||||
std::map<std::string, std::vector<std::string>> parse();
|
||||
};
|
||||
|
||||
|
||||
#endif /* __CLASS_SET_PARSER_H */
|
||||
@ -0,0 +1,67 @@
|
||||
#include "set_parser.h"
|
||||
#include "re2/re2.h"
|
||||
#include "re2/regexp.h"
|
||||
#include "gen_utils.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
SetParser::SetParser(std::string nq) {
|
||||
int query_no_space_length = nq.length();
|
||||
char *query_no_space=(char *)malloc(query_no_space_length+1);
|
||||
memcpy(query_no_space,nq.c_str(),query_no_space_length);
|
||||
query_no_space[query_no_space_length]='\0';
|
||||
query_no_space_length=remove_spaces(query_no_space);
|
||||
query = string(query_no_space);
|
||||
free(query_no_space);
|
||||
}
|
||||
|
||||
std::map<std::string,std::vector<string>> SetParser::parse() {
|
||||
|
||||
re2::RE2::Options *opt2=new re2::RE2::Options(RE2::Quiet);
|
||||
opt2->set_case_sensitive(false);
|
||||
opt2->set_longest_match(false);
|
||||
|
||||
re2::RE2 re0("^\\s*SET\\s+", *opt2);
|
||||
re2::RE2::Replace(&query, re0, "");
|
||||
|
||||
std::map<std::string,std::vector<string>> result;
|
||||
|
||||
#define NAMES "(NAMES)"
|
||||
#define QUOTES "(?:'||\")"
|
||||
#define NAME_VALUE "((?:\\w|\\d)+)"
|
||||
#define SESSION "(?:|SESSION +|@@|@@session.)"
|
||||
#define VAR "(\\w+)"
|
||||
#define SPACES " *"
|
||||
#define VAR_VALUE "((?:[\\w/\\d:\\+\\-]|,)+)"
|
||||
|
||||
const string pattern="(?:" NAMES SPACES QUOTES NAME_VALUE QUOTES "(?: +COLLATE +" QUOTES NAME_VALUE QUOTES "|)" "|" SESSION VAR SPACES "(?:|:)=" SPACES QUOTES VAR_VALUE QUOTES ") *,? *";
|
||||
re2::RE2 re(pattern, *opt2);
|
||||
string var;
|
||||
string value1, value2, value3, value4, value5;
|
||||
re2::StringPiece input(query);
|
||||
while (re2::RE2::Consume(&input, re, &value1, &value2, &value3, &value4, &value5)) {
|
||||
std::vector<std::string> op;
|
||||
|
||||
string key;
|
||||
if (value1 != "") {
|
||||
// NAMES
|
||||
key = value1;
|
||||
op.push_back(value2);
|
||||
if (value3 != "") {
|
||||
op.push_back(value3);
|
||||
}
|
||||
} else if (value4 != "") {
|
||||
// VARIABLE
|
||||
key = value4;
|
||||
op.push_back(value5);
|
||||
}
|
||||
|
||||
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
|
||||
result[key] = op;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
|
||||
|
||||
|
||||
|
||||
DEPS_PATH=../../deps
|
||||
|
||||
RE2_PATH=$(DEPS_PATH)/re2/re2
|
||||
RE2_IDIR=$(RE2_PATH)
|
||||
|
||||
MARIADB_PATH=$(DEPS_PATH)/mariadb-client-library/mariadb_client
|
||||
MARIADB_IDIR=$(MARIADB_PATH)/include
|
||||
MARIADB_LDIR=$(MARIADB_PATH)/libmariadb
|
||||
|
||||
DAEMONPATH=$(DEPS_PATH)/libdaemon/libdaemon
|
||||
DAEMONPATH_IDIR=$(DAEMONPATH)
|
||||
DAEMONPATH_LDIR=$(DAEMONPATH)/libdaemon/.libs
|
||||
|
||||
JEMALLOC_PATH=$(DEPS_PATH)/jemalloc/jemalloc
|
||||
JEMALLOC_IDIR=$(JEMALLOC_PATH)/include/jemalloc
|
||||
JEMALLOC_LDIR=$(JEMALLOC_PATH)/lib
|
||||
|
||||
LIBCONFIG_PATH=$(DEPS_PATH)/libconfig/libconfig-1.4.9
|
||||
LIBCONFIG_IDIR=-I$(LIBCONFIG_PATH)/lib
|
||||
LIBCONFIG_LDIR=-L$(LIBCONFIG_PATH)/lib/.libs
|
||||
|
||||
SQLITE3_DIR=$(DEPS_PATH)/sqlite3/sqlite3
|
||||
|
||||
IDIR=../../include
|
||||
LDIR=../../lib
|
||||
IDIRS=-I$(IDIR) -I$(RE2_IDIR) -I$(JEMALLOC_IDIR) -I$(MARIADB_IDIR) $(LIBCONFIG_IDIR) -I$(DAEMONPATH_IDIR) -I$(SQLITE3_DIR)
|
||||
LDIRS=-L$(LDIR) -L$(JEMALLOC_LDIR) $(LIBCONFIG_LDIR) -L$(RE2_PATH)/obj -L$(MARIADB_LDIR) -L$(DAEMONPATH_LDIR)
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
IDIRS+= -I/usr/local/opt/openssl/include
|
||||
endif
|
||||
|
||||
LIBPROXYSQLAR=$(LDIR)/libproxysql.a
|
||||
|
||||
MYCPPFLAGS=-std=c++11 $(IDIRS) $(OPTZ) $(DEBUG) -ggdb
|
||||
LDFLAGS+=
|
||||
MYLIBS=-Wl,--export-dynamic -Wl,-Bstatic -lconfig -lproxysql -ldaemon -ljemalloc -lconfig++ -lre2 -levent -lmariadbclient -Wl,-Bdynamic -lpthread -lm -lz -lrt -lcrypto -lssl $(EXTRALINK)
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
MYLIBS=-lre2 -lpthread -lm -lz -liconv
|
||||
endif
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
MYLIBS+= -ldl
|
||||
endif
|
||||
ifeq ($(UNAME_S),FreeBSD)
|
||||
MYLIBS+= -lexecinfo
|
||||
endif
|
||||
|
||||
.PHONY: default
|
||||
default: setparsertest
|
||||
|
||||
setparsertest: setparsertest.cpp $(RE2_PATH)/util/test.cc $(LDIR)/set_parser.cpp $(LIBPROXYSQLAR)
|
||||
$(CXX) -o $@ $@.cpp $(RE2_PATH)/util/test.cc $(LIBPROXYSQLAR) $(MYCPPFLAGS) $(CPPFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS)
|
||||
|
||||
clean:
|
||||
rm -f *~ core $(default)
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
#include "re2/re2.h"
|
||||
#include "re2/regexp.h"
|
||||
#include "util/test.h"
|
||||
#include "set_parser.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
int remove_spaces(const char *s) {
|
||||
char *inp = (char *)s, *outp = (char *)s;
|
||||
bool prev_space = false;
|
||||
bool fns = false;
|
||||
while (*inp) {
|
||||
if (isspace(*inp)) {
|
||||
if (fns) {
|
||||
if (!prev_space) {
|
||||
*outp++ = ' ';
|
||||
prev_space = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*outp++ = *inp;
|
||||
prev_space = false;
|
||||
if (!fns) fns=true;
|
||||
}
|
||||
++inp;
|
||||
}
|
||||
if (outp>s) {
|
||||
if (prev_space) {
|
||||
outp--;
|
||||
}
|
||||
}
|
||||
*outp = '\0';
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
bool iequals(const string& a, const string& b)
|
||||
{
|
||||
unsigned int sz = a.size();
|
||||
if (b.size() != sz)
|
||||
return false;
|
||||
for (unsigned int i = 0; i < sz; ++i)
|
||||
if (tolower(a[i]) != tolower(b[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void printMap(const char* prefix, const std::map<std::string, std::vector<std::string>>& dict)
|
||||
{
|
||||
std::cout << prefix << ": ";
|
||||
for(auto mapIt = begin(dict); mapIt != end(dict); ++mapIt)
|
||||
{
|
||||
std::cout << mapIt->first << " : ";
|
||||
|
||||
for(auto c : mapIt->second)
|
||||
{
|
||||
std::cout << c << " ";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
struct Expected {
|
||||
const char* var;
|
||||
std::vector<std::string> values;
|
||||
Expected(const char* var, std::vector<std::string> values): var(var), values(values){};
|
||||
};
|
||||
|
||||
struct Test {
|
||||
const char* query;
|
||||
std::vector<Expected> results;
|
||||
};
|
||||
|
||||
static Test sql_mode[] = {
|
||||
{ "SET @@sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET SESSION sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET @@session.sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET sql_mode = 'TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET SQL_MODE ='TRADITIONAL'", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET SQL_MODE = \"TRADITIONAL\"", { Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
};
|
||||
|
||||
void TestParse(const Test* tests, int ntests, const string& title) {
|
||||
for (int i = 0; i < ntests; i++) {
|
||||
std::map<std::string, std::vector<std::string>> data;
|
||||
for(auto it = std::begin(tests[i].results); it != std::end(tests[i].results); ++it) {
|
||||
data[it->var] = it->values;
|
||||
}
|
||||
|
||||
SetParser parser(tests[i].query);
|
||||
std::map<std::string, std::vector<std::string>> result = parser.parse();
|
||||
|
||||
// printMap("result", result);
|
||||
// printMap("expected", data);
|
||||
|
||||
CHECK_EQ(result.size(), data.size());
|
||||
CHECK(std::equal(std::begin(result), std::end(result), std::begin(data)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(TestParse, SET_SQL_MODE) {
|
||||
TestParse(sql_mode, arraysize(sql_mode), "sql_mode");
|
||||
}
|
||||
|
||||
static Test time_zone[] = {
|
||||
{ "SET @@time_zone = 'Europe/Paris'", { Expected("time_zone", {"Europe/Paris"}) } },
|
||||
};
|
||||
|
||||
TEST(TestParse, SET_TIME_ZONE) {
|
||||
TestParse(time_zone, arraysize(time_zone), "time_zone");
|
||||
}
|
||||
|
||||
static Test names[] = {
|
||||
{ "SET NAMES utf8", { Expected("names", {"utf8"}) } },
|
||||
{ "SET NAMES 'utf8'", { Expected("names", {"utf8"}) } },
|
||||
{ "SET NAMES \"utf8\"", { Expected("names", {"utf8"}) } },
|
||||
{ "SET NAMES utf8 COLLATE unicode_ci", { Expected("names", {"utf8", "unicode_ci"}) } },
|
||||
};
|
||||
|
||||
TEST(TestParse, SET_NAMES) {
|
||||
TestParse(names, arraysize(names), "names");
|
||||
}
|
||||
|
||||
static Test multiple[] = {
|
||||
{ "SET time_zone = 'Europe/Paris', sql_mode = 'TRADITIONAL'", { Expected("time_zone", {"Europe/Paris"}), Expected("sql_mode", {"TRADITIONAL"}) } },
|
||||
{ "SET sql_mode = 'TRADITIONAL', NAMES 'utf8 COLLATE 'unicode_ci'", { Expected("sql_mode", {"TRADITIONAL"}), Expected("names", {"utf8", "unicode_ci"}) } },
|
||||
/* not supported by SetParser
|
||||
{ "SET @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483",
|
||||
{ Expected("sql_mode", {"CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO')"}), Expected("sql_auto_is_null", {"0"}),
|
||||
Expected("wait_timeout", {"2147483"}) } },
|
||||
*/
|
||||
};
|
||||
|
||||
TEST(TestParse, MULTIPLE) {
|
||||
TestParse(multiple, arraysize(multiple), "multiple");
|
||||
}
|
||||
Loading…
Reference in new issue