Files
MaxScale/server/core/trxboundarymatcher.cc
2017-03-15 09:35:15 +02:00

322 lines
6.6 KiB
C++

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2019-07-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include "maxscale/trxboundarymatcher.hh"
#include <maxscale/platform.h>
#include <maxscale/modutil.h>
namespace
{
struct REGEX_DATA
{
const char* zMatch;
uint32_t type_mask;
pcre2_code* pCode;
};
REGEX_DATA this_unit_regexes[] =
{
{
"^\\s*BEGIN(\\s+WORK)?\\s*;?\\s*$",
QUERY_TYPE_BEGIN_TRX
},
{
"^\\s*COMMIT(\\s+WORK)?\\s*;?\\s*$",
QUERY_TYPE_COMMIT,
},
{
"^\\s*ROLLBACK(\\s+WORK)?\\s*;?\\s*$",
QUERY_TYPE_ROLLBACK
},
{
"^\\s*START\\s+TRANSACTION\\s+("
"READ\\s+ONLY|"
"READ\\s+ONLY\\s*,\\s*WITH\\s+CONSISTENT\\s+SNAPSHOT|"
"WITH\\s+CONSISTENT\\s+SNAPSHOT\\s*,\\s*READ\\s+ONLY"
")\\s*;?\\s*$",
QUERY_TYPE_BEGIN_TRX | QUERY_TYPE_READ
},
{
"^\\s*START\\s+TRANSACTION\\s+("
"READ\\s+WRITE|"
"READ\\s+WRITE\\s*,\\s*WITH\\s+CONSISTENT\\s+SNAPSHOT|"
"WITH\\s+CONSISTENT\\s+SNAPSHOT\\s*,\\s*READ\\s+WRITE"
")\\s*;?\\s*$",
QUERY_TYPE_BEGIN_TRX | QUERY_TYPE_WRITE
},
{
"^\\s*START\\s+TRANSACTION(\\s+WITH\\s+CONSISTENT\\s+SNAPSHOT)?\\s*;?\\s*$",
QUERY_TYPE_BEGIN_TRX
},
{
"^\\s*SET\\s+AUTOCOMMIT\\s*\\=\\s*(1|true)\\s*;?\\s*$",
QUERY_TYPE_COMMIT|QUERY_TYPE_ENABLE_AUTOCOMMIT
},
{
"^\\s*SET\\s+AUTOCOMMIT\\s*\\=\\s*(0|false)\\s*;?\\s*$",
QUERY_TYPE_BEGIN_TRX|QUERY_TYPE_DISABLE_AUTOCOMMIT
}
};
const size_t N_REGEXES = sizeof(this_unit_regexes)/sizeof(this_unit_regexes[0]);
struct this_unit
{
const char* zMatch;
pcre2_code* pCode;
REGEX_DATA* pRegexes;
} this_unit =
{
.zMatch = "^\\s*(BEGIN|COMMIT|ROLLBACK|START|SET)",
NULL,
.pRegexes = this_unit_regexes
};
thread_local struct this_thread
{
pcre2_match_data* pData;
pcre2_match_data* match_datas[N_REGEXES];
} this_thread;
bool compile_regexes();
void free_regexes();
bool create_thread_data();
void free_thread_data();
void log_pcre2_error(const char* zMatch, int errcode, int erroffset)
{
PCRE2_UCHAR errbuf[512];
pcre2_get_error_message(errcode, errbuf, sizeof(errbuf));
MXS_ERROR("Regex compilation failed at %d for regex '%s', i.e. '%s': %s.",
erroffset, zMatch, zMatch + erroffset, errbuf);
}
bool compile_regexes()
{
bool success = true;
int errcode;
PCRE2_SIZE erroffset;
this_unit.pCode = pcre2_compile((PCRE2_SPTR)this_unit.zMatch,
PCRE2_ZERO_TERMINATED, PCRE2_CASELESS,
&errcode, &erroffset, NULL);
if (this_unit.pCode)
{
REGEX_DATA* i = this_unit.pRegexes;
REGEX_DATA* end = i + N_REGEXES;
while (success && (i < end))
{
i->pCode = pcre2_compile((PCRE2_SPTR)i->zMatch, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS,
&errcode, &erroffset, NULL);
if (!i->pCode)
{
success = false;
log_pcre2_error(i->zMatch, errcode, erroffset);
}
++i;
}
if (!success)
{
free_regexes();
}
}
else
{
success = false;
log_pcre2_error(this_unit.zMatch, errcode, erroffset);
}
return success;
}
void free_regexes()
{
REGEX_DATA* begin = this_unit.pRegexes;
REGEX_DATA* i = begin + N_REGEXES;
while (i > begin)
{
--i;
if (i->pCode)
{
pcre2_code_free(i->pCode);
i->pCode = NULL;
}
}
if (this_unit.pCode)
{
pcre2_code_free(this_unit.pCode);
this_unit.pCode = NULL;
}
}
bool create_thread_data()
{
bool success = true;
this_thread.pData = pcre2_match_data_create_from_pattern(this_unit.pCode, NULL);
if (this_thread.pData)
{
REGEX_DATA* i = this_unit.pRegexes;
REGEX_DATA* end = i + N_REGEXES;
pcre2_match_data** ppData = this_thread.match_datas;
while (success && (i < end))
{
*ppData = pcre2_match_data_create_from_pattern(i->pCode, NULL);
if (!*ppData)
{
success = false;
MXS_ERROR("PCRE2 match data creation failed.");
}
++i;
++ppData;
}
if (!success)
{
free_thread_data();
}
}
else
{
success = false;
MXS_ERROR("PCRE2 match data creation failed.");
}
return success;
}
void free_thread_data()
{
pcre2_match_data** begin = this_thread.match_datas;
pcre2_match_data** i = begin + N_REGEXES;
while (i > begin)
{
--i;
if (*i)
{
pcre2_match_data_free(*i);
*i = NULL;
}
}
pcre2_match_data_free(this_thread.pData);
this_thread.pData = NULL;
}
}
namespace maxscale
{
//static
bool TrxBoundaryMatcher::process_init()
{
bool rc = compile_regexes();
if (rc)
{
rc = thread_init();
if (!rc)
{
free_regexes();
}
}
return rc;
}
//static
void TrxBoundaryMatcher::process_end()
{
thread_end();
free_regexes();
}
//static
bool TrxBoundaryMatcher::thread_init()
{
return create_thread_data();
}
//static
void TrxBoundaryMatcher::thread_end()
{
free_thread_data();
}
//static
uint32_t TrxBoundaryMatcher::type_mask_of(const char* pSql, size_t len)
{
uint32_t type_mask = 0;
if (pcre2_match(this_unit.pCode, (PCRE2_SPTR)pSql, len, 0, 0, this_thread.pData, NULL) >= 0)
{
REGEX_DATA* i = this_unit.pRegexes;
REGEX_DATA* end = i + N_REGEXES;
pcre2_match_data** ppData = this_thread.match_datas;
while ((type_mask == 0) && (i < end))
{
if (pcre2_match(i->pCode, (PCRE2_SPTR)pSql, len, 0, 0, *ppData, NULL) >= 0)
{
type_mask = i->type_mask;
}
++i;
++ppData;
}
}
return type_mask;
}
//static
uint32_t TrxBoundaryMatcher::type_mask_of(GWBUF* pBuf)
{
uint32_t type_mask = 0;
char* pSql;
int len;
// This will exclude prepared statement but we are fine with that.
if (modutil_extract_SQL(pBuf, &pSql, &len))
{
type_mask = type_mask_of(pSql, len);
}
return type_mask;
}
}