diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 889940e00..fe712008c 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -494,8 +494,15 @@ static skygw_query_type_t resolve_query_type( force_data_modify_op_replication) { type |= QUERY_TYPE_SESSION_WRITE; - } else { + } + else + { type |= QUERY_TYPE_WRITE; + + if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) + { + type |= QUERY_TYPE_CREATE_TMP_TABLE; + } } goto return_qtype; @@ -692,6 +699,17 @@ static skygw_query_type_t resolve_query_type( break; } } /**< for */ +#if defined(TEMPORARY_TABLES) + if ((skygw_query_type_t)type == QUERY_TYPE_READ) + { + /** + * Find out the database name and all tables the query + * uses. Create a hashvalue from each and if any of the + * values can be found from property's hashtable, set + * query type to QUERY_TYPE_READ_TMP_TABLE. + */ + } +#endif } /**< if */ return_qtype: qtype = (skygw_query_type_t)type; @@ -816,3 +834,25 @@ char* skygw_query_classifier_get_stmtname( return ((THD *)(mysql->thd))->lex->prepared_stmt_name.str; } + +/** + * Finds the head of the list of tables affected by the current select statement. + * @param thd Pointer to a valid thread descriptor structure + * @return Head of the TABLE_LIST chain or NULL in case of an error + */ +void* skygw_get_affected_tables(void* thdp) +{ + THD* thd = (THD*)thdp; + + if(thd == NULL || + thd->lex == NULL || + thd->lex->current_select == NULL) + { + ss_dassert(thd != NULL && + thd->lex != NULL && + thd->lex->current_select != NULL); + return NULL; + } + + return (void*)thd->lex->current_select->table_list.first; +} \ No newline at end of file diff --git a/query_classifier/query_classifier.h b/query_classifier/query_classifier.h index 31f9cf44e..52bfea4f2 100644 --- a/query_classifier/query_classifier.h +++ b/query_classifier/query_classifier.h @@ -43,7 +43,9 @@ typedef enum { QUERY_TYPE_COMMIT = 0x0200, /*< COMMIT */ QUERY_TYPE_PREPARE_NAMED_STMT = 0x0400, /*< Prepared stmt with name from user */ QUERY_TYPE_PREPARE_STMT = 0x0800, /*< Prepared stmt with id provided by server */ - QUERY_TYPE_EXEC_STMT = 0x1000 /*< Execute prepared statement */ + QUERY_TYPE_EXEC_STMT = 0x1000, /*< Execute prepared statement */ + QUERY_TYPE_CREATE_TMP_TABLE = 0x2000, /*< Create temporary table */ + QUERY_TYPE_READ_TMP_TABLE = 0x4000 /*< Read temporary table */ } skygw_query_type_t; #define QUERY_IS_TYPE(mask,type) ((mask & type) == type) @@ -60,6 +62,8 @@ skygw_query_type_t skygw_query_classifier_get_type( /** Free THD context and close MYSQL */ void skygw_query_classifier_free(MYSQL* mysql); char* skygw_query_classifier_get_stmtname(MYSQL* mysql); +void* skygw_get_affected_tables(void* thdp); + EXTERN_C_BLOCK_END diff --git a/server/core/hashtable.c b/server/core/hashtable.c index 50857bfec..b56f5d169 100644 --- a/server/core/hashtable.c +++ b/server/core/hashtable.c @@ -63,6 +63,10 @@ static void hashtable_read_lock(HASHTABLE *table); static void hashtable_read_unlock(HASHTABLE *table); static void hashtable_write_lock(HASHTABLE *table); static void hashtable_write_unlock(HASHTABLE *table); +static HASHTABLE *hashtable_alloc_real(HASHTABLE* target, + int size, + int (*hashfn)(), + int (*cmpfn)()); /** * Special null function used as default memory allfunctions in the hashtable @@ -81,11 +85,75 @@ nullfn(void *data) /** * Allocate a new hash table * + * @param target The address where hashtable is to be initialized, if NULL then allocate * @param size The size of the hash table * @param hashfn The user supplied hash function * @param cmpfn The user supplied key comparison function * @return The hashtable table */ +#if 1 +HASHTABLE * +hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)()) +{ + return hashtable_alloc_real(NULL, size, hashfn, cmpfn); +} + +HASHTABLE* hashtable_alloc_flat( + HASHTABLE* target, + int size, + int (*hashfn)(), + int (*cmpfn)()) +{ + return hashtable_alloc_real(target, size, hashfn, cmpfn); +} + +static HASHTABLE * +hashtable_alloc_real( + HASHTABLE* target, + int size, + int (*hashfn)(), + int (*cmpfn)()) +{ + HASHTABLE *rval; + + if (target == NULL) + { + if ((rval = malloc(sizeof(HASHTABLE))) == NULL) + return NULL; + rval->ht_isflat = false; + } + else + { + rval = target; + rval->ht_isflat = true; + } + +#if defined(SS_DEBUG) + rval->ht_chk_top = CHK_NUM_HASHTABLE; + rval->ht_chk_tail = CHK_NUM_HASHTABLE; +#endif + rval->hashsize = size; + rval->hashfn = hashfn; + rval->cmpfn = cmpfn; + rval->kcopyfn = nullfn; + rval->vcopyfn = nullfn; + rval->kfreefn = nullfn; + rval->vfreefn = nullfn; + rval->n_readers = 0; + rval->writelock = 0; + spinlock_init(&rval->spin); + if ((rval->entries = (HASHENTRIES **)calloc(size, sizeof(HASHENTRIES *))) == NULL) + { + free(rval); + return NULL; + } + memset(rval->entries, 0, size * sizeof(HASHENTRIES *)); + + return rval; +} + +#else + HASHTABLE * hashtable_alloc(int size, int (*hashfn)(), int (*cmpfn)()) { @@ -117,18 +185,49 @@ HASHTABLE *rval; return rval; } - +#endif /** * Delete an entire hash table * * @param table The hash table to delete */ +#if 1 + void hashtable_free(HASHTABLE *table) { int i; HASHENTRIES *entry, *ptr; + hashtable_write_lock(table); + for (i = 0; i < table->hashsize; i++) + { + entry = table->entries[i]; + while (entry) + { + ptr = entry->next; + table->kfreefn(entry->key); + table->vfreefn(entry->value); + free(entry); + entry = ptr; + } + } + free(table->entries); + + if (!table->ht_isflat) + { + free(table); + } +} + +#else + +void +hashtable_free(HASHTABLE *table) +{ + int i; + HASHENTRIES *entry, *ptr; + hashtable_write_lock(table); for (i = 0; i < table->hashsize; i++) { @@ -146,6 +245,7 @@ HASHENTRIES *entry, *ptr; free(table); } +#endif /** * Provide memory management functions to the hash table. This allows * function pointers to be registered that can make copies of the diff --git a/server/include/hashtable.h b/server/include/hashtable.h index 92bc71b8e..175bd5d32 100644 --- a/server/include/hashtable.h +++ b/server/include/hashtable.h @@ -84,12 +84,17 @@ typedef struct hashtable { SPINLOCK spin; /**< Internal spinlock for the hashtable */ int n_readers; /**< Number of clients reading the table */ int writelock; /**< The table is locked by a writer */ + bool ht_isflat; /**< Indicates whether hashtable is in stack or heap */ #if defined(SS_DEBUG) skygw_chk_t ht_chk_tail; #endif } HASHTABLE; extern HASHTABLE *hashtable_alloc(int, int (*hashfn)(), int (*cmpfn)()); +HASHTABLE *hashtable_alloc_flat(HASHTABLE* target, + int size, + int (*hashfn)(), + int (*cmpfn)()); /**< Allocate a hashtable */ extern void hashtable_memory_fns(HASHTABLE *, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN, HASHMEMORYFN); /**< Provide an interface to control key/value memory diff --git a/server/modules/include/readwritesplit.h b/server/modules/include/readwritesplit.h index 6fc639005..3530dc086 100644 --- a/server/modules/include/readwritesplit.h +++ b/server/modules/include/readwritesplit.h @@ -78,7 +78,8 @@ typedef enum rses_property_type_t { RSES_PROP_TYPE_UNDEFINED=-1, RSES_PROP_TYPE_SESCMD=0, RSES_PROP_TYPE_FIRST = RSES_PROP_TYPE_SESCMD, - RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_SESCMD, + RSES_PROP_TYPE_TMPTABLES, + RSES_PROP_TYPE_LAST=RSES_PROP_TYPE_TMPTABLES, RSES_PROP_TYPE_COUNT=RSES_PROP_TYPE_LAST+1 } rses_property_type_t; @@ -143,6 +144,7 @@ struct rses_property_st { rses_property_type_t rses_prop_type; union rses_prop_data { mysql_sescmd_t sescmd; + HASHTABLE tmp_table_hash; void* placeholder; /*< to be removed due new type */ } rses_prop_data; rses_property_t* rses_prop_next; /*< next property of same type */ diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 1c22d4979..7fdd5d8ef 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1263,6 +1263,9 @@ static int routeQuery( ss_dassert(succp); goto return_ret; } + /** + * qtype is QUERY_TYPE_WRITE or QUERY_TYPE_READ_TMP_TABLE + */ else { bool succp = true; @@ -1287,7 +1290,15 @@ static int routeQuery( if (!rses_begin_locked_router_action(router_cli_ses)) { goto return_ret; - } + } +#if defined(TEMPORARY_TABLES) + /** + * If query is of type QUERY_TYPE_CREATE_TMP_TABLE then find out + * the database and table name, create a hashvalue and + * add it to the router client session's property. If property + * doesn't exist then create it first. + */ +#endif if (master_dcb == NULL) { @@ -2682,8 +2693,13 @@ static bool execute_sescmd_in_backend( sescmd_cursor_clone_querybuf(scur)); break; - case MYSQL_COM_QUERY: - case MYSQL_COM_INIT_DB: + case MYSQL_COM_INIT_DB: +#if defined(TEMPORARY_TABLES) + /** + * Record database name and store to session. + */ +#endif + case MYSQL_COM_QUERY: default: /** * Mark session command buffer, it triggers writing @@ -2969,7 +2985,7 @@ static bool route_session_write( rses_property_done(prop); succp = false; goto return_succp; - } + } /** * Additional reference is created to querybuf to * prevent it from being released before properties