Commit d72eebaa authored by Marko Mäkelä's avatar Marko Mäkelä

Merge 10.1 into 10.2

parents 4832b751 49854811
......@@ -14,7 +14,7 @@ set default role role_a for user_a@localhost;
set default role invalid_role for user_a@localhost;
ERROR OP000: Invalid role specification `invalid_role`
set default role role_b for user_a@localhost;
ERROR OP000: Invalid role specification `role_b`
ERROR OP000: User `user_a@localhost` has not been granted role `role_b`
set default role role_b for user_b@localhost;
show grants;
Grants for user_a@localhost
......@@ -36,7 +36,7 @@ user host default_role
user_a localhost role_a
user_b localhost role_b
set default role role_b for current_user;
ERROR OP000: Invalid role specification `role_b`
ERROR OP000: User `user_a@localhost` has not been granted role `role_b`
show grants;
Grants for user_b@localhost
GRANT role_b TO 'user_b'@'localhost'
......
......@@ -38,3 +38,90 @@ ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'us
drop role test_role;
drop role not_granted_role;
drop user test_user@localhost;
#
# MDEV-22312: Bad error message for SET DEFAULT ROLE when user account
# is not granted the role
#
CREATE USER a;
CREATE USER b;
CREATE ROLE r1;
CREATE ROLE r2;
SET DEFAULT ROLE r1 FOR a;
ERROR OP000: User `a@%` has not been granted role `r1`
GRANT r1 TO b;
GRANT r2 TO b;
SET DEFAULT ROLE r1 FOR b;
# Change user b
SELECT CURRENT_ROLE;
CURRENT_ROLE
r1
SET ROLE r2;
SELECT CURRENT_ROLE;
CURRENT_ROLE
r2
SET DEFAULT ROLE r1 FOR a;
ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
SET DEFAULT ROLE r2;
# Change user root (session 1: select_priv to b)
GRANT SELECT ON mysql.* TO b;
# Change user b (session 1: select_priv)
SHOW GRANTS FOR b;
Grants for b@%
GRANT r1 TO 'b'@'%'
GRANT r2 TO 'b'@'%'
GRANT USAGE ON *.* TO 'b'@'%'
GRANT SELECT ON `mysql`.* TO 'b'@'%'
SET DEFAULT ROLE r1 FOR a;
ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
SELECT CURRENT_ROLE;
CURRENT_ROLE
r2
SET DEFAULT ROLE NONE;
SELECT CURRENT_ROLE;
CURRENT_ROLE
r2
SET DEFAULT ROLE current_role FOR current_user;
SET DEFAULT ROLE invalid_role;
ERROR OP000: Invalid role specification `invalid_role`
SET DEFAULT ROLE invalid_role FOR a;
ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
SET DEFAULT ROLE none FOR a;
ERROR 42000: Access denied for user 'b'@'%' to database 'mysql'
# Change user root (session 2: adding update_priv to user b)
GRANT UPDATE ON mysql.* TO b;
# Change user b
SHOW GRANTS FOR b;
Grants for b@%
GRANT r1 TO 'b'@'%'
GRANT r2 TO 'b'@'%'
GRANT USAGE ON *.* TO 'b'@'%'
GRANT SELECT, UPDATE ON `mysql`.* TO 'b'@'%'
SET DEFAULT ROLE r1 FOR a;
ERROR OP000: User `a@%` has not been granted role `r1`
SET DEFAULT ROLE invalid_role;
ERROR OP000: Invalid role specification `invalid_role`
SET DEFAULT ROLE invalid_role FOR a;
ERROR OP000: Invalid role specification `invalid_role`
SET DEFAULT ROLE none FOR a;
# Change user root (session 3: Grant role to user a)
GRANT r1 TO a;
SET DEFAULT ROLE r1 FOR a;
# Change user a (verify session 3)
SELECT CURRENT_ROLE;
CURRENT_ROLE
r1
SET DEFAULT ROLE None;
# Change user b (session 3: role granted to user a)
SET DEFAULT ROLE r1 FOR a;
SET DEFAULT ROLE r2 FOR a;
ERROR OP000: User `a@%` has not been granted role `r2`
SET DEFAULT ROLE invalid_role;
ERROR OP000: Invalid role specification `invalid_role`
SET DEFAULT ROLE invalid_role FOR a;
ERROR OP000: Invalid role specification `invalid_role`
SELECT user, host, default_role FROM mysql.user where user='a' or user='b';
user host default_role
a % r1
b % r2
DROP ROLE r1, r2;
DROP USER a, b;
......@@ -60,3 +60,110 @@ change_user 'root';
drop role test_role;
drop role not_granted_role;
drop user test_user@localhost;
--echo #
--echo # MDEV-22312: Bad error message for SET DEFAULT ROLE when user account
--echo # is not granted the role
--echo #
CREATE USER a;
CREATE USER b;
CREATE ROLE r1;
CREATE ROLE r2;
# Role has not been granted to user a, but the role is visible to current_user
--error ER_INVALID_ROLE
SET DEFAULT ROLE r1 FOR a;
# Granting roles to user b
GRANT r1 TO b;
GRANT r2 TO b;
# After granting the role, role can be set as default
SET DEFAULT ROLE r1 FOR b;
--echo # Change user b
change_user b;
SELECT CURRENT_ROLE;
SET ROLE r2;
SELECT CURRENT_ROLE;
# User b has no UPDATE_PRIV for mysql.user
--error ER_DBACCESS_DENIED_ERROR
SET DEFAULT ROLE r1 FOR a;
SET DEFAULT ROLE r2;
--echo # Change user root (session 1: select_priv to b)
change_user root;
# Let's grant select_priv to user b
GRANT SELECT ON mysql.* TO b;
--echo # Change user b (session 1: select_priv)
change_user b;
SHOW GRANTS FOR b;
# User must have update_priv before setting the role
--error ER_DBACCESS_DENIED_ERROR
SET DEFAULT ROLE r1 FOR a;
# Testing the `CURRENT_ROLE` as a special case
SELECT CURRENT_ROLE;
SET DEFAULT ROLE NONE;
SELECT CURRENT_ROLE;
SET DEFAULT ROLE current_role FOR current_user;
# Testing of non-existing role
--error ER_INVALID_ROLE
SET DEFAULT ROLE invalid_role;
# Testing of non-existing role for different user
--error ER_DBACCESS_DENIED_ERROR
SET DEFAULT ROLE invalid_role FOR a;
# Testing the `None` role for different user
-- error ER_DBACCESS_DENIED_ERROR
SET DEFAULT ROLE none FOR a;
--echo # Change user root (session 2: adding update_priv to user b)
change_user root;
# update_priv are enough
GRANT UPDATE ON mysql.* TO b;
--echo # Change user b
change_user b;
SHOW GRANTS FOR b;
# In all tests in session user a has not been granted the role
# Testing setting role for different user, should fail with new error
--error ER_INVALID_ROLE
SET DEFAULT ROLE r1 FOR a;
# Testing of non-existing role
--error ER_INVALID_ROLE
SET DEFAULT ROLE invalid_role;
# Testing of non-existing role for different user with update_priv
--error ER_INVALID_ROLE
SET DEFAULT ROLE invalid_role FOR a;
# Testing the `None` role for different user with update_priv
SET DEFAULT ROLE none FOR a;
--echo # Change user root (session 3: Grant role to user a)
change_user root;
# After granting the privilege for a, user b can set default role
GRANT r1 TO a;
SET DEFAULT ROLE r1 FOR a;
--echo # Change user a (verify session 3)
change_user a;
SELECT CURRENT_ROLE;
SET DEFAULT ROLE None;
--echo # Change user b (session 3: role granted to user a)
change_user b;
# This should set role because b has update_priv
SET DEFAULT ROLE r1 FOR a;
# Testing non-granted role r2 still should fail
-- error ER_INVALID_ROLE
SET DEFAULT ROLE r2 FOR a;
# Testing of non-existing role
--error ER_INVALID_ROLE
SET DEFAULT ROLE invalid_role;
# Testing of non-existing role for different user
--error ER_INVALID_ROLE
SET DEFAULT ROLE invalid_role FOR a;
# Clear the workspace
change_user root;
--sorted_result
SELECT user, host, default_role FROM mysql.user where user='a' or user='b';
DROP ROLE r1, r2;
DROP USER a, b;
......@@ -66,7 +66,7 @@ Grants for test_user@localhost
GRANT USAGE ON *.* TO 'test_user'@'localhost'
GRANT test_role1 TO 'test_user'@'localhost'
set role test_role2;
ERROR OP000: Invalid role specification `test_role2`
ERROR OP000: User `test_user@localhost` has not been granted role `test_role2`
select current_user(), current_role();
current_user() current_role()
test_user@localhost NULL
......
......@@ -815,7 +815,6 @@ String *Item_nodeset_func_attributebyname::val_nodeset(String *nodeset)
String *Item_nodeset_func_predicate::val_nodeset(String *str)
{
Item_nodeset_func *nodeset_func= (Item_nodeset_func*) args[0];
Item_func *comp_func= (Item_func*)args[1];
uint pos= 0, size;
prepare(str);
size= (uint)(fltend - fltbeg);
......@@ -825,7 +824,7 @@ String *Item_nodeset_func_predicate::val_nodeset(String *str)
((XPathFilter*)(&nodeset_func->context_cache))->append_element(flt->num,
flt->pos,
size);
if (comp_func->val_int())
if (args[1]->val_int())
((XPathFilter*)str)->append_element(flt->num, pos++);
}
return str;
......
......@@ -971,8 +971,17 @@ int set_var_default_role::check(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
real_user= get_current_user(thd, user);
int status= acl_check_set_default_role(thd, real_user->host.str, real_user->user.str);
return status;
real_role= role.str;
if (role.str == current_role.str)
{
if (!thd->security_ctx->priv_role[0])
real_role= "NONE";
else
real_role= thd->security_ctx->priv_role;
}
return acl_check_set_default_role(thd, real_user->host.str,
real_user->user.str, real_role);
#else
return 0;
#endif
......@@ -983,7 +992,8 @@ int set_var_default_role::update(THD *thd)
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
thd->m_reprepare_observer= 0;
int res= acl_set_default_role(thd, real_user->host.str, real_user->user.str, role.str);
int res= acl_set_default_role(thd, real_user->host.str, real_user->user.str,
real_role);
thd->m_reprepare_observer= save_reprepare_observer;
return res;
#else
......
......@@ -350,6 +350,7 @@ class set_var_default_role: public set_var_base
{
LEX_USER *user, *real_user;
LEX_STRING role;
const char *real_role;
public:
set_var_default_role(LEX_USER *user_arg, LEX_STRING role_arg) :
user(user_arg), role(role_arg) {}
......
/* Copyright (c) 2000, 2018, Oracle and/or its affiliates.
Copyright (c) 2009, 2018, MariaDB
Copyright (c) 2009, 2020, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -201,7 +201,6 @@ LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") };
LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") };
LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") };
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
#endif
......@@ -2618,8 +2617,43 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_RETURN(res);
}
static int check_user_can_set_role(const char *user, const char *host,
const char *ip, const char *rolename, ulonglong *access)
static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
{
LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
if (rolename->length == grantee->user.length &&
!strcmp(rolename->str, grantee->user.str))
return -1; // End search, we've found our role.
/* Keep looking, we haven't found our role yet. */
return 0;
}
/*
unlike find_user_exact and find_user_wild,
this function finds anonymous users too, it's when a
user is not empty, but priv_user (acl_user->user) is empty.
*/
static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
{
ACL_USER *result= NULL;
mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0; i < acl_users.elements; i++)
{
ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
if ((!acl_user_tmp->user.str ||
!strcmp(user, acl_user_tmp->user.str)) &&
compare_hostname(&acl_user_tmp->host, host, ip))
{
result= acl_user_tmp;
break;
}
}
return result;
}
static int check_user_can_set_role(THD *thd, const char *user, const char *host,
const char *ip, const char *rolename,
ulonglong *access)
{
ACL_ROLE *role;
ACL_USER_BASE *acl_user_base;
......@@ -2636,10 +2670,7 @@ static int check_user_can_set_role(const char *user, const char *host,
/* get the current user */
acl_user= find_user_wild(host, user, ip);
if (acl_user == NULL)
{
my_error(ER_INVALID_CURRENT_USER, MYF(0));
result= -1;
}
result= ER_INVALID_CURRENT_USER;
else if (access)
*access= acl_user->access;
......@@ -2649,9 +2680,9 @@ static int check_user_can_set_role(const char *user, const char *host,
role= find_acl_role(rolename);
/* According to SQL standard, the same error message must be presented */
if (role == NULL) {
my_error(ER_INVALID_ROLE, MYF(0), rolename);
result= -1;
if (role == NULL)
{
result= ER_INVALID_ROLE;
goto end;
}
......@@ -2672,7 +2703,6 @@ static int check_user_can_set_role(const char *user, const char *host,
/* According to SQL standard, the same error message must be presented */
if (!is_granted)
{
my_error(ER_INVALID_ROLE, MYF(0), rolename);
result= 1;
goto end;
}
......@@ -2681,17 +2711,66 @@ static int check_user_can_set_role(const char *user, const char *host,
{
*access = acl_user->access | role->access;
}
end:
mysql_mutex_unlock(&acl_cache->lock);
return result;
/* We present different error messages depending if the user has sufficient
privileges to know if the INVALID_ROLE exists. */
switch (result)
{
case ER_INVALID_CURRENT_USER:
my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
break;
case ER_INVALID_ROLE:
/* Role doesn't exist at all */
my_error(ER_INVALID_ROLE, MYF(0), rolename);
break;
case 1:
StringBuffer<1024> c_usr;
LEX_CSTRING role_lex;
/* First, check if current user can see mysql database. */
bool read_access= !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 1);
role_lex.str= rolename;
role_lex.length= strlen(rolename);
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *cur_user= find_user_or_anon(thd->security_ctx->priv_host,
thd->security_ctx->priv_user,
thd->security_ctx->ip);
/* If the current user does not have select priv to mysql database,
see if the current user can discover the role if it was granted to him.
*/
if (cur_user && (read_access ||
traverse_role_graph_down(cur_user, &role_lex,
check_role_is_granted_callback,
NULL) == -1))
{
/* Role is not granted but current user can see the role */
c_usr.append(user, strlen(user));
c_usr.append('@');
c_usr.append(host, strlen(host));
my_printf_error(ER_INVALID_ROLE, "User %`s has not been granted role %`s",
MYF(0), c_usr.c_ptr(), rolename);
}
else
{
/* Role is not granted and current user cannot see the role */
my_error(ER_INVALID_ROLE, MYF(0), rolename);
}
mysql_mutex_unlock(&acl_cache->lock);
break;
}
return result;
}
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
{
/* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
return check_user_can_set_role(thd->security_ctx->priv_user,
thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
return check_user_can_set_role(thd, thd->security_ctx->priv_user,
thd->security_ctx->host, thd->security_ctx->ip, rolename, access);
}
......@@ -3471,9 +3550,12 @@ bool change_password(THD *thd, LEX_USER *user)
DBUG_RETURN(result);
}
int acl_check_set_default_role(THD *thd, const char *host, const char *user)
int acl_check_set_default_role(THD *thd, const char *host, const char *user,
const char *role)
{
return check_alter_user(thd, host, user);
DBUG_ENTER("acl_check_set_default_role");
DBUG_RETURN(check_alter_user(thd, host, user) ||
check_user_can_set_role(thd, user, host, NULL, role, NULL));
}
int acl_set_default_role(THD *thd, const char *host, const char *user,
......@@ -3494,16 +3576,6 @@ int acl_set_default_role(THD *thd, const char *host, const char *user,
DBUG_PRINT("enter",("host: '%s' user: '%s' rolename: '%s'",
safe_str(user), safe_str(host), safe_str(rolename)));
if (rolename == current_role.str) {
if (!thd->security_ctx->priv_role[0])
rolename= "NONE";
else
rolename= thd->security_ctx->priv_role;
}
if (check_user_can_set_role(user, host, host, rolename, NULL))
DBUG_RETURN(result);
if (!strcasecmp(rolename, "NONE"))
clear_role= TRUE;
......@@ -3667,31 +3739,6 @@ bool is_acl_user(const char *host, const char *user)
return res;
}
/*
unlike find_user_exact and find_user_wild,
this function finds anonymous users too, it's when a
user is not empty, but priv_user (acl_user->user) is empty.
*/
static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
{
ACL_USER *result= NULL;
mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0; i < acl_users.elements; i++)
{
ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
if ((!acl_user_tmp->user.str ||
!strcmp(user, acl_user_tmp->user.str)) &&
compare_hostname(&acl_user_tmp->host, host, ip))
{
result= acl_user_tmp;
break;
}
}
return result;
}
/*
Find first entry that matches the specified user@host pair
*/
......@@ -3997,7 +4044,7 @@ static bool test_if_create_new_users(THD *thd)
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
create_new_users=0;
create_new_users=0;
}
}
return create_new_users;
......@@ -9244,17 +9291,6 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
mysql_mutex_unlock(&acl_cache->lock);
}
static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data)
{
LEX_CSTRING *rolename= static_cast<LEX_CSTRING *>(data);
if (rolename->length == grantee->user.length &&
!strcmp(rolename->str, grantee->user.str))
return -1; // End search, we've found our role.
/* Keep looking, we haven't found our role yet. */
return 0;
}
/*
Modify a privilege table.
......@@ -11094,7 +11130,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
Security context in THD contains two pairs of (user,host):
1. (user,host) pair referring to inbound connection.
2. (priv_user,priv_host) pair obtained from mysql.user table after doing
authnetication of incoming connection.
authentication of incoming connection.
Privileges should be checked wrt (priv_user, priv_host) tuple, because
(user,host) pair obtained from inbound connection may have different
values than what is actually stored in mysql.user table and while granting
......@@ -11511,7 +11547,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
ulong j,test_access= want_access & ~GRANT_ACL;
for (priv_id=0, j = SELECT_ACL;j <= GLOBAL_ACLS; priv_id++,j <<= 1)
{
if (test_access & j)
if (test_access & j)
{
if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
command_array[priv_id],
......
......@@ -409,7 +409,7 @@ bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
bool with_grant);
int acl_setrole(THD *thd, char *rolename, ulonglong access);
int acl_check_setrole(THD *thd, char *rolename, ulonglong *access);
int acl_check_set_default_role(THD *thd, const char *host, const char *user);
int acl_check_set_default_role(THD *thd, const char *host, const char *user, const char *role);
int acl_set_default_role(THD *thd, const char *host, const char *user,
const char *rolename);
......
This diff is collapsed.
......@@ -280,7 +280,6 @@ class Table_statistics
Column_statistics *column_stats; /* Array of statistical data for columns */
Index_statistics *index_stats; /* Array of statistical data for indexes */
ulong *idx_avg_frequency; /* Array of records per key for index prefixes */
ulong total_hist_size; /* Total size of all histograms */
uchar *histograms; /* Sequence of histograms */
};
......
......@@ -440,10 +440,6 @@ void TABLE_SHARE::destroy()
delete_stat_values_for_table_share(this);
free_root(&stats_cb.mem_root, MYF(0));
stats_cb.stats_can_be_read= FALSE;
stats_cb.stats_is_read= FALSE;
stats_cb.histograms_can_be_read= FALSE;
stats_cb.histograms_are_read= FALSE;
/* The mutexes are initialized only for shares that are part of the TDC */
if (tmp_table == NO_TMP_TABLE)
......
......@@ -567,15 +567,94 @@ enum open_frm_error {
from persistent statistical tables
*/
struct TABLE_STATISTICS_CB
class TABLE_STATISTICS_CB
{
class Statistics_state
{
enum state_codes
{
EMPTY, /** data is not loaded */
LOADING, /** data is being loaded in some connection */
READY /** data is loaded and available for use */
};
int32 state;
public:
/** No state copy */
Statistics_state &operator=(const Statistics_state &) { return *this; }
/** Checks if data loading have been completed */
bool is_ready() const
{
return my_atomic_load32_explicit(const_cast<int32*>(&state),
MY_MEMORY_ORDER_ACQUIRE) == READY;
}
/**
Sets mutual exclusion for data loading
If stats are in LOADING state, waits until state change.
@return
@retval true atomic EMPTY -> LOADING transfer completed, ok to load
@retval false stats are in READY state, no need to load
*/
bool start_load()
{
for (;;)
{
int32 expected= EMPTY;
if (my_atomic_cas32_weak_explicit(&state, &expected, LOADING,
MY_MEMORY_ORDER_RELAXED,
MY_MEMORY_ORDER_RELAXED))
return true;
if (expected == READY)
return false;
(void) LF_BACKOFF;
}
}
/** Marks data available for subsequent use */
void end_load()
{
DBUG_ASSERT(my_atomic_load32_explicit(&state, MY_MEMORY_ORDER_RELAXED) ==
LOADING);
my_atomic_store32_explicit(&state, READY, MY_MEMORY_ORDER_RELEASE);
}
/** Restores empty state on error (e.g. OOM) */
void abort_load()
{
DBUG_ASSERT(my_atomic_load32_explicit(&state, MY_MEMORY_ORDER_RELAXED) ==
LOADING);
my_atomic_store32_explicit(&state, EMPTY, MY_MEMORY_ORDER_RELAXED);
}
};
class Statistics_state stats_state;
class Statistics_state hist_state;
public:
MEM_ROOT mem_root; /* MEM_ROOT to allocate statistical data for the table */
Table_statistics *table_stats; /* Structure to access the statistical data */
bool stats_can_be_read; /* Memory for statistical data is allocated */
bool stats_is_read; /* Statistical data for table has been read
from statistical tables */
bool histograms_can_be_read;
bool histograms_are_read;
ulong total_hist_size; /* Total size of all histograms */
bool histograms_are_ready() const
{
return !total_hist_size || hist_state.is_ready();
}
bool start_histograms_load()
{
return total_hist_size && hist_state.start_load();
}
void end_histograms_load() { hist_state.end_load(); }
void abort_histograms_load() { hist_state.abort_load(); }
bool stats_are_ready() const { return stats_state.is_ready(); }
bool start_stats_load() { return stats_state.start_load(); }
void end_stats_load() { stats_state.end_load(); }
void abort_stats_load() { stats_state.abort_load(); }
};
......
......@@ -932,7 +932,7 @@ MRN_SHARE *mrn_get_share(const char *table_name, TABLE *table, int *error)
share->wrap_key_info = NULL;
share->wrap_primary_key = MAX_KEY;
}
memcpy(wrap_table_share, table->s, sizeof(*wrap_table_share));
*wrap_table_share= *table->s;
mrn_init_sql_alloc(current_thd, &(wrap_table_share->mem_root));
wrap_table_share->keys = share->wrap_keys;
wrap_table_share->key_info = share->wrap_key_info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment