152 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.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/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 "sharding_common.h"
 | 
						|
#include <maxscale/alloc.h>
 | 
						|
#include <maxscale/poll.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * Extract the database name from a COM_INIT_DB or literal USE ... query.
 | 
						|
 * @param buf Buffer with the database change query
 | 
						|
 * @param str Pointer where the database name is copied
 | 
						|
 * @return True for success, false for failure
 | 
						|
 */
 | 
						|
bool extract_database(GWBUF* buf, char* str)
 | 
						|
{
 | 
						|
    uint8_t* packet;
 | 
						|
    char *saved, *tok, *query = NULL;
 | 
						|
    bool succp = true;
 | 
						|
    unsigned int plen;
 | 
						|
 | 
						|
    packet = GWBUF_DATA(buf);
 | 
						|
    plen = gw_mysql_get_byte3(packet) - 1;
 | 
						|
 | 
						|
    /** Copy database name from MySQL packet to session */
 | 
						|
    if (qc_get_operation(buf) == QUERY_OP_CHANGE_DB)
 | 
						|
    {
 | 
						|
        const char *delim = "` \n\t;";
 | 
						|
 | 
						|
        query = modutil_get_SQL(buf);
 | 
						|
        tok = strtok_r(query, delim, &saved);
 | 
						|
 | 
						|
        if (tok == NULL || strcasecmp(tok, "use") != 0)
 | 
						|
        {
 | 
						|
            MXS_ERROR("extract_database: Malformed chage database packet.");
 | 
						|
            succp = false;
 | 
						|
            goto retblock;
 | 
						|
        }
 | 
						|
 | 
						|
        tok = strtok_r(NULL, delim, &saved);
 | 
						|
 | 
						|
        if (tok == NULL)
 | 
						|
        {
 | 
						|
            MXS_ERROR("extract_database: Malformed change database packet.");
 | 
						|
            succp = false;
 | 
						|
            goto retblock;
 | 
						|
        }
 | 
						|
 | 
						|
        strncpy(str, tok, MYSQL_DATABASE_MAXLEN);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        memcpy(str, packet + 5, plen);
 | 
						|
        memset(str + plen, 0, 1);
 | 
						|
    }
 | 
						|
retblock:
 | 
						|
    MXS_FREE(query);
 | 
						|
    return succp;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Create a fake error message from a DCB.
 | 
						|
 * @param fail_str Custom error message
 | 
						|
 * @param dcb DCB to use as the origin of the error
 | 
						|
 */
 | 
						|
void create_error_reply(char* fail_str, DCB* dcb)
 | 
						|
{
 | 
						|
    MXS_INFO("change_current_db: failed to change database: %s", fail_str);
 | 
						|
    GWBUF* errbuf = modutil_create_mysql_err_msg(1, 0, 1049, "42000", fail_str);
 | 
						|
 | 
						|
    if (errbuf == NULL)
 | 
						|
    {
 | 
						|
        MXS_ERROR("Creating buffer for error message failed.");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    /** Set flags that help router to identify session commands reply */
 | 
						|
    gwbuf_set_type(errbuf, GWBUF_TYPE_MYSQL);
 | 
						|
    gwbuf_set_type(errbuf, GWBUF_TYPE_SESCMD_RESPONSE);
 | 
						|
    gwbuf_set_type(errbuf, GWBUF_TYPE_RESPONSE_END);
 | 
						|
 | 
						|
    poll_add_epollin_event_to_dcb(dcb,
 | 
						|
                                  errbuf);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Read new database name from MYSQL_COM_INIT_DB packet or a literal USE ... COM_QUERY
 | 
						|
 * packet, check that it exists in the hashtable and copy its name to MYSQL_session.
 | 
						|
 *
 | 
						|
 * @param dest Destination where the database name will be written
 | 
						|
 * @param dbhash Hashtable containing valid databases
 | 
						|
 * @param buf   Buffer containing the database change query
 | 
						|
 *
 | 
						|
 * @return true if new database is set, false if non-existent database was tried
 | 
						|
 * to be set
 | 
						|
 */
 | 
						|
bool change_current_db(char* dest,
 | 
						|
                       HASHTABLE* dbhash,
 | 
						|
                       GWBUF* buf)
 | 
						|
{
 | 
						|
    char* target;
 | 
						|
    bool succp;
 | 
						|
    char db[MYSQL_DATABASE_MAXLEN + 1];
 | 
						|
    if (GWBUF_LENGTH(buf) <= MYSQL_DATABASE_MAXLEN - 5)
 | 
						|
    {
 | 
						|
        /** Copy database name from MySQL packet to session */
 | 
						|
        if (!extract_database(buf, db))
 | 
						|
        {
 | 
						|
            succp = false;
 | 
						|
            goto retblock;
 | 
						|
        }
 | 
						|
        MXS_INFO("change_current_db: INIT_DB with database '%s'", db);
 | 
						|
        /**
 | 
						|
         * Update the session's active database only if it's in the hashtable.
 | 
						|
         * If it isn't found, send a custom error packet to the client.
 | 
						|
         */
 | 
						|
 | 
						|
        if ((target = (char*)hashtable_fetch(dbhash, (char*)db)) == NULL)
 | 
						|
        {
 | 
						|
            succp = false;
 | 
						|
            goto retblock;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            strcpy(dest, db);
 | 
						|
            MXS_INFO("change_current_db: database is on server: '%s'.", target);
 | 
						|
            succp = true;
 | 
						|
            goto retblock;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        /** Create error message */
 | 
						|
        MXS_ERROR("change_current_db: failed to change database: Query buffer too large");
 | 
						|
        MXS_INFO("change_current_db: failed to change database: "
 | 
						|
                 "Query buffer too large [%ld bytes]", GWBUF_LENGTH(buf));
 | 
						|
        succp = false;
 | 
						|
        goto retblock;
 | 
						|
    }
 | 
						|
 | 
						|
retblock:
 | 
						|
    return succp;
 | 
						|
}
 |