
The setting up and the initialization of the query classifier has now been separated. The gateway explicitly sets up the query classifier (i.e. chooses which one to use and what arguments to provide), but the actual initialization is performed as part of the general module initialization.
342 lines
8.3 KiB
C
342 lines
8.3 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/bsl.
|
|
*
|
|
* 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 <my_config.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <maxscale/query_classifier.h>
|
|
#include <maxscale/buffer.h>
|
|
#include <mysql.h>
|
|
#include <unistd.h>
|
|
#include <maxscale/gwdirs.h>
|
|
#include <maxscale/log_manager.h>
|
|
|
|
char* append(char* types, const char* type_name, size_t* lenp)
|
|
{
|
|
size_t len = strlen(type_name) + 1;
|
|
|
|
if (types)
|
|
{
|
|
len += 1;
|
|
}
|
|
|
|
*lenp += len;
|
|
|
|
char* tmp = realloc(types, *lenp);
|
|
|
|
if (types)
|
|
{
|
|
types = tmp;
|
|
strcat(types, "|");
|
|
strcat(types, type_name);
|
|
}
|
|
else
|
|
{
|
|
types = tmp;
|
|
strcpy(types, type_name);
|
|
}
|
|
|
|
|
|
return types;
|
|
}
|
|
|
|
char* get_types_as_string(uint32_t types)
|
|
{
|
|
char* s = NULL;
|
|
size_t len = 0;
|
|
|
|
if (types & QUERY_TYPE_LOCAL_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_LOCAL_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_WRITE)
|
|
{
|
|
s = append(s, "QUERY_TYPE_WRITE", &len);
|
|
}
|
|
if (types & QUERY_TYPE_MASTER_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_MASTER_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_SESSION_WRITE)
|
|
{
|
|
s = append(s, "QUERY_TYPE_SESSION_WRITE", &len);
|
|
}
|
|
if (types & QUERY_TYPE_USERVAR_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_USERVAR_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_SYSVAR_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_SYSVAR_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_GSYSVAR_READ)
|
|
{
|
|
s = append(s, "QUERY_TYPE_GSYSVAR_READ", &len);
|
|
}
|
|
if (types & QUERY_TYPE_GSYSVAR_WRITE)
|
|
{
|
|
s = append(s, "QUERY_TYPE_GSYSVAR_WRITE", &len);
|
|
}
|
|
if (types & QUERY_TYPE_BEGIN_TRX)
|
|
{
|
|
s = append(s, "QUERY_TYPE_BEGIN_TRX", &len);
|
|
}
|
|
if (types & QUERY_TYPE_ENABLE_AUTOCOMMIT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_ENABLE_AUTOCOMMIT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_DISABLE_AUTOCOMMIT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_DISABLE_AUTOCOMMIT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_ROLLBACK)
|
|
{
|
|
s = append(s, "QUERY_TYPE_ROLLBACK", &len);
|
|
}
|
|
if (types & QUERY_TYPE_COMMIT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_COMMIT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_PREPARE_NAMED_STMT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_PREPARE_NAMED_STMT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_PREPARE_STMT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_PREPARE_STMT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_EXEC_STMT)
|
|
{
|
|
s = append(s, "QUERY_TYPE_EXEC_STMT", &len);
|
|
}
|
|
if (types & QUERY_TYPE_CREATE_TMP_TABLE)
|
|
{
|
|
s = append(s, "QUERY_TYPE_CREATE_TMP_TABLE", &len);
|
|
}
|
|
if (types & QUERY_TYPE_READ_TMP_TABLE)
|
|
{
|
|
s = append(s, "QUERY_TYPE_READ_TMP_TABLE", &len);
|
|
}
|
|
if (types & QUERY_TYPE_SHOW_DATABASES)
|
|
{
|
|
s = append(s, "QUERY_TYPE_SHOW_DATABASES", &len);
|
|
}
|
|
if (types & QUERY_TYPE_SHOW_TABLES)
|
|
{
|
|
s = append(s, "QUERY_TYPE_SHOW_TABLES", &len);
|
|
}
|
|
|
|
if (!s)
|
|
{
|
|
s = append(s, "QUERY_TYPE_UNKNOWN", &len);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
int test(FILE* input, FILE* expected)
|
|
{
|
|
int rc = EXIT_SUCCESS;
|
|
|
|
int buffsz = getpagesize(), strsz = 0;
|
|
char buffer[1024], *strbuff = (char*)calloc(buffsz, sizeof(char));
|
|
|
|
int rd;
|
|
|
|
while ((rd = fread(buffer, sizeof(char), 1023, input)))
|
|
{
|
|
/**Fill the read buffer*/
|
|
|
|
if (strsz + rd >= buffsz)
|
|
{
|
|
char* tmp = realloc(strbuff, (buffsz * 2) * sizeof(char));
|
|
|
|
if (tmp == NULL)
|
|
{
|
|
free(strbuff);
|
|
fprintf(stderr, "Error: Memory allocation failed.");
|
|
return 1;
|
|
}
|
|
strbuff = tmp;
|
|
buffsz *= 2;
|
|
}
|
|
|
|
memcpy(strbuff + strsz, buffer, rd);
|
|
strsz += rd;
|
|
*(strbuff + strsz) = '\0';
|
|
|
|
char *tok, *nlptr;
|
|
|
|
/**Remove newlines*/
|
|
while ((nlptr = strpbrk(strbuff, "\n")) != NULL && (nlptr - strbuff) < strsz)
|
|
{
|
|
memmove(nlptr, nlptr + 1, strsz - (nlptr + 1 - strbuff));
|
|
strsz -= 1;
|
|
}
|
|
|
|
/**Parse read buffer for full queries*/
|
|
|
|
while (strpbrk(strbuff, ";") != NULL)
|
|
{
|
|
tok = strpbrk(strbuff, ";");
|
|
unsigned int qlen = tok - strbuff + 1;
|
|
unsigned int payload_len = qlen + 1;
|
|
unsigned int buf_len = payload_len + 4;
|
|
GWBUF* buff = gwbuf_alloc(buf_len);
|
|
*((unsigned char*)(GWBUF_DATA(buff))) = payload_len;
|
|
*((unsigned char*)(GWBUF_DATA(buff) + 1)) = (payload_len >> 8);
|
|
*((unsigned char*)(GWBUF_DATA(buff) + 2)) = (payload_len >> 16);
|
|
*((unsigned char*)(GWBUF_DATA(buff) + 3)) = 0x00;
|
|
*((unsigned char*)(GWBUF_DATA(buff) + 4)) = 0x03;
|
|
memcpy(GWBUF_DATA(buff) + 5, strbuff, qlen);
|
|
memmove(strbuff, tok + 1, strsz - qlen);
|
|
strsz -= qlen;
|
|
memset(strbuff + strsz, 0, buffsz - strsz);
|
|
qc_query_type_t type = qc_get_type(buff);
|
|
char expbuff[256];
|
|
int expos = 0;
|
|
|
|
while ((rd = fgetc(expected)) != '\n' && !feof(expected))
|
|
{
|
|
expbuff[expos++] = rd;
|
|
}
|
|
expbuff[expos] = '\0';
|
|
|
|
char *qtypestr = get_types_as_string(type);
|
|
const char* q = (const char*) GWBUF_DATA(buff) + 5;
|
|
|
|
printf("Query : %.*s\n", qlen, q);
|
|
printf("Reported: %s\n", qtypestr);
|
|
|
|
if (strcmp(qtypestr, expbuff) == 0)
|
|
{
|
|
printf("OK\n");
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR : %s\n", expbuff);
|
|
rc = 1;
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
free(qtypestr);
|
|
|
|
gwbuf_free(buff);
|
|
}
|
|
}
|
|
|
|
free(strbuff);
|
|
return rc;
|
|
}
|
|
|
|
int run(const char* input_filename, const char* expected_filename)
|
|
{
|
|
int rc = EXIT_FAILURE;
|
|
|
|
FILE *input = fopen(input_filename, "rb");
|
|
|
|
if (input)
|
|
{
|
|
FILE *expected = fopen(expected_filename, "rb");
|
|
|
|
if (expected)
|
|
{
|
|
rc = test(input, expected);
|
|
fclose(expected);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "error: Failed to open file %s.", expected_filename);
|
|
}
|
|
|
|
fclose(input);
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "error: Failed to open file %s", input_filename);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
int rc = EXIT_FAILURE;
|
|
|
|
if ((argc == 3) || (argc == 4))
|
|
{
|
|
const char* lib;
|
|
char* libdir;
|
|
const char* input_name;
|
|
const char* expected_name;
|
|
|
|
if (argc == 3)
|
|
{
|
|
lib = "qc_mysqlembedded";
|
|
libdir = strdup("../qc_mysqlembedded");
|
|
input_name = argv[1];
|
|
expected_name = argv[2];
|
|
}
|
|
else
|
|
{
|
|
lib = argv[1];
|
|
input_name = argv[2];
|
|
expected_name = argv[3];
|
|
|
|
size_t sz = strlen(lib);
|
|
char buffer[sz + 3 + 1]; // "../" and terminating NULL.
|
|
sprintf(buffer, "../%s", lib);
|
|
|
|
libdir = strdup(buffer);
|
|
}
|
|
|
|
set_libdir(libdir);
|
|
set_datadir(strdup("/tmp"));
|
|
set_langdir(strdup("."));
|
|
set_process_datadir(strdup("/tmp"));
|
|
|
|
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
|
|
{
|
|
if (qc_setup(lib, NULL) && qc_process_init())
|
|
{
|
|
rc = run(input_name, expected_name);
|
|
qc_process_end();
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "error: %s: Could not initialize query classifier library %s.\n",
|
|
argv[0], lib);
|
|
}
|
|
|
|
mxs_log_finish();
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "error: %s: Could not initialize log.\n", argv[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Usage: classify <input> <expected output>\n");
|
|
}
|
|
|
|
return rc;
|
|
}
|