diff --git a/extra/comp_err.c b/extra/comp_err.c index 9ddd1d7d971..8bc8a989a6a 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -191,10 +191,11 @@ int main(int argc, char *argv[]) static int create_header_files(struct errors *error_head) { - uint er_count= 0; + uint er_last; FILE *er_definef, *sql_statef; struct errors *tmp_error; DBUG_ENTER("create_header_files"); + LINT_INIT(er_last); if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME)))) { @@ -209,6 +210,8 @@ static int create_header_files(struct errors *error_head) fprintf(er_definef, "/* Autogenerated file, please don't edit */\n\n"); fprintf(sql_statef, "/* Autogenerated file, please don't edit */\n\n"); + fprintf(er_definef, "#define ER_ERROR_FIRST %d\n", error_head->d_code); + for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error) { /* @@ -217,16 +220,16 @@ static int create_header_files(struct errors *error_head) */ fprintf(er_definef, "#define %s %d\n", tmp_error->er_name, tmp_error->d_code); + er_last= tmp_error->d_code; /* generating sql_state.h file */ if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0]) fprintf(sql_statef, "%-40s,\"%s\", \"%s\",\n", tmp_error->er_name, tmp_error->sql_code1, tmp_error->sql_code2); - er_count++; } /* finishing off with mysqld_error.h */ - fprintf(er_definef, "#define ER_ERROR_MESSAGES %d\n", er_count); + fprintf(er_definef, "#define ER_ERROR_LAST %d\n", er_last); my_fclose(er_definef, MYF(0)); my_fclose(sql_statef, MYF(0)); DBUG_RETURN(0); diff --git a/include/errmsg.h b/include/errmsg.h index 6115b24a3d8..55bbdf6d767 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -21,6 +21,7 @@ extern "C" { #endif void init_client_errs(void); +void finish_client_errs(void); extern const char *client_errors[]; /* Error messages */ #ifdef __cplusplus } @@ -35,6 +36,9 @@ extern const char *client_errors[]; /* Error messages */ #endif #define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ +/* Do not add error numbers before CR_ERROR_FIRST. */ +/* If necessary to add lower numbers, change CR_ERROR_FIRST accordingly. */ +#define CR_ERROR_FIRST 2000 /*Copy first error nr.*/ #define CR_UNKNOWN_ERROR 2000 #define CR_SOCKET_CREATE_ERROR 2001 #define CR_CONNECTION_ERROR 2002 @@ -90,3 +94,6 @@ extern const char *client_errors[]; /* Error messages */ #define CR_SECURE_AUTH 2049 #define CR_FETCH_CANCELED 2050 #define CR_NO_DATA 2051 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2051 +/* Add error numbers before CR_ERROR_LAST and change it accordingly. */ + diff --git a/include/my_base.h b/include/my_base.h index 4d043cf6b5b..dd1c56eb8bb 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -273,6 +273,9 @@ enum ha_base_keytype { /* Errorcodes given by functions */ /* opt_sum_query() assumes these codes are > 1 */ +/* Do not add error numbers before HA_ERR_FIRST. */ +/* If necessary to add lower numbers, change HA_ERR_FIRST accordingly. */ +#define HA_ERR_FIRST 120 /*Copy first error nr.*/ #define HA_ERR_KEY_NOT_FOUND 120 /* Didn't find key on read or update */ #define HA_ERR_FOUND_DUPP_KEY 121 /* Dupplicate key on write */ #define HA_ERR_RECORD_CHANGED 123 /* Uppdate with is recoverable */ @@ -308,6 +311,9 @@ enum ha_base_keytype { #define HA_ERR_NO_SUCH_TABLE 155 /* The table does not exist in engine */ #define HA_ERR_TABLE_EXIST 156 /* The table existed in storage engine */ #define HA_ERR_NO_CONNECTION 157 /* Could not connect to storage engine */ +#define HA_ERR_LAST 157 /*Copy last error nr.*/ +/* Add error numbers before HA_ERR_LAST and change it accordingly. */ +#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) /* Other constants */ @@ -370,6 +376,15 @@ enum data_file_type { /* For key ranges */ +#define NO_MIN_RANGE 1 +#define NO_MAX_RANGE 2 +#define NEAR_MIN 4 +#define NEAR_MAX 8 +#define UNIQUE_RANGE 16 +#define EQ_RANGE 32 +#define NULL_RANGE 64 +#define GEOM_FLAG 128 + typedef struct st_key_range { const byte *key; @@ -377,6 +392,14 @@ typedef struct st_key_range enum ha_rkey_function flag; } key_range; +typedef struct st_key_multi_range +{ + key_range start_key; + key_range end_key; + char *ptr; /* Free to use by caller (ptr to row etc) */ + uint range_flag; /* key range flags see above */ +} KEY_MULTI_RANGE; + /* For number of records */ #ifdef BIG_TABLES diff --git a/include/my_sys.h b/include/my_sys.h index e630c9bdbba..cbcd6f0f833 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -43,8 +43,6 @@ extern int NEAR my_errno; /* Last error in mysys */ #define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;} #define MY_INIT(name); { my_progname= name; my_init(); } -#define MAXMAPS (4) /* Number of error message maps */ -#define ERRMOD (1000) /* Max number of errors in a map */ #define ERRMSGSIZE (SC_MAXWIDTH) /* Max length of a error message */ #define NRERRBUFFS (2) /* Buffers for parameters */ #define MY_FILE_ERROR ((uint) ~0) @@ -213,7 +211,6 @@ void __CDECL hfree(void *ptr); #else extern int errno; /* declare errno */ #endif -extern const char ** NEAR my_errmsg[]; extern char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; extern char *home_dir; /* Home directory for user */ extern char *my_progname; /* program-name (printed in errors) */ @@ -610,6 +607,8 @@ extern int my_error _VARARGS((int nr,myf MyFlags, ...)); extern int my_printf_error _VARARGS((uint my_err, const char *format, myf MyFlags, ...) __attribute__ ((format (printf, 2, 4)))); +extern int my_error_register(const char **errmsgs, int first, int last); +extern const char **my_error_unregister(int first, int last); extern int my_message(uint my_err, const char *str,myf MyFlags); extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags); extern int my_message_curses(uint my_err, const char *str,myf MyFlags); diff --git a/include/mysys_err.h b/include/mysys_err.h index 230be5f4720..1fd7c2eddc6 100644 --- a/include/mysys_err.h +++ b/include/mysys_err.h @@ -20,13 +20,15 @@ extern "C" { #endif -#define GLOB 0 /* Error maps */ -#define GLOBERRS 28 /* Max number of error messages in map's */ -#define EE(X) globerrs[ X ] /* Defines to add error to right map */ +#define GLOBERRS (EE_ERROR_LAST - EE_ERROR_FIRST + 1) /* Nr of global errors */ +#define EE(X) (globerrs[(X) - EE_ERROR_FIRST]) extern const char * NEAR globerrs[]; /* my_error_messages is here */ /* Error message numbers in global map */ +/* Do not add error numbers before EE_ERROR_FIRST. */ +/* If necessary to add lower numbers, change EE_ERROR_FIRST accordingly. */ +#define EE_ERROR_FIRST 0 /*Copy first error nr.*/ #define EE_FILENOTFOUND 0 #define EE_CANTCREATEFILE 1 #define EE_READ 2 @@ -54,6 +56,8 @@ extern const char * NEAR globerrs[]; /* my_error_messages is here */ #define EE_CANT_SYMLINK 25 #define EE_REALPATH 26 #define EE_SYNC 27 +#define EE_ERROR_LAST 27 /*Copy last error nr.*/ +/* Add error numbers before EE_ERROR_LAST and change it accordingly. */ /* exit codes for all MySQL programs */ diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index 9da422e927f..4fb930da50f 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -2735,9 +2735,8 @@ loop: offsets, ULINT_UNDEFINED, &heap); offsets2 = rec_get_offsets(right_rec, index, offsets2, ULINT_UNDEFINED, &heap); - if (cmp_rec_rec(rec, right_rec, offsets, offsets2, - dict_index_get_n_fields(index), - index) >= 0) { + if (cmp_rec_rec(rec, right_rec, offsets, offsets2, index) + >= 0) { btr_validate_report2(index, level, page, right_page); diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index a3083e0e545..4c2a501a08a 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -2848,7 +2848,7 @@ btr_estimate_number_of_different_key_vals( cmp_rec_rec_with_match(rec, next_rec, offsets1, offsets2, - index, n_cols, &matched_fields, + index, &matched_fields, &matched_bytes); for (j = matched_fields + 1; j <= n_cols; j++) { diff --git a/innobase/btr/btr0pcur.c b/innobase/btr/btr0pcur.c index ad7613e0655..ceaa4f41a18 100644 --- a/innobase/btr/btr0pcur.c +++ b/innobase/btr/btr0pcur.c @@ -268,9 +268,7 @@ btr_pcur_restore_position( cursor->old_n_fields, &heap); ut_ad(cmp_rec_rec(cursor->old_rec, - rec, offsets1, offsets2, - cursor->old_n_fields, - index) == 0); + rec, offsets1, offsets2, index) == 0); mem_heap_free(heap); #endif /* UNIV_DEBUG */ return(TRUE); diff --git a/innobase/include/rem0cmp.h b/innobase/include/rem0cmp.h index 77a5a42c2d5..1b1ee26b809 100644 --- a/innobase/include/rem0cmp.h +++ b/innobase/include/rem0cmp.h @@ -137,10 +137,6 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -163,10 +159,6 @@ cmp_rec_rec( rec_t* rec2, /* in: physical record */ const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ dict_index_t* index); /* in: data dictionary index */ diff --git a/innobase/include/rem0cmp.ic b/innobase/include/rem0cmp.ic index d4c30f25f03..b86534e0a6a 100644 --- a/innobase/include/rem0cmp.ic +++ b/innobase/include/rem0cmp.ic @@ -59,12 +59,11 @@ cmp_rec_rec( rec_t* rec2, /* in: physical record */ const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ - ulint n, /* in: number of fields to compare */ dict_index_t* index) /* in: data dictionary index */ { ulint match_f = 0; ulint match_b = 0; - return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, n, + return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, &match_f, &match_b)); } diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index 6c3dabf04a2..b9a79c259a4 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -373,6 +373,26 @@ rec_set_n_fields_old( REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); } +/********************************************************** +The following function retrieves the status bits of a new-style record. */ +UNIV_INLINE +ulint +rec_get_status( +/*===========*/ + /* out: status bits */ + rec_t* rec) /* in: physical record */ +{ + ulint ret; + + ut_ad(rec); + + ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, + REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); + ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); + + return(ret); +} + /********************************************************** The following function is used to get the number of fields in a record. */ @@ -484,26 +504,6 @@ rec_set_info_bits( REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); } -/********************************************************** -The following function retrieves the status bits of a new-style record. */ -UNIV_INLINE -ulint -rec_get_status( -/*===========*/ - /* out: status bits */ - rec_t* rec) /* in: physical record */ -{ - ulint ret; - - ut_ad(rec); - - ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, - REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); - ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); - - return(ret); -} - /********************************************************** The following function is used to set the status bits of a new-style record. */ UNIV_INLINE @@ -756,6 +756,25 @@ rec_offs_set_n_alloc( offsets[0] = n_alloc; } +/************************************************************** +The following function returns the number of fields in a record. */ +UNIV_INLINE +ulint +rec_offs_n_fields( +/*===============*/ + /* out: number of fields */ + const ulint* offsets)/* in: array returned by rec_get_offsets() */ +{ + ulint n_fields; + ut_ad(offsets); + n_fields = offsets[1]; + ut_ad(n_fields > 0); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); + return(n_fields); +} + /**************************************************************** Validates offsets returned by rec_get_offsets(). */ UNIV_INLINE @@ -927,7 +946,8 @@ rec_offs_nth_size( { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); - return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK); + return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n]) + & REC_OFFS_MASK); } /********************************************************** @@ -1202,25 +1222,6 @@ rec_get_data_size_old( return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec))); } -/************************************************************** -The following function returns the number of fields in a record. */ -UNIV_INLINE -ulint -rec_offs_n_fields( -/*===============*/ - /* out: number of fields */ - const ulint* offsets)/* in: array returned by rec_get_offsets() */ -{ - ulint n_fields; - ut_ad(offsets); - n_fields = offsets[1]; - ut_ad(n_fields > 0); - ut_ad(n_fields <= REC_MAX_N_FIELDS); - ut_ad(n_fields + REC_OFFS_HEADER_SIZE - <= rec_offs_get_n_alloc(offsets)); - return(n_fields); -} - /************************************************************** The following function sets the number of fields in offsets. */ UNIV_INLINE diff --git a/innobase/page/page0page.c b/innobase/page/page0page.c index 67c7bd936d1..20bbfba7a50 100644 --- a/innobase/page/page0page.c +++ b/innobase/page/page0page.c @@ -1749,8 +1749,7 @@ page_validate( /* Check that the records are in the ascending order */ if ((count >= 2) && (!page_cur_is_after_last(&cur))) { if (!(1 == cmp_rec_rec(rec, old_rec, - offsets, old_offsets, - ULINT_UNDEFINED, index))) { + offsets, old_offsets, index))) { fprintf(stderr, "InnoDB: Records in wrong order on page %lu", (ulong) buf_frame_get_page_no(page)); diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 974fc7a24d0..6473d356ba8 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -728,10 +728,6 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -765,14 +761,8 @@ cmp_rec_rec_with_match( ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); comp = rec_offs_comp(offsets1); - if (n == ULINT_UNDEFINED) { - rec1_n_fields = rec_offs_n_fields(offsets1); - rec2_n_fields = rec_offs_n_fields(offsets2); - } else { - ut_ad(n <= rec_offs_n_fields(offsets1)); - ut_ad(n <= rec_offs_n_fields(offsets2)); - rec1_n_fields = rec2_n_fields = n; - } + rec1_n_fields = rec_offs_n_fields(offsets1); + rec2_n_fields = rec_offs_n_fields(offsets2); cur_field = *matched_fields; cur_bytes = *matched_bytes; diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index 74876ad9402..90cbffe7a9e 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -333,14 +333,14 @@ rec_get_offsets_func( n = n_fields; } - size = (n + (1 + REC_OFFS_HEADER_SIZE)) * sizeof(ulint); + size = n + (1 + REC_OFFS_HEADER_SIZE); if (!offsets || rec_offs_get_n_alloc(offsets) < size) { if (!*heap) { - *heap = mem_heap_create_func(size, + *heap = mem_heap_create_func(size * sizeof(ulint), NULL, MEM_HEAP_DYNAMIC, file, line); } - offsets = mem_heap_alloc(*heap, size); + offsets = mem_heap_alloc(*heap, size * sizeof(ulint)); rec_offs_set_n_alloc(offsets, size); } diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 710bf4ccd8d..73726e772e5 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -199,7 +199,33 @@ const char *client_errors[]= #endif +/* + Register client error messages for use with my_error(). + + SYNOPSIS + init_client_errs() + + RETURN + void +*/ + void init_client_errs(void) { - my_errmsg[CLIENT_ERRMAP] = &client_errors[0]; + (void) my_error_register(client_errors, CR_ERROR_FIRST, CR_ERROR_LAST); +} + + +/* + Unregister client error messages. + + SYNOPSIS + finish_client_errs() + + RETURN + void +*/ + +void finish_client_errs(void) +{ + (void) my_error_unregister(CR_ERROR_FIRST, CR_ERROR_LAST); } diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 4476a42f8ac..8c20b566957 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -185,6 +185,7 @@ void STDCALL mysql_server_end() } else mysql_thread_end(); + finish_client_errs(); mysql_client_init= org_my_init_done= 0; } diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index b964cb35dd8..d79d9040ee7 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -45,6 +45,12 @@ int mi_delete(MI_INFO *info,const byte *record) /* Test if record is in datafile */ + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); + DBUG_EXECUTE_IF("my_error_test_undefined_error", + mi_print_error(info, INT_MAX); + DBUG_RETURN(my_errno= INT_MAX);); if (!(info->update & HA_STATE_AKTIV)) { DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No database read */ @@ -109,13 +115,19 @@ err: mi_sizestore(lastpos,info->lastpos); myisam_log_command(MI_LOG_DELETE,info,(byte*) lastpos, sizeof(lastpos),0); if (save_errno != HA_ERR_RECORD_CHANGED) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* mark table crashed */ + } VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE)); info->update|=HA_STATE_WRITTEN; /* Buffer changed */ allow_break(); /* Allow SIGHUP & SIGINT */ my_errno=save_errno; if (save_errno == HA_ERR_KEY_NOT_FOUND) + { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; + } DBUG_RETURN(my_errno); } /* mi_delete */ @@ -142,6 +154,7 @@ static int _mi_ck_real_delete(register MI_INFO *info, MI_KEYDEF *keyinfo, if ((old_root=*root) == HA_OFFSET_ERROR) { + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(my_errno=HA_ERR_CRASHED); } if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+ @@ -253,7 +266,9 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, my_off_t root; uchar *kpos=keypos; - tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey); + if (!(tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey)) + && (my_errno == HA_ERR_CRASHED)) + mi_print_error(info, HA_ERR_CRASHED); root=_mi_dpos(info,nod_flag,kpos); if (subkeys == -1) { @@ -302,6 +317,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, if (!nod_flag) { DBUG_PRINT("error",("Didn't find key")); + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; /* This should newer happend */ goto err; } @@ -317,6 +333,8 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, &next_block); if (tmp == 0) { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_PRINT("exit",("Return: %d",0)); DBUG_RETURN(0); } @@ -473,6 +491,8 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key, (info->quick_mode ? MI_MIN_KEYBLOCK_LENGTH : (uint) keyinfo->underflow_block_length)); err: + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); } /* del */ @@ -562,7 +582,11 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, s_length=remove_key(keyinfo,key_reflength,keypos,anc_key, anc_buff+anc_length,(my_off_t *) 0); if (!s_length) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); goto err; + } anc_length-=s_length; mi_putint(anc_buff,anc_length,key_reflength); @@ -671,7 +695,11 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, s_length=remove_key(keyinfo,key_reflength,keypos,anc_key, anc_buff+anc_length,(my_off_t *) 0); if (!s_length) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); goto err; + } anc_length-=s_length; mi_putint(anc_buff,anc_length,key_reflength); @@ -732,6 +760,8 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, goto err; DBUG_RETURN(anc_length <= (uint) keyinfo->block_length/2); err: + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); } /* underflow */ diff --git a/myisam/mi_extra.c b/myisam/mi_extra.c index 4b011ca424f..999c4ba8f3d 100644 --- a/myisam/mi_extra.c +++ b/myisam/mi_extra.c @@ -186,7 +186,10 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) if (info->opt_flag & WRITE_CACHE_USED) { if ((error=flush_io_cache(&info->rec_cache))) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ + } } break; case HA_EXTRA_NO_READCHECK: @@ -285,6 +288,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) { error=my_errno; share->changed=1; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ } if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) @@ -339,6 +343,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) if (error) { share->changed=1; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ } } diff --git a/myisam/mi_info.c b/myisam/mi_info.c index cf63ef63618..bdece9c2ee3 100644 --- a/myisam/mi_info.c +++ b/myisam/mi_info.c @@ -105,3 +105,36 @@ int mi_status(MI_INFO *info, register MI_ISAMINFO *x, uint flag) } DBUG_RETURN(0); } + + +/* + Write a message to the error log. + + SYNOPSIS + mi_report_error() + file_name Name of table file (e.g. index_file_name). + errcode Error number. + + DESCRIPTION + This function supplies my_error() with a table name. Most error + messages need one. Since string arguments in error messages are limited + to 64 characters by convention, we ensure that in case of truncation, + that the end of the index file path is in the message. This contains + the most valuable information (the table name and the database name). + + RETURN + void +*/ + +void mi_report_error(int errcode, const char *file_name) +{ + size_t lgt; + DBUG_ENTER("mi_report_error"); + DBUG_PRINT("enter",("errcode %d, table '%s'", errcode, file_name)); + + if ((lgt= strlen(file_name)) > 64) + file_name+= lgt - 64; + my_error(errcode, MYF(ME_NOREFRESH), file_name); + DBUG_VOID_RETURN; +} + diff --git a/myisam/mi_key.c b/myisam/mi_key.c index caca63452b0..eaaee617f32 100644 --- a/myisam/mi_key.c +++ b/myisam/mi_key.c @@ -477,6 +477,7 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, byte *buf) { /* Read only key */ if (_mi_put_key_in_record(info,(uint) info->lastinx,buf)) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; return -1; } diff --git a/myisam/mi_keycache.c b/myisam/mi_keycache.c index 99a2fd6db15..33d0ac4f6bc 100644 --- a/myisam/mi_keycache.c +++ b/myisam/mi_keycache.c @@ -79,6 +79,7 @@ int mi_assign_to_key_cache(MI_INFO *info, if (flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE)) { error= my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Mark that table must be checked */ } diff --git a/myisam/mi_locking.c b/myisam/mi_locking.c index 66950f62321..91e9f09b9fb 100644 --- a/myisam/mi_locking.c +++ b/myisam/mi_locking.c @@ -66,6 +66,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) share->kfile,FLUSH_KEEP)) { error=my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Mark that table must be checked */ } if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) @@ -73,6 +74,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) if (end_io_cache(&info->rec_cache)) { error=my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); } } @@ -98,7 +100,10 @@ int mi_lock_database(MI_INFO *info, int lock_type) else share->not_flushed=1; if (error) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } } if (info->lock_type != F_EXTRA_LCK) { @@ -285,6 +290,7 @@ void mi_update_status(void* param) { if (end_io_cache(&info->rec_cache)) { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); } info->opt_flag&= ~WRITE_CACHE_USED; diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 58db2e47c1f..040bc1503aa 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -106,6 +106,12 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share_buff.state.key_del=key_del; share_buff.key_cache= multi_key_cache_search(name_buff, strlen(name_buff)); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open", + if (strstr(name, "/t1")) + { + my_errno= HA_ERR_CRASHED; + goto err; + }); if ((kfile=my_open(name_buff,(open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0) { if ((errno != EROFS && errno != EACCES) || @@ -601,6 +607,10 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) err: save_errno=my_errno ? my_errno : HA_ERR_END_OF_FILE; + if ((save_errno == HA_ERR_CRASHED) || + (save_errno == HA_ERR_CRASHED_ON_USAGE) || + (save_errno == HA_ERR_CRASHED_ON_REPAIR)) + mi_report_error(save_errno, name); switch (errpos) { case 6: my_free((gptr) m_info,MYF(0)); @@ -1223,7 +1233,10 @@ int mi_enable_indexes(MI_INFO *info) if (share->state.state.data_file_length || (share->state.state.key_file_length != share->base.keystart)) + { + mi_print_error(info, HA_ERR_CRASHED); error= HA_ERR_CRASHED; + } else share->state.key_map= ((ulonglong) 1L << share->base.keys) - 1; return error; diff --git a/myisam/mi_page.c b/myisam/mi_page.c index 16713c87e10..dc2bc75f1a0 100644 --- a/myisam/mi_page.c +++ b/myisam/mi_page.c @@ -40,6 +40,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo, { DBUG_PRINT("error",("Got errno: %d from key_cache_read",my_errno)); info->last_keypage=HA_OFFSET_ERROR; + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -51,6 +52,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo, (ulong) page, page_size)); DBUG_DUMP("page", (char*) tmp, keyinfo->block_length); info->last_keypage = HA_OFFSET_ERROR; + mi_print_error(info, HA_ERR_CRASHED); my_errno = HA_ERR_CRASHED; tmp = 0; } diff --git a/myisam/mi_range.c b/myisam/mi_range.c index 789607c9889..0d8f8763b92 100644 --- a/myisam/mi_range.c +++ b/myisam/mi_range.c @@ -233,7 +233,11 @@ static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, u while (page < end) { if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff)) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); return 0; /* Error */ + } max_key++; if (page == keypos) keynr=max_key; diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index 12db00337ee..d564c672f19 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -78,6 +78,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, case HA_KEY_ALG_RTREE: if (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; goto err; } diff --git a/myisam/mi_search.c b/myisam/mi_search.c index 2259dd17fcd..f252719d29c 100644 --- a/myisam/mi_search.c +++ b/myisam/mi_search.c @@ -161,6 +161,8 @@ int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, DBUG_RETURN(0); err: DBUG_PRINT("exit",("Error: %d",my_errno)); + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); info->lastpos= HA_OFFSET_ERROR; info->page_changed=1; DBUG_RETURN (-1); @@ -234,6 +236,7 @@ int _mi_seq_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff); if (length == 0 || page > end) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p", length, page, end)); @@ -380,6 +383,7 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, if (page > end) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p", length, page, end)); @@ -969,6 +973,7 @@ uchar *_mi_get_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key); if (*return_key_length == 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -1006,6 +1011,7 @@ static my_bool _mi_get_prev_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key); if (*return_key_length == 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(1); } @@ -1046,6 +1052,7 @@ uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, if (*return_key_length == 0) { DBUG_PRINT("error",("Couldn't find last key: page: %p", page)); + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -1178,7 +1185,11 @@ int _mi_search_next(register MI_INFO *info, register MI_KEYDEF *keyinfo, memcpy(lastkey,key,key_length); if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag, &info->int_keypos,lastkey))) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } } else /* Previous key */ { @@ -1236,8 +1247,10 @@ int _mi_search_first(register MI_INFO *info, register MI_KEYDEF *keyinfo, page=info->buff+2+nod_flag; } while ((pos=_mi_kpos(nod_flag,page)) != HA_OFFSET_ERROR); - info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page, - info->lastkey); + if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page, + info->lastkey)) && + (my_errno == HA_ERR_CRASHED)) + mi_print_error(info, HA_ERR_CRASHED); info->int_keypos=page; info->int_maxpos=info->buff+mi_getint(info->buff)-1; info->int_nod_flag=nod_flag; info->int_keytree_version=keyinfo->version; diff --git a/myisam/mi_update.c b/myisam/mi_update.c index f62be133ed9..2936e29a01c 100644 --- a/myisam/mi_update.c +++ b/myisam/mi_update.c @@ -34,6 +34,9 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec) LINT_INIT(changed); LINT_INIT(old_checksum); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); if (!(info->update & HA_STATE_AKTIV)) { DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); @@ -205,7 +208,10 @@ err: } while (i-- != 0); } else + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_ROW_CHANGED | key_changed); @@ -214,6 +220,9 @@ err: VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE)); allow_break(); /* Allow SIGHUP & SIGINT */ if (save_errno == HA_ERR_KEY_NOT_FOUND) + { + mi_print_error(info, HA_ERR_CRASHED); save_errno=HA_ERR_CRASHED; + } DBUG_RETURN(my_errno=save_errno); } /* mi_update */ diff --git a/myisam/mi_write.c b/myisam/mi_write.c index c2f24ae1ae3..8ff653bdd2d 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -52,6 +52,9 @@ int mi_write(MI_INFO *info, byte *record) DBUG_ENTER("mi_write"); DBUG_PRINT("enter",("isam: %d data: %d",info->s->kfile,info->dfile)); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); if (share->options & HA_OPTION_READ_ONLY_DATA) { DBUG_RETURN(my_errno=EACCES); @@ -202,7 +205,10 @@ err: } } else + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } info->update= (HA_STATE_CHANGED | HA_STATE_WRITTEN | HA_STATE_ROW_CHANGED); my_errno=save_errno; err2: @@ -346,7 +352,11 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, if (tmp_key_length) dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length); else + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); dupp_key_pos= HA_OFFSET_ERROR; + } if (keyinfo->flag & HA_FULLTEXT) { uint off; @@ -455,6 +465,7 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, { if (t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(-1); } @@ -464,6 +475,7 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, { if (-t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(-1); } @@ -558,7 +570,11 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo, key_pos=_mi_find_half_pos(nod_flag,keyinfo,buff,key_buff, &key_length, &after_key); if (!key_pos) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } length=(uint) (key_pos-buff); a_length=mi_getint(buff); mi_putint(buff,length,nod_flag); @@ -578,7 +594,11 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo, /* Store new page */ if (!(*keyinfo->get_key)(keyinfo,nod_flag,&key_pos,key_buff)) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(uchar *) 0, (uchar*) 0, (uchar*) 0, key_buff, &s_temp); diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index 12ce112dbe0..c0f56a7b720 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -356,6 +356,8 @@ typedef struct st_mi_sort_param #define mi_mark_crashed_on_repair(x) { (x)->s->state.changed|=STATE_CRASHED|STATE_CRASHED_ON_REPAIR ; (x)->update|= HA_STATE_CHANGED; } #define mi_is_crashed(x) ((x)->s->state.changed & STATE_CRASHED) #define mi_is_crashed_on_repair(x) ((x)->s->state.changed & STATE_CRASHED_ON_REPAIR) +#define mi_print_error(INFO, ERRNO) \ + mi_report_error((ERRNO), (INFO)->s->index_file_name) /* Functions to store length of space packed keys, VARCHAR or BLOB keys */ @@ -667,6 +669,7 @@ extern void _myisam_log_command(enum myisam_log_commands command, extern void _myisam_log_record(enum myisam_log_commands command,MI_INFO *info, const byte *record,my_off_t filepos, int result); +extern void mi_report_error(int errcode, const char *file_name); extern my_bool _mi_memmap_file(MI_INFO *info); extern void _mi_unmap_file(MI_INFO *info); extern uint save_pack_length(byte *block_buff,ulong length); diff --git a/mysql-test/include/system_db_struct.inc b/mysql-test/include/system_db_struct.inc index 5a7aa26c65a..e24c8f3311d 100644 --- a/mysql-test/include/system_db_struct.inc +++ b/mysql-test/include/system_db_struct.inc @@ -10,3 +10,4 @@ show create table user; show create table func; show create table tables_priv; show create table columns_priv; +show create table procs_priv; diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 702b725764b..fef813371c8 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -9,6 +9,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -31,6 +32,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -57,6 +59,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index b69ba9702a6..629a3221330 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -10,8 +10,8 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' grant delete on mysqltest.* to mysqltest_1@localhost; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0 +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections +localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 0 0 show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -62,7 +62,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost; show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION revoke all privileges on mysqltest.* from mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; flush privileges; @@ -347,13 +347,16 @@ drop table t1; SHOW PRIVILEGES; Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index edfd1a07680..48cbac10d46 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -48,9 +48,9 @@ GRANT SELECT, INSERT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' use mysqltest; insert into t1 values (1, 'I can''t change it!'); update t1 set data='I can change it!' where id = 1; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1' insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!'; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1' select * from t1; id data 1 I can't change it! @@ -202,7 +202,7 @@ drop user '%@a'@'a'; create user mysqltest_2@localhost; grant usage on *.* to mysqltest_2@localhost with grant option; select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysql' +ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 'user' create user mysqltest_A@'%'; rename user mysqltest_A@'%' to mysqltest_B@'%'; drop user mysqltest_B@'%'; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 0bd3ba14636..037588afda8 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -58,6 +58,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -172,7 +173,7 @@ CHARACTER_SET_NAME DEFAULT_COLLATE_NAME DESCRIPTION MAXLEN latin1 latin1_swedish_ci ISO 8859-1 West European 1 select * from information_schema.COLLATIONS where COLLATION_NAME like 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -192,7 +193,7 @@ latin1_general_ci latin1 48 0 latin1_general_cs latin1 49 0 latin1_spanish_ci latin1 94 0 SHOW COLLATION * LIKE 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -222,7 +223,7 @@ latin1_general_ci latin1_general_cs latin1_spanish_ci SHOW COLLATION * WHERE COLLATION_NAME like 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -346,8 +347,11 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test ALTER YES 'mysqltest_1'@'localhost' NULL test CREATE TEMPORARY TABLES YES 'mysqltest_1'@'localhost' NULL test LOCK TABLES YES +'mysqltest_1'@'localhost' NULL test EXECUTE YES 'mysqltest_1'@'localhost' NULL test CREATE VIEW YES 'mysqltest_1'@'localhost' NULL test SHOW VIEW YES +'mysqltest_1'@'localhost' NULL test CREATE ROUTINE YES +'mysqltest_1'@'localhost' NULL test ALTER ROUTINE YES select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%'; GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test t1 SELECT NO @@ -467,20 +471,20 @@ drop table t1; SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets; Table Create Table character_sets CREATE TEMPORARY TABLE `character_sets` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' -) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=2267 +) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=1818 set names latin2; SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets; Table Create Table character_sets CREATE TEMPORARY TABLE `character_sets` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' -) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=2267 +) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=1818 set names latin1; create table t1 select * from information_schema.CHARACTER_SETS where CHARACTER_SET_NAME like "latin1"; @@ -491,8 +495,8 @@ alter table t1 default character set utf8; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=utf8 @@ -600,6 +604,8 @@ Process_priv select,insert,update,references Show_db_priv select,insert,update,references Lock_tables_priv select,insert,update,references Show_view_priv select,insert,update,references +Create_routine_priv select,insert,update,references +Alter_routine_priv select,insert,update,references max_questions select,insert,update,references max_connections select,insert,update,references use test; @@ -641,3 +647,17 @@ drop view t3; drop table t4; select * from information_schema.table_names; ERROR 42S02: Unknown table 'table_names' in information_schema +select column_type from information_schema.columns +where table_schema="information_schema" and table_name="COLUMNS" and +(column_name="character_set_name" or column_name="collation_name"); +column_type +varchar(64) +varchar(64) +select TABLE_ROWS from information_schema.tables where +table_schema="information_schema" and table_name="COLUMNS"; +TABLE_ROWS +NULL +select table_type from information_schema.tables +where table_schema="mysql" and table_name="user"; +table_type +BASE TABLE diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index a9d90813660..618816cd623 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -178,9 +178,9 @@ t3 CREATE TABLE `t3` ( ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t1`,`t2`) create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); select * from t4; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: All tables in the MERGE table are not identically defined alter table t4 add column c int; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: All tables in the MERGE table are not identically defined create database mysqltest; create table mysqltest.t6 (a int not null primary key auto_increment, message char(20)); create table t5 (a int not null, b char(20), key(a)) engine=MERGE UNION=(test.t1,mysqltest.t6); diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index dbca5c39a6c..0347d3a52f5 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -31,7 +31,7 @@ create table t1 engine=myisam SELECT 1,"table 1"; flush tables; repair table t1; Table Op Msg_type Msg_text -test.t1 repair error Can't open file: 't1.MYI' (errno: 130) +test.t1 repair error Incorrect file format 't1' repair table t1 use_frm; Table Op Msg_type Msg_text test.t1 repair warning Number of rows changed from 0 to 1 diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index ec9bd33d301..c1cabce7e92 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -382,19 +382,19 @@ show create database mysqltest; Database Create Database mysqltest CREATE DATABASE `mysqltest` /*!40100 DEFAULT CHARACTER SET latin1 */ drop table t1; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: drop command denied to user 'mysqltest_1'@'localhost' for table 't1' drop database mysqltest; ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' select * from mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' +ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 't1' show create database mysqltest; ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' drop table mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' +ERROR 42000: drop command denied to user 'mysqltest_2'@'localhost' for table 't1' drop database mysqltest; ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' select * from mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest' +ERROR 42000: select command denied to user 'mysqltest_3'@'localhost' for table 't1' show create database mysqltest; ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest' drop table mysqltest.t1; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 25582796812..365368873fa 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -23,12 +23,16 @@ root@localhost 1 select db(); db() db1_secret +grant execute on db1_secret.stamp to user1@'%'; +grant execute on db1_secret.db to user1@'%'; +grant execute on db1_secret.stamp to ''@'%'; +grant execute on db1_secret.db to ''@'%'; call db1_secret.stamp(2); select db1_secret.db(); db1_secret.db() db1_secret select * from db1_secret.t1; -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' +ERROR 42000: select command denied to user 'user1'@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' drop procedure db1_secret.dummy; @@ -38,7 +42,7 @@ select db1_secret.db(); db1_secret.db() db1_secret select * from db1_secret.t1; -ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret' +ERROR 42000: select command denied to user ''@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret' drop procedure db1_secret.dummy; @@ -82,15 +86,16 @@ insert into t2 values (0); grant usage on db2.* to user1@localhost; grant select on db2.* to user1@localhost; grant usage on db2.* to user2@localhost; -grant select,insert,update,delete on db2.* to user2@localhost; +grant select,insert,update,delete,create routine on db2.* to user2@localhost; +grant create routine on db2.* to user1@localhost; flush privileges; use db2; create procedure p () insert into t2 values (1); call p(); -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2' +ERROR 42000: insert command denied to user 'user1'@'localhost' for table 't2' use db2; call p(); -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2' +ERROR 42000: execute command denied to user 'user2'@'localhost' for routine 'db2.p' select * from t2; s1 0 @@ -100,6 +105,8 @@ select * from t2; s1 0 2 +grant usage on db2.q to user2@localhost with grant option; +grant execute on db2.q to user1@localhost; use db2; call q(); select * from t2; @@ -110,9 +117,9 @@ s1 alter procedure p modifies sql data; drop procedure p; alter procedure q modifies sql data; -ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q' +ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q' drop procedure q; -ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q' +ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q' use db2; alter procedure q modifies sql data; drop procedure q; @@ -126,3 +133,64 @@ drop database db2; select type,db,name from mysql.proc; type db name delete from mysql.user where user='user1' or user='user2'; +delete from mysql.procs_priv where user='user1' or user='user2'; +grant usage on *.* to usera@localhost; +grant usage on *.* to userb@localhost; +grant usage on *.* to userc@localhost; +create database sptest; +create table t1 ( u varchar(64), i int ); +create procedure sptest.p1(i int) insert into test.t1 values (user(), i); +grant insert on t1 to usera@localhost; +grant execute on sptest.p1 to usera@localhost; +show grants for usera@localhost; +Grants for usera@localhost +GRANT USAGE ON *.* TO 'usera'@'localhost' +GRANT INSERT ON `test`.`t1` TO 'usera'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'usera'@'localhost' +grant execute on sptest.p1 to userc@localhost with grant option; +show grants for userc@localhost; +Grants for userc@localhost +GRANT USAGE ON *.* TO 'userc'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION +call sptest.p1(1); +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: grant command denied to user 'usera'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'usera'@'localhost' for routine 'sptest.p1' +call sptest.p1(2); +ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1' +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1' +call sptest.p1(3); +grant execute on sptest.p1 to userb@localhost; +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userc'@'localhost' for routine 'sptest.p1' +call sptest.p1(4); +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: grant command denied to user 'userb'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1' +select * from t1; +u i +usera@localhost 1 +userc@localhost 3 +userb@localhost 4 +grant all privileges on sptest.p1 to userc@localhost; +show grants for userc@localhost; +Grants for userc@localhost +GRANT USAGE ON *.* TO 'userc'@'localhost' +GRANT EXECUTE, ALTER ROUTINE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION +show grants for userb@localhost; +Grants for userb@localhost +GRANT USAGE ON *.* TO 'userb'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'userb'@'localhost' +revoke all privileges on sptest.p1 from userb@localhost; +show grants for userb@localhost; +Grants for userb@localhost +GRANT USAGE ON *.* TO 'userb'@'localhost' +use test; +drop database sptest; +delete from mysql.user where user='usera' or user='userb' or user='userc'; +delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 0d7f9f7d50c..269c4104a05 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1654,13 +1654,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par Database Table In_use Name_locked Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) @@ -1704,13 +1707,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par Database Table In_use Name_locked Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 400b508ff50..e9606ec5f88 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -9,6 +9,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -36,6 +37,9 @@ db CREATE TABLE `db` ( `Lock_tables_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Execute_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', PRIMARY KEY (`Host`,`Db`,`User`), KEY `User` (`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges' @@ -89,6 +93,8 @@ user CREATE TABLE `user` ( `Repl_client_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `ssl_type` enum('','ANY','X509','SPECIFIED') collate utf8_bin NOT NULL default '', `ssl_cipher` blob NOT NULL, `x509_issuer` blob NOT NULL, @@ -133,5 +139,18 @@ columns_priv CREATE TABLE `columns_priv` ( `Column_priv` set('Select','Insert','Update','References') collate utf8_bin NOT NULL default '', PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges' +show create table procs_priv; +Table Create Table +procs_priv CREATE TABLE `procs_priv` ( + `Host` char(60) collate utf8_bin NOT NULL default '', + `Db` char(64) collate utf8_bin NOT NULL default '', + `User` char(16) collate utf8_bin NOT NULL default '', + `Routine_name` char(64) collate utf8_bin NOT NULL default '', + `Grantor` char(77) collate utf8_bin NOT NULL default '', + `Timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `Proc_priv` set('Execute','Alter Routine','Grant') collate utf8_bin NOT NULL default '', + PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges' show tables; Tables_in_test diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index 69c42ce2252..ad3fc1c228e 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -64,10 +64,10 @@ connection mrbad; show grants for current_user(); use mysqltest; insert into t1 values (1, 'I can''t change it!'); ---error 1044 +--error 1142 update t1 set data='I can change it!' where id = 1; # This should not be allowed since it too require UPDATE privilege. ---error 1044 +--error 1142 insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!'; select * from t1; @@ -199,7 +199,7 @@ create user mysqltest_2@localhost; grant usage on *.* to mysqltest_2@localhost with grant option; connect (user2,localhost,mysqltest_2,,); connection user2; ---error 1044 +--error 1142 select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password; create user mysqltest_A@'%'; rename user mysqltest_A@'%' to mysqltest_B@'%'; diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index f7d4cdd43b2..66315fe7a29 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -331,3 +331,18 @@ drop table t4; # --error 1109 select * from information_schema.table_names; + +# +# Bug#2719 information_schema: errors in "columns" +# +select column_type from information_schema.columns +where table_schema="information_schema" and table_name="COLUMNS" and +(column_name="character_set_name" or column_name="collation_name"); + +# +# Bug#2718 information_schema: errors in "tables" +# +select TABLE_ROWS from information_schema.tables where +table_schema="information_schema" and table_name="COLUMNS"; +select table_type from information_schema.tables +where table_schema="mysql" and table_name="user"; diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 9d367260049..41d44376525 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -47,9 +47,9 @@ show create table t3; # The following should give errors create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); ---error 1016 +--error 1168 select * from t4; ---error 1016 +--error 1168 alter table t4 add column c int; # diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 8680da9b31a..566f9f625df 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -287,25 +287,25 @@ connect (con1,localhost,mysqltest_1,,mysqltest); connection con1; select * from t1; show create database mysqltest; ---error 1044 +--error 1142 drop table t1; --error 1044 drop database mysqltest; connect (con2,localhost,mysqltest_2,,test); connection con2; ---error 1044 +--error 1142 select * from mysqltest.t1; --error 1044 show create database mysqltest; ---error 1044 +--error 1142 drop table mysqltest.t1; --error 1044 drop database mysqltest; connect (con3,localhost,mysqltest_3,,test); connection con3; ---error 1044 +--error 1142 select * from mysqltest.t1; --error 1044 show create database mysqltest; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index d1119499cf1..aad5f4eaf9e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -40,6 +40,11 @@ call stamp(1); select * from t1; select db(); +grant execute on db1_secret.stamp to user1@'%'; +grant execute on db1_secret.db to user1@'%'; +grant execute on db1_secret.stamp to ''@'%'; +grant execute on db1_secret.db to ''@'%'; + connect (con2user1,localhost,user1,,); connect (con3anon,localhost,anon,,); @@ -54,7 +59,7 @@ call db1_secret.stamp(2); select db1_secret.db(); # ...but not this ---error 1044 +--error 1142 select * from db1_secret.t1; # ...and not this @@ -74,7 +79,7 @@ call db1_secret.stamp(3); select db1_secret.db(); # ...but not this ---error 1044 +--error 1142 select * from db1_secret.t1; # ...and not this @@ -146,7 +151,8 @@ insert into t2 values (0); grant usage on db2.* to user1@localhost; grant select on db2.* to user1@localhost; grant usage on db2.* to user2@localhost; -grant select,insert,update,delete on db2.* to user2@localhost; +grant select,insert,update,delete,create routine on db2.* to user2@localhost; +grant create routine on db2.* to user1@localhost; flush privileges; connection con2user1; @@ -155,7 +161,7 @@ use db2; create procedure p () insert into t2 values (1); # Check that this doesn't work. ---error 1044 +--error 1142 call p(); connect (con4user2,localhost,user2,,); @@ -164,7 +170,7 @@ connection con4user2; use db2; # This should not work, since p is executed with definer's (user1's) rights. ---error 1044 +--error 1370 call p(); select * from t2; @@ -173,6 +179,12 @@ create procedure q () insert into t2 values (2); call q(); select * from t2; +connection con1root; +grant usage on db2.q to user2@localhost with grant option; + +connection con4user2; +grant execute on db2.q to user1@localhost; + connection con2user1; use db2; @@ -206,6 +218,9 @@ drop procedure q; # Clean up #Still connection con1root; +disconnect con2user1; +disconnect con3anon; +disconnect con4user2; use test; select type,db,name from mysql.proc; drop database db1_secret; @@ -214,3 +229,75 @@ drop database db2; select type,db,name from mysql.proc; # Get rid of the users delete from mysql.user where user='user1' or user='user2'; +# And any routine privileges +delete from mysql.procs_priv where user='user1' or user='user2'; + +# +# Test the new security acls +# +grant usage on *.* to usera@localhost; +grant usage on *.* to userb@localhost; +grant usage on *.* to userc@localhost; +create database sptest; +create table t1 ( u varchar(64), i int ); +create procedure sptest.p1(i int) insert into test.t1 values (user(), i); +grant insert on t1 to usera@localhost; +grant execute on sptest.p1 to usera@localhost; +show grants for usera@localhost; +grant execute on sptest.p1 to userc@localhost with grant option; +show grants for userc@localhost; + +connect (con2usera,localhost,usera,,); +connect (con3userb,localhost,userb,,); +connect (con4userc,localhost,userc,,); + +connection con2usera; +call sptest.p1(1); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con3userb; +--error 1370 +call sptest.p1(2); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con4userc; +call sptest.p1(3); +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con3userb; +call sptest.p1(4); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con1root; +select * from t1; + +grant all privileges on sptest.p1 to userc@localhost; +show grants for userc@localhost; +show grants for userb@localhost; + +connection con4userc; +revoke all privileges on sptest.p1 from userb@localhost; + +connection con1root; +show grants for userb@localhost; + +#cleanup +disconnect con4userc; +disconnect con3userb; +disconnect con2usera; +use test; +drop database sptest; +delete from mysql.user where user='usera' or user='userb' or user='userc'; +delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; + diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test index e34dbefbcba..56f291ae69d 100644 --- a/mysql-test/t/system_mysql_db_fix.test +++ b/mysql-test/t/system_mysql_db_fix.test @@ -74,7 +74,7 @@ INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N',' -- disable_query_log -DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type; +DROP TABLE db, host, user, func, tables_priv, columns_priv, procs_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type; -- enable_query_log diff --git a/mysys/errors.c b/mysys/errors.c index 5401c2b3cc6..5f548cad480 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -53,15 +53,13 @@ const char * NEAR globerrs[GLOBERRS]= void init_glob_errs(void) { - my_errmsg[GLOB] = & globerrs[0]; -} /* init_glob_errs */ + /* This is now done statically. */ +} #else void init_glob_errs() { - my_errmsg[GLOB] = & globerrs[0]; - EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; EE(EE_CANTCREATEFILE) = "Can't create/write to file '%s' (Errcode: %d)"; EE(EE_READ) = "Error reading file '%s' (Errcode: %d)"; diff --git a/mysys/my_error.c b/mysys/my_error.c index 175f8cf516b..0c18bbf6e8b 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -31,9 +31,30 @@ my_printf_error(ER_CODE, format, MYF(N), ...) */ -const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0}; char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; +/* + Message texts are registered into a linked list of 'my_err_head' structs. + Each struct contains (1.) an array of pointers to C character strings with + '\0' termination, (2.) the error number for the first message in the array + (array index 0) and (3.) the error number for the last message in the array + (array index (last - first)). + The array may contain gaps with NULL pointers and pointers to empty strings. + Both kinds of gaps will be translated to "Unknown error %d.", if my_error() + is called with a respective error number. + The list of header structs is sorted in increasing order of error numbers. + Negative error numbers are allowed. Overlap of error numbers is not allowed. + Not registered error numbers will be translated to "Unknown error %d.". +*/ +static struct my_err_head +{ + struct my_err_head *meh_next; /* chain link */ + const char **meh_errmsgs; /* error messages array */ + int meh_first; /* error number matching array slot 0 */ + int meh_last; /* error number matching last slot */ +} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST}; +static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; + /* Error message to user @@ -42,30 +63,42 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; nr Errno MyFlags Flags ... variable list - NOTE - The following subset of printf format is supported: - "%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored. - Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but - the length value is ignored. + RETURN + What (*error_handler_hook)() returns: + 0 OK */ int my_error(int nr, myf MyFlags, ...) { const char *format; + struct my_err_head *meh_p; va_list args; char ebuff[ERRMSGSIZE + 20]; DBUG_ENTER("my_error"); DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno)); - if (nr / ERRMOD == GLOB && my_errmsg[GLOB] == 0) - init_glob_errs(); - format= my_errmsg[nr / ERRMOD][nr % ERRMOD]; + /* Search for the error messages array, which could contain the message. */ + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) + if (nr <= meh_p->meh_last) + break; - va_start(args,MyFlags); - (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); - va_end(args); +#ifdef SHARED_LIBRARY + if ((meh_p == &my_errmsgs_globerrs) && ! globerrs[0]) + init_glob_errs(); +#endif + + /* get the error message string. Default, if NULL or empty string (""). */ + if (! (format= (meh_p && (nr >= meh_p->meh_first)) ? + meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format) + (void) my_snprintf (ebuff, sizeof(ebuff), "Unknown error %d", nr); + else + { + va_start(args,MyFlags); + (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); + va_end(args); + } DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); } @@ -108,3 +141,109 @@ int my_message(uint error, const char *str, register myf MyFlags) { return (*error_handler_hook)(error, str, MyFlags); } + + +/* + Register error messages for use with my_error(). + + SYNOPSIS + my_error_register() + errmsgs array of pointers to error messages + first error number of first message in the array + last error number of last message in the array + + DESCRIPTION + The pointer array is expected to contain addresses to NUL-terminated + C character strings. The array contains (last - first + 1) pointers. + NULL pointers and empty strings ("") are allowed. These will be mapped to + "Unknown error" when my_error() is called with a matching error number. + This function registers the error numbers 'first' to 'last'. + No overlapping with previously registered error numbers is allowed. + + RETURN + 0 OK + != 0 Error +*/ + +int my_error_register(const char **errmsgs, int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Allocate a new header structure. */ + if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head), + MYF(MY_WME)))) + return 1; + meh_p->meh_errmsgs= errmsgs; + meh_p->meh_first= first; + meh_p->meh_last= last; + + /* Search for the right position in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if ((*search_meh_pp)->meh_last > first) + break; + } + + /* Error numbers must be unique. No overlapping is allowed. */ + if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) + return 1; + + /* Insert header into the chain. */ + meh_p->meh_next= *search_meh_pp; + *search_meh_pp= meh_p; + return 0; +} + + +/* + Unregister formerly registered error messages. + + SYNOPSIS + my_error_unregister() + first error number of first message + last error number of last message + + DESCRIPTION + This function unregisters the error numbers 'first' to 'last'. + These must have been previously registered by my_error_register(). + 'first' and 'last' must exactly match the registration. + If a matching registration is present, the header is removed from the + list and the pointer to the error messages pointers array is returned. + Otherwise, NULL is returned. + + RETURN + non-NULL OK, returns address of error messages pointers array. + NULL Error, no such number range registered. +*/ + +const char **my_error_unregister(int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + const char **errmsgs; + + /* Search for the registration in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if (((*search_meh_pp)->meh_first == first) && + ((*search_meh_pp)->meh_last == last)) + break; + } + if (! *search_meh_pp) + return NULL; + + /* Remove header from the chain. */ + meh_p= *search_meh_pp; + *search_meh_pp= meh_p->meh_next; + + /* Save the return value and free the header. */ + errmsgs= meh_p->meh_errmsgs; + my_free((gptr) meh_p, MYF(0)); + + return errmsgs; +} diff --git a/ndb/include/Makefile.am b/ndb/include/Makefile.am index 49857c53fb1..a0ac90fec13 100644 --- a/ndb/include/Makefile.am +++ b/ndb/include/Makefile.am @@ -2,6 +2,7 @@ include $(top_srcdir)/ndb/config/common.mk.am ndbinclude_HEADERS = \ +ndb_constants.h \ ndb_types.h \ ndb_version.h diff --git a/ndb/include/kernel/AttributeDescriptor.hpp b/ndb/include/kernel/AttributeDescriptor.hpp index e4c142270db..af28e777213 100644 --- a/ndb/include/kernel/AttributeDescriptor.hpp +++ b/ndb/include/kernel/AttributeDescriptor.hpp @@ -28,7 +28,6 @@ private: static void setType(Uint32 &, Uint32 type); static void setSize(Uint32 &, Uint32 size); static void setArray(Uint32 &, Uint32 arraySize); - static void setOriginal(Uint32 &, Uint32 original); static void setNullable(Uint32 &, Uint32 nullable); static void setDKey(Uint32 &, Uint32 dkey); static void setPrimaryKey(Uint32 &, Uint32 dkey); @@ -40,7 +39,6 @@ private: static Uint32 getSizeInWords(const Uint32 &); static Uint32 getArrayType(const Uint32 &); static Uint32 getArraySize(const Uint32 &); - static Uint32 getOriginal(const Uint32 &); static Uint32 getNullable(const Uint32 &); static Uint32 getDKey(const Uint32 &); static Uint32 getPrimaryKey(const Uint32 &); @@ -50,19 +48,19 @@ private: /** * * a = Array type - 2 Bits -> Max 3 (Bit 0-1) - * t = Attribute type - 2 Bits -> Max 3 (Bit 2-3) - * s = Attribute size - 3 Bits -> Max 7 (Bit 4-6) - * o = Original attribute - 1 Bit 7 - * n = Nullable - 1 Bit 8 - * d = Disk based - 1 Bit 10 - * k = Distribution Key Ind - 1 Bit 12 + * t = Attribute type - 5 Bits -> Max 31 (Bit 2-6) + * s = Attribute size - 3 Bits -> Max 7 (Bit 8-10) + * d = Disk based - 1 Bit 11 + * n = Nullable - 1 Bit 12 + * k = Distribution Key Ind - 1 Bit 13 * p = Primary key attribute - 1 Bit 14 * y = Dynamic attribute - 1 Bit 15 * z = Array size - 16 Bits -> Max 65535 (Bit 16-31) * * 1111111111222222222233 * 01234567890123456789012345678901 - * aattsss n d k pyzzzzzzzzzzzzzzzz + * aattttt sssdnkpyzzzzzzzzzzzzzzzz + * aattsss n d k pyzzzzzzzzzzzzzzzz [ old format ] * */ @@ -70,20 +68,17 @@ private: #define AD_ARRAY_TYPE_MASK (3) #define AD_TYPE_SHIFT (2) -#define AD_TYPE_MASK (3) +#define AD_TYPE_MASK (31) -#define AD_SIZE_SHIFT (4) +#define AD_SIZE_SHIFT (8) #define AD_SIZE_MASK (7) #define AD_SIZE_IN_BYTES_SHIFT (3) - #define AD_SIZE_IN_WORDS_OFFSET (31) #define AD_SIZE_IN_WORDS_SHIFT (5) -#define AD_ORIGINAL_SHIFT (8) -#define AD_NULLABLE_SHIFT (8) - -#define AD_DISTR_KEY_SHIFT (12) +#define AD_NULLABLE_SHIFT (12) +#define AD_DISTR_KEY_SHIFT (13) #define AD_PRIMARY_KEY (14) #define AD_DYNAMIC (15) @@ -123,13 +118,6 @@ AttributeDescriptor::setNullable(Uint32 & desc, Uint32 nullable){ desc |= (nullable << AD_NULLABLE_SHIFT); } -inline -void -AttributeDescriptor::setOriginal(Uint32 & desc, Uint32 original){ - ASSERT_BOOL(original, "AttributeDescriptor::setOriginal"); - desc |= (original << AD_ORIGINAL_SHIFT); -} - inline void AttributeDescriptor::setDKey(Uint32 & desc, Uint32 dkey){ @@ -199,12 +187,6 @@ AttributeDescriptor::getNullable(const Uint32 & desc){ return (desc >> AD_NULLABLE_SHIFT) & 1; } -inline -Uint32 -AttributeDescriptor::getOriginal(const Uint32 & desc){ - return (desc >> AD_ORIGINAL_SHIFT) & 1; -} - inline Uint32 AttributeDescriptor::getDKey(const Uint32 & desc){ diff --git a/ndb/include/kernel/signaldata/DictTabInfo.hpp b/ndb/include/kernel/signaldata/DictTabInfo.hpp index 4d459ddf813..616da05b3ae 100644 --- a/ndb/include/kernel/signaldata/DictTabInfo.hpp +++ b/ndb/include/kernel/signaldata/DictTabInfo.hpp @@ -100,14 +100,14 @@ public: AttributeName = 1000, // String, Mandatory AttributeId = 1001, //Mandatory between DICT's otherwise not allowed - AttributeType = 1002, //Default UnSignedType + AttributeType = 1002, //for osu 4.1->5.0.x AttributeSize = 1003, //Default DictTabInfo::a32Bit AttributeArraySize = 1005, //Default 1 AttributeKeyFlag = 1006, //Default noKey AttributeStorage = 1007, //Default MainMemory AttributeNullableFlag = 1008, //Default NotNullable AttributeDKey = 1010, //Default NotDKey - AttributeExtType = 1013, //Default 0 (undefined) + AttributeExtType = 1013, //Default ExtUnsigned AttributeExtPrecision = 1014, //Default 0 AttributeExtScale = 1015, //Default 0 AttributeExtLength = 1016, //Default 0 @@ -201,12 +201,6 @@ public: StorePermanent = 2 }; - // AttributeType constants - STATIC_CONST( SignedType = 0 ); - STATIC_CONST( UnSignedType = 1 ); - STATIC_CONST( FloatingPointType = 2 ); - STATIC_CONST( StringType = 3 ); - // AttributeSize constants STATIC_CONST( aBit = 0 ); STATIC_CONST( an8Bit = 3 ); @@ -282,7 +276,7 @@ public: struct Attribute { char AttributeName[MAX_TAB_NAME_SIZE]; Uint32 AttributeId; - Uint32 AttributeType; + Uint32 AttributeType; // for osu 4.1->5.0.x Uint32 AttributeSize; Uint32 AttributeArraySize; Uint32 AttributeKeyFlag; @@ -306,66 +300,55 @@ public: // translate to old kernel types and sizes inline bool translateExtType() { + AttributeType = ~0; // deprecated switch (AttributeExtType) { case DictTabInfo::ExtUndefined: break; case DictTabInfo::ExtTinyint: - AttributeType = DictTabInfo::SignedType; AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtTinyunsigned: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtSmallint: - AttributeType = DictTabInfo::SignedType; AttributeSize = DictTabInfo::a16Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtSmallunsigned: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::a16Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtMediumint: - AttributeType = DictTabInfo::SignedType; AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = 3 * AttributeExtLength; return true; case DictTabInfo::ExtMediumunsigned: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = 3 * AttributeExtLength; return true; case DictTabInfo::ExtInt: - AttributeType = DictTabInfo::SignedType; AttributeSize = DictTabInfo::a32Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtUnsigned: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::a32Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtBigint: - AttributeType = DictTabInfo::SignedType; AttributeSize = DictTabInfo::a64Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtBigunsigned: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::a64Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtFloat: - AttributeType = DictTabInfo::FloatingPointType; AttributeSize = DictTabInfo::a32Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtDouble: - AttributeType = DictTabInfo::FloatingPointType; AttributeSize = DictTabInfo::a64Bit; AttributeArraySize = AttributeExtLength; return true; @@ -374,35 +357,32 @@ public: break; case DictTabInfo::ExtChar: case DictTabInfo::ExtBinary: - AttributeType = DictTabInfo::StringType; AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = AttributeExtLength; return true; case DictTabInfo::ExtVarchar: case DictTabInfo::ExtVarbinary: - AttributeType = DictTabInfo::StringType; + // to fix AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = AttributeExtLength + 2; return true; case DictTabInfo::ExtDatetime: - AttributeType = DictTabInfo::StringType; + // to fix AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = 8 * AttributeExtLength; return true; case DictTabInfo::ExtTimespec: - AttributeType = DictTabInfo::StringType; + // to fix AttributeSize = DictTabInfo::an8Bit; AttributeArraySize = 12 * AttributeExtLength; return true; case DictTabInfo::ExtBlob: case DictTabInfo::ExtText: - AttributeType = DictTabInfo::StringType; AttributeSize = DictTabInfo::an8Bit; // head + inline part [ attr precision lower half ] AttributeArraySize = (NDB_BLOB_HEAD_SIZE << 2) + (AttributeExtPrecision & 0xFFFF); return true; case DictTabInfo::ExtBit: - AttributeType = DictTabInfo::UnSignedType; AttributeSize = DictTabInfo::aBit; AttributeArraySize = AttributeExtLength; return true; diff --git a/ndb/include/ndb_constants.h b/ndb/include/ndb_constants.h new file mode 100644 index 00000000000..04d86e267f7 --- /dev/null +++ b/ndb/include/ndb_constants.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2003 MySQL AB + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + * @file ndb_constants.h + * + * Constants common to NDB API and NDB kernel. + * Changing the values makes database upgrade impossible. + * + * New or removed definitions must be replicated to + * NdbDictionary.hpp and NdbSqlUtil.cpp. + * + * Not for use by application programs. + * Use the enums provided by NdbDictionary instead. + */ + +#ifndef NDB_CONSTANTS_H +#define NDB_CONSTANTS_H + +/* + * Data type constants. + */ + +#define NDB_TYPE_UNDEFINED 0 + +#define NDB_TYPE_TINYINT 1 +#define NDB_TYPE_TINYUNSIGNED 2 +#define NDB_TYPE_SMALLINT 3 +#define NDB_TYPE_SMALLUNSIGNED 4 +#define NDB_TYPE_MEDIUMINT 5 +#define NDB_TYPE_MEDIUMUNSIGNED 6 +#define NDB_TYPE_INT 7 +#define NDB_TYPE_UNSIGNED 8 +#define NDB_TYPE_BIGINT 9 +#define NDB_TYPE_BIGUNSIGNED 10 +#define NDB_TYPE_FLOAT 11 +#define NDB_TYPE_DOUBLE 12 +#define NDB_TYPE_DECIMAL 13 // not used +#define NDB_TYPE_CHAR 14 +#define NDB_TYPE_VARCHAR 15 +#define NDB_TYPE_BINARY 16 +#define NDB_TYPE_VARBINARY 17 +#define NDB_TYPE_DATETIME 18 // need to fix +#define NDB_TYPE_TIMESPEC 19 // need to fix +#define NDB_TYPE_BLOB 20 +#define NDB_TYPE_TEXT 21 +#define NDB_TYPE_BIT 22 + +#define NDB_TYPE_MAX 23 + +#endif diff --git a/ndb/include/ndb_types.h b/ndb/include/ndb_types.h index 0d603cc2ab3..cec6c48c15c 100644 --- a/ndb/include/ndb_types.h +++ b/ndb/include/ndb_types.h @@ -56,5 +56,6 @@ typedef unsigned long long Uint64; typedef signed long long Int64; #endif +#include "ndb_constants.h" #endif diff --git a/ndb/include/ndbapi/NdbDictionary.hpp b/ndb/include/ndbapi/NdbDictionary.hpp index 05a9a4fd338..2e5d70a1a76 100644 --- a/ndb/include/ndbapi/NdbDictionary.hpp +++ b/ndb/include/ndbapi/NdbDictionary.hpp @@ -95,8 +95,6 @@ public: SystemTable = 1, ///< System table UserTable = 2, ///< User table (may be temporary) UniqueHashIndex = 3, ///< Unique un-ordered hash index - HashIndex = 4, ///< Non-unique un-ordered hash index - UniqueOrderedIndex = 5, ///< Unique ordered index OrderedIndex = 6, ///< Non-unique ordered index HashIndexTrigger = 7, ///< Index maintenance, internal IndexTrigger = 8, ///< Index maintenance, internal @@ -164,29 +162,29 @@ public: * The builtin column types */ enum Type { - Undefined=0, ///< Undefined - Tinyint, ///< 8 bit. 1 byte signed integer, can be used in array - Tinyunsigned, ///< 8 bit. 1 byte unsigned integer, can be used in array - Smallint, ///< 16 bit. 2 byte signed integer, can be used in array - Smallunsigned, ///< 16 bit. 2 byte unsigned integer, can be used in array - Mediumint, ///< 24 bit. 3 byte signed integer, can be used in array - Mediumunsigned,///< 24 bit. 3 byte unsigned integer, can be used in array - Int, ///< 32 bit. 4 byte signed integer, can be used in array - Unsigned, ///< 32 bit. 4 byte unsigned integer, can be used in array - Bigint, ///< 64 bit. 8 byte signed integer, can be used in array - Bigunsigned, ///< 64 Bit. 8 byte signed integer, can be used in array - Float, ///< 32-bit float. 4 bytes float, can be used in array - Double, ///< 64-bit float. 8 byte float, can be used in array - Decimal, ///< Precision, Scale are applicable - Char, ///< Len. A fixed array of 1-byte chars - Varchar, ///< Max len - Binary, ///< Len - Varbinary, ///< Max len - Datetime, ///< Precision down to 1 sec (sizeof(Datetime) == 8 bytes ) - Timespec, ///< Precision down to 1 nsec(sizeof(Datetime) == 12 bytes ) - Blob, ///< Binary large object (see NdbBlob) - Text, ///< Text blob, - Bit ///< Bit, length specifies no of bits + Undefined = NDB_TYPE_UNDEFINED, ///< Undefined + Tinyint = NDB_TYPE_TINYINT, ///< 8 bit. 1 byte signed integer, can be used in array + Tinyunsigned = NDB_TYPE_TINYUNSIGNED, ///< 8 bit. 1 byte unsigned integer, can be used in array + Smallint = NDB_TYPE_SMALLINT, ///< 16 bit. 2 byte signed integer, can be used in array + Smallunsigned = NDB_TYPE_SMALLUNSIGNED, ///< 16 bit. 2 byte unsigned integer, can be used in array + Mediumint = NDB_TYPE_MEDIUMINT, ///< 24 bit. 3 byte signed integer, can be used in array + Mediumunsigned = NDB_TYPE_MEDIUMUNSIGNED,///< 24 bit. 3 byte unsigned integer, can be used in array + Int = NDB_TYPE_INT, ///< 32 bit. 4 byte signed integer, can be used in array + Unsigned = NDB_TYPE_UNSIGNED, ///< 32 bit. 4 byte unsigned integer, can be used in array + Bigint = NDB_TYPE_BIGINT, ///< 64 bit. 8 byte signed integer, can be used in array + Bigunsigned = NDB_TYPE_BIGUNSIGNED, ///< 64 Bit. 8 byte signed integer, can be used in array + Float = NDB_TYPE_FLOAT, ///< 32-bit float. 4 bytes float, can be used in array + Double = NDB_TYPE_DOUBLE, ///< 64-bit float. 8 byte float, can be used in array + Decimal = NDB_TYPE_DECIMAL, ///< Precision, Scale are applicable + Char = NDB_TYPE_CHAR, ///< Len. A fixed array of 1-byte chars + Varchar = NDB_TYPE_VARCHAR, ///< Max len + Binary = NDB_TYPE_BINARY, ///< Len + Varbinary = NDB_TYPE_VARBINARY, ///< Max len + Datetime = NDB_TYPE_DATETIME, ///< Precision down to 1 sec (sizeof(Datetime) == 8 bytes ) + Timespec = NDB_TYPE_TIMESPEC, ///< Precision down to 1 nsec(sizeof(Datetime) == 12 bytes ) + Blob = NDB_TYPE_BLOB, ///< Binary large object (see NdbBlob) + Text = NDB_TYPE_TEXT, ///< Text blob, + Bit = NDB_TYPE_BIT ///< Bit, length specifies no of bits }; /** diff --git a/ndb/include/ndbapi/NdbOperation.hpp b/ndb/include/ndbapi/NdbOperation.hpp index c9a961c519d..4d7db773505 100644 --- a/ndb/include/ndbapi/NdbOperation.hpp +++ b/ndb/include/ndbapi/NdbOperation.hpp @@ -577,21 +577,21 @@ public: * @param Label label to jump to * @return -1 if unsuccessful */ - int branch_col_eq(Uint32 ColId, const char * val, Uint32 len, + int branch_col_eq(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_ne(Uint32 ColId, const char * val, Uint32 len, + int branch_col_ne(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_lt(Uint32 ColId, const char * val, Uint32 len, + int branch_col_lt(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_le(Uint32 ColId, const char * val, Uint32 len, + int branch_col_le(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_gt(Uint32 ColId, const char * val, Uint32 len, + int branch_col_gt(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_ge(Uint32 ColId, const char * val, Uint32 len, + int branch_col_ge(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label); - int branch_col_like(Uint32 ColId, const char *, Uint32 len, + int branch_col_like(Uint32 ColId, const void *, Uint32 len, bool nopad, Uint32 Label); - int branch_col_notlike(Uint32 ColId, const char *, Uint32 len, + int branch_col_notlike(Uint32 ColId, const void *, Uint32 len, bool nopad, Uint32 Label); /** @@ -822,7 +822,7 @@ protected: int read_attr(const NdbColumnImpl* anAttrObject, Uint32 RegDest); int write_attr(const NdbColumnImpl* anAttrObject, Uint32 RegSource); int branch_reg_reg(Uint32 type, Uint32, Uint32, Uint32); - int branch_col(Uint32 type, Uint32, const char *, Uint32, bool, Uint32 Label); + int branch_col(Uint32 type, Uint32, const void *, Uint32, bool, Uint32 Label); int branch_col_null(Uint32 type, Uint32 col, Uint32 Label); // Handle ATTRINFO signals diff --git a/ndb/include/ndbapi/NdbReceiver.hpp b/ndb/include/ndbapi/NdbReceiver.hpp index 3b7b1535b28..0ec4b6ecb13 100644 --- a/ndb/include/ndbapi/NdbReceiver.hpp +++ b/ndb/include/ndbapi/NdbReceiver.hpp @@ -98,7 +98,7 @@ private: Uint32 m_received_result_length; bool nextResult() const { return m_current_row < m_result_rows; } - void copyout(NdbReceiver&); + NdbRecAttr* copyout(NdbReceiver&); }; #ifdef NDB_NO_DROPPED_SIGNAL diff --git a/ndb/include/ndbapi/NdbScanFilter.hpp b/ndb/include/ndbapi/NdbScanFilter.hpp index 02190f88b9a..6acb0456333 100644 --- a/ndb/include/ndbapi/NdbScanFilter.hpp +++ b/ndb/include/ndbapi/NdbScanFilter.hpp @@ -45,7 +45,17 @@ public: NAND = 3, ///< NOT (x1 AND x2 AND x3) NOR = 4 ///< NOT (x1 OR x2 OR x3) }; - + + enum BinaryCondition + { + COND_LE = 0, ///< lower bound + COND_LT = 1, ///< lower bound, strict + COND_GE = 2, ///< upper bound + COND_GT = 3, ///< upper bound, strict + COND_EQ = 4, ///< equality + COND_NE = 5 + }; + /** * @name Grouping * @{ @@ -74,7 +84,12 @@ public: * Explanation missing */ int isfalse(); - + + /** + * Compare column ColId with val + */ + int cmp(BinaryCondition cond, int ColId, const void *val, Uint32 len); + /** * @name Integer Comparators * @{ @@ -82,80 +97,61 @@ public: /** Compare column value with integer for equal * ®return 0 if successful, -1 otherwize */ - int eq(int ColId, Uint32 value); + int eq(int ColId, Uint32 value) { return cmp(COND_EQ, ColId, &value, 4);} + /** Compare column value with integer for not equal. * ®return 0 if successful, -1 otherwize */ - int ne(int ColId, Uint32 value); + int ne(int ColId, Uint32 value) { return cmp(COND_NE, ColId, &value, 4);} /** Compare column value with integer for less than. * ®return 0 if successful, -1 otherwize */ - int lt(int ColId, Uint32 value); + int lt(int ColId, Uint32 value) { return cmp(COND_LT, ColId, &value, 4);} /** Compare column value with integer for less than or equal. * ®return 0 if successful, -1 otherwize */ - int le(int ColId, Uint32 value); + int le(int ColId, Uint32 value) { return cmp(COND_LE, ColId, &value, 4);} /** Compare column value with integer for greater than. * ®return 0 if successful, -1 otherwize */ - int gt(int ColId, Uint32 value); + int gt(int ColId, Uint32 value) { return cmp(COND_GT, ColId, &value, 4);} /** Compare column value with integer for greater than or equal. * ®return 0 if successful, -1 otherwize */ - int ge(int ColId, Uint32 value); + int ge(int ColId, Uint32 value) { return cmp(COND_GE, ColId, &value, 4);} /** Compare column value with integer for equal. 64-bit. * ®return 0 if successful, -1 otherwize */ - int eq(int ColId, Uint64 value); + int eq(int ColId, Uint64 value) { return cmp(COND_EQ, ColId, &value, 8);} /** Compare column value with integer for not equal. 64-bit. * ®return 0 if successful, -1 otherwize */ - int ne(int ColId, Uint64 value); + int ne(int ColId, Uint64 value) { return cmp(COND_NE, ColId, &value, 8);} /** Compare column value with integer for less than. 64-bit. * ®return 0 if successful, -1 otherwize */ - int lt(int ColId, Uint64 value); + int lt(int ColId, Uint64 value) { return cmp(COND_LT, ColId, &value, 8);} /** Compare column value with integer for less than or equal. 64-bit. * ®return 0 if successful, -1 otherwize */ - int le(int ColId, Uint64 value); + int le(int ColId, Uint64 value) { return cmp(COND_LE, ColId, &value, 8);} /** Compare column value with integer for greater than. 64-bit. * ®return 0 if successful, -1 otherwize */ - int gt(int ColId, Uint64 value); + int gt(int ColId, Uint64 value) { return cmp(COND_GT, ColId, &value, 8);} /** Compare column value with integer for greater than or equal. 64-bit. * ®return 0 if successful, -1 otherwize */ - int ge(int ColId, Uint64 value); + int ge(int ColId, Uint64 value) { return cmp(COND_GE, ColId, &value, 8);} /** @} *********************************************************************/ /** Check if column value is NULL */ int isnull(int ColId); /** Check if column value is non-NULL */ int isnotnull(int ColId); - - /** - * @name String Comparators - * @{ - */ - /** - * Compare string against a Char or Varchar column. - * - * By default Char comparison blank-pads both sides to common length. - * Varchar comparison does not blank-pad. - * - * The extra nopad argument can be used to - * force non-padded comparison for a Char column. - * ®return 0 if successful, -1 otherwize - */ - int eq(int ColId, const char * val, Uint32 len, bool nopad=false); - int ne(int ColId, const char * val, Uint32 len, bool nopad=false); - int lt(int ColId, const char * val, Uint32 len, bool nopad=false); - int le(int ColId, const char * val, Uint32 len, bool nopad=false); - int gt(int ColId, const char * val, Uint32 len, bool nopad=false); - int ge(int ColId, const char * val, Uint32 len, bool nopad=false); - + +#ifndef DOXYGEN_SHOULD_SKIP_INTERNAL /** * Like comparison operator. * ®return 0 if successful, -1 otherwize @@ -167,6 +163,7 @@ public: */ int notlike(int ColId, const char * val, Uint32 len, bool nopad=false); /** @} *********************************************************************/ +#endif private: #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL diff --git a/ndb/include/ndbapi/NdbScanOperation.hpp b/ndb/include/ndbapi/NdbScanOperation.hpp index 58793c28750..7e3a0ed18b7 100644 --- a/ndb/include/ndbapi/NdbScanOperation.hpp +++ b/ndb/include/ndbapi/NdbScanOperation.hpp @@ -206,6 +206,7 @@ protected: bool m_ordered; bool m_descending; Uint32 m_read_range_no; + NdbRecAttr *m_curr_row; // Pointer to last returned row }; inline diff --git a/ndb/include/util/NdbSqlUtil.hpp b/ndb/include/util/NdbSqlUtil.hpp index 3deaf81cfc7..8ea3e9c8124 100644 --- a/ndb/include/util/NdbSqlUtil.hpp +++ b/ndb/include/util/NdbSqlUtil.hpp @@ -62,35 +62,31 @@ public: CmpUnknown = 2 // insufficient partial data }; - /** - * Kernel data types. Must match m_typeList in NdbSqlUtil.cpp. - * Now also must match types in NdbDictionary. - */ struct Type { enum Enum { - Undefined = 0, // Undefined - Tinyint, // 8 bit - Tinyunsigned, // 8 bit - Smallint, // 16 bit - Smallunsigned, // 16 bit - Mediumint, // 24 bit - Mediumunsigned, // 24 bit - Int, // 32 bit - Unsigned, // 32 bit - Bigint, // 64 bit - Bigunsigned, // 64 Bit - Float, // 32-bit float - Double, // 64-bit float - Decimal, // Precision, Scale - Char, // Len - Varchar, // Max len - Binary, // Len - Varbinary, // Max len - Datetime, // Precision down to 1 sec (size 8 bytes) - Timespec, // Precision down to 1 nsec (size 12 bytes) - Blob, // Blob - Text, // Text blob, - Bit // A bit + Undefined = NDB_TYPE_UNDEFINED, + Tinyint = NDB_TYPE_TINYINT, + Tinyunsigned = NDB_TYPE_TINYUNSIGNED, + Smallint = NDB_TYPE_SMALLINT, + Smallunsigned = NDB_TYPE_SMALLUNSIGNED, + Mediumint = NDB_TYPE_MEDIUMINT, + Mediumunsigned = NDB_TYPE_MEDIUMUNSIGNED, + Int = NDB_TYPE_INT, + Unsigned = NDB_TYPE_UNSIGNED, + Bigint = NDB_TYPE_BIGINT, + Bigunsigned = NDB_TYPE_BIGUNSIGNED, + Float = NDB_TYPE_FLOAT, + Double = NDB_TYPE_DOUBLE, + Decimal = NDB_TYPE_DECIMAL, + Char = NDB_TYPE_CHAR, + Varchar = NDB_TYPE_VARCHAR, + Binary = NDB_TYPE_BINARY, + Varbinary = NDB_TYPE_VARBINARY, + Datetime = NDB_TYPE_DATETIME, + Timespec = NDB_TYPE_TIMESPEC, + Blob = NDB_TYPE_BLOB, + Text = NDB_TYPE_TEXT, + Bit = NDB_TYPE_BIT }; Enum m_typeId; Cmp* m_cmp; // comparison method diff --git a/ndb/src/common/debugger/signaldata/DictTabInfo.cpp b/ndb/src/common/debugger/signaldata/DictTabInfo.cpp index a0a1c4b169d..816c4fb9452 100644 --- a/ndb/src/common/debugger/signaldata/DictTabInfo.cpp +++ b/ndb/src/common/debugger/signaldata/DictTabInfo.cpp @@ -59,7 +59,7 @@ SimpleProperties::SP2StructMapping DictTabInfo::AttributeMapping[] = { DTIMAPS(Attribute, AttributeName, AttributeName, 0, MAX_ATTR_NAME_SIZE), DTIMAP(Attribute, AttributeId, AttributeId), - DTIMAP2(Attribute, AttributeType, AttributeType, 0, 3), + DTIMAP(Attribute, AttributeType, AttributeType), DTIMAP2(Attribute, AttributeSize, AttributeSize, 3, 7), DTIMAP2(Attribute, AttributeArraySize, AttributeArraySize, 0, 65535), DTIMAP2(Attribute, AttributeKeyFlag, AttributeKeyFlag, 0, 1), @@ -126,13 +126,13 @@ void DictTabInfo::Attribute::init(){ memset(AttributeName, 0, sizeof(AttributeName));//AttributeName[0] = 0; AttributeId = 0; - AttributeType = DictTabInfo::UnSignedType; + AttributeType = ~0, // deprecated AttributeSize = DictTabInfo::a32Bit; AttributeArraySize = 1; AttributeKeyFlag = 0; AttributeNullableFlag = 0; AttributeDKey = 0; - AttributeExtType = 0, + AttributeExtType = DictTabInfo::ExtUnsigned, AttributeExtPrecision = 0, AttributeExtScale = 0, AttributeExtLength = 0, diff --git a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp index fa13b359ca6..c895b8ca6f0 100644 --- a/ndb/src/kernel/blocks/dbdict/Dbdict.cpp +++ b/ndb/src/kernel/blocks/dbdict/Dbdict.cpp @@ -290,12 +290,12 @@ Dbdict::packTableIntoPagesImpl(SimpleProperties::Writer & w, const Uint32 nullable = AttributeDescriptor::getNullable(desc); const Uint32 DKey = AttributeDescriptor::getDKey(desc); - w.add(DictTabInfo::AttributeType, attrType); + // AttributeType deprecated w.add(DictTabInfo::AttributeSize, attrSize); w.add(DictTabInfo::AttributeArraySize, arraySize); w.add(DictTabInfo::AttributeNullableFlag, nullable); w.add(DictTabInfo::AttributeDKey, DKey); - w.add(DictTabInfo::AttributeExtType, attrPtr.p->extType); + w.add(DictTabInfo::AttributeExtType, attrType); w.add(DictTabInfo::AttributeExtPrecision, attrPtr.p->extPrecision); w.add(DictTabInfo::AttributeExtScale, attrPtr.p->extScale); w.add(DictTabInfo::AttributeExtLength, attrPtr.p->extLength); @@ -4169,7 +4169,7 @@ Dbdict::sendLQHADDATTRREQ(Signal* signal, LqhAddAttrReq::Entry& entry = req->attributes[i]; entry.attrId = attrPtr.p->attributeId; entry.attrDescriptor = attrPtr.p->attributeDescriptor; - entry.extTypeInfo = attrPtr.p->extType; + entry.extTypeInfo = 0; // charset number passed to TUP, TUX in upper half entry.extTypeInfo |= (attrPtr.p->extPrecision & ~0xFFFF); if (tabPtr.p->isIndex()) { @@ -4792,7 +4792,6 @@ void Dbdict::handleTabInfo(SimpleProperties::Reader & it, attrPtr.p->attributeId = attrDesc.AttributeId; attrPtr.p->tupleKey = (keyCount + 1) * attrDesc.AttributeKeyFlag; - attrPtr.p->extType = attrDesc.AttributeExtType; attrPtr.p->extPrecision = attrDesc.AttributeExtPrecision; attrPtr.p->extScale = attrDesc.AttributeExtScale; attrPtr.p->extLength = attrDesc.AttributeExtLength; @@ -4843,7 +4842,7 @@ void Dbdict::handleTabInfo(SimpleProperties::Reader & it, } Uint32 desc = 0; - AttributeDescriptor::setType(desc, attrDesc.AttributeType); + AttributeDescriptor::setType(desc, attrDesc.AttributeExtType); AttributeDescriptor::setSize(desc, attrDesc.AttributeSize); AttributeDescriptor::setArray(desc, attrDesc.AttributeArraySize); AttributeDescriptor::setNullable(desc, attrDesc.AttributeNullableFlag); @@ -6406,6 +6405,7 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) jam(); const Uint32 a = aRec->attributeDescriptor; bool isNullable = AttributeDescriptor::getNullable(a); + Uint32 attrType = AttributeDescriptor::getType(a); w.add(DictTabInfo::AttributeName, aRec->attributeName); w.add(DictTabInfo::AttributeId, k); if (indexPtr.p->isHashIndex()) { @@ -6416,8 +6416,7 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)isNullable); } - // ext type overrides - w.add(DictTabInfo::AttributeExtType, aRec->extType); + w.add(DictTabInfo::AttributeExtType, attrType); w.add(DictTabInfo::AttributeExtPrecision, aRec->extPrecision); w.add(DictTabInfo::AttributeExtScale, aRec->extScale); w.add(DictTabInfo::AttributeExtLength, aRec->extLength); @@ -6431,7 +6430,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz); w.add(DictTabInfo::AttributeKeyFlag, (Uint32)false); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false); - // ext type overrides w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned); w.add(DictTabInfo::AttributeExtLength, tablePtr.p->tupKeyLength); w.add(DictTabInfo::AttributeEnd, (Uint32)true); @@ -6443,7 +6441,6 @@ Dbdict::createIndex_toCreateTable(Signal* signal, OpCreateIndexPtr opPtr) w.add(DictTabInfo::AttributeId, opPtr.p->m_attrList.sz); w.add(DictTabInfo::AttributeKeyFlag, (Uint32)true); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)false); - // ext type overrides w.add(DictTabInfo::AttributeExtType, (Uint32)DictTabInfo::ExtUnsigned); w.add(DictTabInfo::AttributeExtLength, indexPtr.p->tupKeyLength); w.add(DictTabInfo::AttributeEnd, (Uint32)true); diff --git a/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp b/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp index cb5477a1551..9778c938e0f 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupExecQuery.cpp @@ -1540,13 +1540,8 @@ int Dbtup::interpreterNextLab(Signal* signal, // Calculate the number of words of this attribute. // We allow writes into arrays as long as they fit into the 64 bit // register size. - //TEST_MR See to that TattrNoOfWords can be - // read faster from attribute description. /* --------------------------------------------------------------- */ - Uint32 TarraySize = (TattrDesc1 >> 16); - Uint32 TattrLogLen = (TattrDesc1 >> 4) & 0xf; - Uint32 TattrNoOfBits = TarraySize << TattrLogLen; - Uint32 TattrNoOfWords = (TattrNoOfBits + 31) >> 5; + Uint32 TattrNoOfWords = AttributeDescriptor::getSizeInWords(TattrDesc1); Uint32 Toptype = operPtr.p->optype; Uint32 TdataForUpdate[3]; @@ -1823,9 +1818,6 @@ int Dbtup::interpreterNextLab(Signal* signal, case Interpreter::BRANCH_ATTR_OP_ARG:{ jam(); Uint32 cond = Interpreter::getBinaryCondition(theInstruction); - Uint32 diff = Interpreter::getArrayLengthDiff(theInstruction); - Uint32 vchr = Interpreter::isVarchar(theInstruction); - Uint32 nopad =Interpreter::isNopad(theInstruction); Uint32 ins2 = TcurrentProgram[TprogramCounter]; Uint32 attrId = Interpreter::getBranchCol_AttrId(ins2) << 16; Uint32 argLen = Interpreter::getBranchCol_Len(ins2); @@ -1844,84 +1836,81 @@ int Dbtup::interpreterNextLab(Signal* signal, } tmpHabitant = attrId; } - - AttributeHeader ah(tmpArea[0]); + attrId >>= 16; + AttributeHeader ah(tmpArea[0]); + const char* s1 = (char*)&tmpArea[1]; const char* s2 = (char*)&TcurrentProgram[TprogramCounter+1]; - Uint32 attrLen = (4 * ah.getDataSize()) - diff; - if (vchr) { -#if NDB_VERSION_MAJOR >= 3 - bool vok = false; - if (attrLen >= 2) { - Uint32 vlen = (s1[0] << 8) | s1[1]; // big-endian - s1 += 2; - attrLen -= 2; - if (attrLen >= vlen) { - attrLen = vlen; - vok = true; - } - } - if (!vok) { - terrorCode = ZREGISTER_INIT_ERROR; - tupkeyErrorLab(signal); - return -1; - } -#else - Uint32 tmp; - if (attrLen >= 2) { - unsigned char* ss = (unsigned char*)&s1[attrLen - 2]; - tmp = (ss[0] << 8) | ss[1]; - if (tmp <= attrLen - 2) - attrLen = tmp; - } - // XXX handle bad data -#endif - } - bool res = false; - + Uint32 attrLen = (4 * ah.getDataSize()); + Uint32 TattrDescrIndex = tabptr.p->tabDescriptor + + (attrId << ZAD_LOG_SIZE); + Uint32 TattrDesc1 = tableDescriptor[TattrDescrIndex].tabDescr; + Uint32 TattrDesc2 = tableDescriptor[TattrDescrIndex+1].tabDescr; + Uint32 typeId = AttributeDescriptor::getType(TattrDesc1); + void * cs = 0; + if(AttributeOffset::getCharsetFlag(TattrDesc2)) + { + Uint32 pos = AttributeOffset::getCharsetPos(TattrDesc2); + cs = tabptr.p->charsetArray[pos]; + } + const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(typeId); + + bool r1_null = ah.isNULL(); + bool r2_null = argLen == 0; + int res; + if(r1_null || r2_null) + { + res = r1_null && r2_null ? 0 : r1_null ? -1 : 1; + } + else + { + res = (*sqlType.m_cmp)(cs, s1, attrLen, s2, argLen, true); + } + switch ((Interpreter::BinaryCondition)cond) { case Interpreter::EQ: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) == 0; + res = (res == 0); break; case Interpreter::NE: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) != 0; + res = (res != 0); break; // note the condition is backwards case Interpreter::LT: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) > 0; + res = (res > 0); break; case Interpreter::LE: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) >= 0; + res = (res >= 0); break; case Interpreter::GT: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) < 0; + res = (res < 0); break; case Interpreter::GE: - res = NdbSqlUtil::char_compare(s1, attrLen, s2, argLen, !nopad) <= 0; + res = (res <= 0); break; case Interpreter::LIKE: - res = NdbSqlUtil::char_like(s1, attrLen, s2, argLen, !nopad); + res = NdbSqlUtil::char_like(s1, attrLen, s2, argLen, false); break; case Interpreter::NOT_LIKE: - res = ! NdbSqlUtil::char_like(s1, attrLen, s2, argLen, !nopad); + res = ! NdbSqlUtil::char_like(s1, attrLen, s2, argLen, false); break; - // XXX handle invalid value + // XXX handle invalid value } #ifdef TRACE_INTERPRETER - ndbout_c("cond=%u diff=%d vc=%d nopad=%d attr(%d) = >%.*s<(%d) str=>%.*s<(%d) -> res = %d", - cond, diff, vchr, nopad, - attrId >> 16, attrLen, s1, attrLen, argLen, s2, argLen, res); + ndbout_c("cond=%u diff=%d vc=%d nopad=%d attr(%d) = >%.*s<(%d) str=>%.*s<(%d) -> res = %d", + cond, diff, vchr, nopad, + attrId >> 16, attrLen, s1, attrLen, argLen, s2, argLen, res); #endif if (res) TprogramCounter = brancher(theInstruction, TprogramCounter); - else { - Uint32 tmp = (Interpreter::mod4(argLen) >> 2) + 1; + else + { + Uint32 tmp = ((argLen + 3) >> 2) + 1; TprogramCounter += tmp; } break; } - + case Interpreter::BRANCH_ATTR_EQ_NULL:{ jam(); Uint32 ins2 = TcurrentProgram[TprogramCounter]; diff --git a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp index 13593602abc..4ce807528c4 100644 --- a/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp +++ b/ndb/src/kernel/blocks/dbtup/DbtupMeta.cpp @@ -288,8 +288,7 @@ void Dbtup::execTUP_ADD_ATTRREQ(Signal* signal) ptrCheckGuard(fragOperPtr, cnoOfFragoprec, fragoperrec); Uint32 attrId = signal->theData[2]; Uint32 attrDescriptor = signal->theData[3]; - // DICT sends extended type (ignored) and charset number - Uint32 extType = (signal->theData[4] & 0xFF); + // DICT sends charset number in upper half Uint32 csNumber = (signal->theData[4] >> 16); regTabPtr.i = fragOperPtr.p->tableidFrag; diff --git a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp index 021d3d94d8e..93c4a583624 100644 --- a/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp +++ b/ndb/src/kernel/blocks/dbtux/DbtuxMeta.cpp @@ -201,7 +201,7 @@ Dbtux::execTUX_ADD_ATTRREQ(Signal* signal) DescAttr& descAttr = descEnt.m_descAttr[attrId]; descAttr.m_attrDesc = req->attrDescriptor; descAttr.m_primaryAttrId = req->primaryAttrId; - descAttr.m_typeId = req->extTypeInfo & 0xFF; + descAttr.m_typeId = AttributeDescriptor::getType(req->attrDescriptor); descAttr.m_charset = (req->extTypeInfo >> 16); #ifdef VM_TRACE if (debugFlags & DebugMeta) { diff --git a/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp b/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp index 089cf613b03..e155a51be08 100644 --- a/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp +++ b/ndb/src/kernel/blocks/ndbcntr/NdbcntrMain.cpp @@ -1562,13 +1562,9 @@ void Ndbcntr::createSystableLab(Signal* signal, unsigned index) ndbassert(column.pos == i); w.add(DictTabInfo::AttributeName, column.name); w.add(DictTabInfo::AttributeId, (Uint32)column.pos); - //w.add(DictTabInfo::AttributeType, DictTabInfo::UnSignedType); - //w.add(DictTabInfo::AttributeSize, DictTabInfo::a32Bit); - //w.add(DictTabInfo::AttributeArraySize, 1); w.add(DictTabInfo::AttributeKeyFlag, (Uint32)column.keyFlag); //w.add(DictTabInfo::AttributeStorage, (Uint32)DictTabInfo::MainMemory); w.add(DictTabInfo::AttributeNullableFlag, (Uint32)column.nullable); - // ext type overrides w.add(DictTabInfo::AttributeExtType, (Uint32)column.type); w.add(DictTabInfo::AttributeExtLength, (Uint32)column.length); w.add(DictTabInfo::AttributeEnd, (Uint32)true); diff --git a/ndb/src/kernel/vm/MetaData.hpp b/ndb/src/kernel/vm/MetaData.hpp index aded6c96573..1000114a421 100644 --- a/ndb/src/kernel/vm/MetaData.hpp +++ b/ndb/src/kernel/vm/MetaData.hpp @@ -160,7 +160,6 @@ public: Uint32 attributeDescriptor; /* Extended attributes */ - Uint32 extType; Uint32 extPrecision; Uint32 extScale; Uint32 extLength; diff --git a/ndb/src/ndbapi/Ndb.cpp b/ndb/src/ndbapi/Ndb.cpp index 4f08ac5d829..99d6c319611 100644 --- a/ndb/src/ndbapi/Ndb.cpp +++ b/ndb/src/ndbapi/Ndb.cpp @@ -912,7 +912,8 @@ Ndb::setTupleIdInNdb(Uint32 aTableId, Uint64 val, bool increase ) Uint64 Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) { - DEBUG_TRACE("opTupleIdOnNdb"); + DBUG_ENTER("Ndb::opTupleIdOnNdb"); + DBUG_PRINT("enter", ("table=%u value=%llu op=%u", aTableId, opValue, op)); NdbTransaction* tConnection; NdbOperation* tOperation; @@ -1008,7 +1009,7 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) setDatabaseName(currentDb.c_str()); setDatabaseSchemaName(currentSchema.c_str()); - return ret; + DBUG_RETURN(ret); error_handler: theError.code = tConnection->theError.code; @@ -1018,7 +1019,11 @@ Ndb::opTupleIdOnNdb(Uint32 aTableId, Uint64 opValue, Uint32 op) setDatabaseName(currentDb.c_str()); setDatabaseSchemaName(currentSchema.c_str()); - return ~0; + DBUG_PRINT("error", ("ndb=%d con=%d op=%d", + theError.code, + tConnection ? tConnection->theError.code : -1, + tOperation ? tOperation->theError.code : -1)); + DBUG_RETURN(~0); } static const Uint32 MAX_KEY_LEN_64_WORDS = 4; diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/ndb/src/ndbapi/NdbDictionaryImpl.cpp index d677301468b..68fe6f9d235 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.cpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.cpp @@ -74,7 +74,6 @@ NdbColumnImpl::operator=(const NdbColumnImpl& col) m_autoIncrement = col.m_autoIncrement; m_autoIncrementInitialValue = col.m_autoIncrementInitialValue; m_defaultValue = col.m_defaultValue; - m_attrType = col.m_attrType; m_attrSize = col.m_attrSize; m_arraySize = col.m_arraySize; m_keyInfoPos = col.m_keyInfoPos; @@ -228,7 +227,6 @@ NdbColumnImpl::create_psuedo(const char * name){ col->m_impl.m_attrId = AttributeHeader::RANGE_NO; col->m_impl.m_attrSize = 4; col->m_impl.m_arraySize = 1; - col->m_impl.m_extType = NdbSqlUtil::Type::Unsigned; } else { abort(); } @@ -1110,8 +1108,6 @@ objectTypeMapping[] = { { DictTabInfo::SystemTable, NdbDictionary::Object::SystemTable }, { DictTabInfo::UserTable, NdbDictionary::Object::UserTable }, { DictTabInfo::UniqueHashIndex, NdbDictionary::Object::UniqueHashIndex }, - { DictTabInfo::HashIndex, NdbDictionary::Object::HashIndex }, - { DictTabInfo::UniqueOrderedIndex, NdbDictionary::Object::UniqueOrderedIndex }, { DictTabInfo::OrderedIndex, NdbDictionary::Object::OrderedIndex }, { DictTabInfo::HashIndexTrigger, NdbDictionary::Object::HashIndexTrigger }, { DictTabInfo::IndexTrigger, NdbDictionary::Object::IndexTrigger }, @@ -1143,41 +1139,10 @@ static const ApiKernelMapping indexTypeMapping[] = { { DictTabInfo::UniqueHashIndex, NdbDictionary::Index::UniqueHashIndex }, - { DictTabInfo::HashIndex, NdbDictionary::Index::HashIndex }, - { DictTabInfo::UniqueOrderedIndex, NdbDictionary::Index::UniqueOrderedIndex}, { DictTabInfo::OrderedIndex, NdbDictionary::Index::OrderedIndex }, { -1, -1 } }; -// TODO: remove, api-kernel type codes must match now -static const -ApiKernelMapping -columnTypeMapping[] = { - { DictTabInfo::ExtTinyint, NdbDictionary::Column::Tinyint }, - { DictTabInfo::ExtTinyunsigned, NdbDictionary::Column::Tinyunsigned }, - { DictTabInfo::ExtSmallint, NdbDictionary::Column::Smallint }, - { DictTabInfo::ExtSmallunsigned, NdbDictionary::Column::Smallunsigned }, - { DictTabInfo::ExtMediumint, NdbDictionary::Column::Mediumint }, - { DictTabInfo::ExtMediumunsigned, NdbDictionary::Column::Mediumunsigned }, - { DictTabInfo::ExtInt, NdbDictionary::Column::Int }, - { DictTabInfo::ExtUnsigned, NdbDictionary::Column::Unsigned }, - { DictTabInfo::ExtBigint, NdbDictionary::Column::Bigint }, - { DictTabInfo::ExtBigunsigned, NdbDictionary::Column::Bigunsigned }, - { DictTabInfo::ExtFloat, NdbDictionary::Column::Float }, - { DictTabInfo::ExtDouble, NdbDictionary::Column::Double }, - { DictTabInfo::ExtDecimal, NdbDictionary::Column::Decimal }, - { DictTabInfo::ExtChar, NdbDictionary::Column::Char }, - { DictTabInfo::ExtVarchar, NdbDictionary::Column::Varchar }, - { DictTabInfo::ExtBinary, NdbDictionary::Column::Binary }, - { DictTabInfo::ExtVarbinary, NdbDictionary::Column::Varbinary }, - { DictTabInfo::ExtDatetime, NdbDictionary::Column::Datetime }, - { DictTabInfo::ExtTimespec, NdbDictionary::Column::Timespec }, - { DictTabInfo::ExtBlob, NdbDictionary::Column::Blob }, - { DictTabInfo::ExtText, NdbDictionary::Column::Text }, - { DictTabInfo::ExtBit, NdbDictionary::Column::Bit }, - { -1, -1 } -}; - int NdbDictInterface::parseTableInfo(NdbTableImpl ** ret, const Uint32 * data, Uint32 len, @@ -1249,15 +1214,11 @@ NdbDictInterface::parseTableInfo(NdbTableImpl ** ret, NdbColumnImpl * col = new NdbColumnImpl(); col->m_attrId = attrDesc.AttributeId; col->setName(attrDesc.AttributeName); - col->m_type = (NdbDictionary::Column::Type) - getApiConstant(attrDesc.AttributeExtType, - columnTypeMapping, - NdbDictionary::Column::Undefined); - if (col->m_type == NdbDictionary::Column::Undefined) { + if (attrDesc.AttributeExtType >= NDB_TYPE_MAX) { delete impl; return 703; } - col->m_extType = attrDesc.AttributeExtType; + col->m_type = (NdbDictionary::Column::Type)attrDesc.AttributeExtType; col->m_precision = (attrDesc.AttributeExtPrecision & 0xFFFF); col->m_scale = attrDesc.AttributeExtScale; col->m_length = attrDesc.AttributeExtLength; @@ -1281,7 +1242,6 @@ NdbDictInterface::parseTableInfo(NdbTableImpl ** ret, delete impl; return 703; } - col->m_attrType =attrDesc.AttributeType; col->m_attrSize = (1 << attrDesc.AttributeSize) / 8; col->m_arraySize = attrDesc.AttributeArraySize; if(attrDesc.AttributeSize == 0) @@ -1535,10 +1495,11 @@ NdbDictInterface::createOrAlterTable(Ndb & ndb, tmpAttr.AttributeNullableFlag = col->m_nullable; tmpAttr.AttributeDKey = col->m_distributionKey; - tmpAttr.AttributeExtType = - getKernelConstant(col->m_type, - columnTypeMapping, - DictTabInfo::ExtUndefined); + if (col->m_type >= NDB_TYPE_MAX) { + m_error.code = 703; + return -1; + } + tmpAttr.AttributeExtType = (Uint32)col->m_type; tmpAttr.AttributeExtPrecision = ((unsigned)col->m_precision & 0xFFFF); tmpAttr.AttributeExtScale = col->m_scale; tmpAttr.AttributeExtLength = col->m_length; @@ -2953,8 +2914,6 @@ NdbDictInterface::listObjects(NdbDictionary::Dictionary::List& list, BaseString schemaName; BaseString objectName; if ((element.type == NdbDictionary::Object::UniqueHashIndex) || - (element.type == NdbDictionary::Object::HashIndex) || - (element.type == NdbDictionary::Object::UniqueOrderedIndex) || (element.type == NdbDictionary::Object::OrderedIndex)) { char * indexName = new char[n << 2]; memcpy(indexName, &data[pos], n << 2); diff --git a/ndb/src/ndbapi/NdbDictionaryImpl.hpp b/ndb/src/ndbapi/NdbDictionaryImpl.hpp index a7aab117d13..47b711b84f3 100644 --- a/ndb/src/ndbapi/NdbDictionaryImpl.hpp +++ b/ndb/src/ndbapi/NdbDictionaryImpl.hpp @@ -73,11 +73,9 @@ public: /** * Internal types and sizes, and aggregates */ - Uint32 m_attrType; // type outsize API and DICT Uint32 m_attrSize; // element size (size when arraySize==1) Uint32 m_arraySize; // length or length+2 for Var* types Uint32 m_keyInfoPos; - Uint32 m_extType; // used by restore (kernel type in versin v2x) bool getInterpretableType() const ; bool getCharType() const; bool getBlobType() const; diff --git a/ndb/src/ndbapi/NdbIndexOperation.cpp b/ndb/src/ndbapi/NdbIndexOperation.cpp index 181449549bb..4cedffed4a2 100644 --- a/ndb/src/ndbapi/NdbIndexOperation.cpp +++ b/ndb/src/ndbapi/NdbIndexOperation.cpp @@ -61,8 +61,6 @@ NdbIndexOperation::indxInit(const NdbIndexImpl * anIndex, case(NdbDictionary::Index::UniqueHashIndex): break; case(NdbDictionary::Index::Undefined): - case(NdbDictionary::Index::HashIndex): - case(NdbDictionary::Index::UniqueOrderedIndex): case(NdbDictionary::Index::OrderedIndex): setErrorCodeAbort(4003); return -1; diff --git a/ndb/src/ndbapi/NdbOperationInt.cpp b/ndb/src/ndbapi/NdbOperationInt.cpp index 1a8523ef5fd..8e704ca4263 100644 --- a/ndb/src/ndbapi/NdbOperationInt.cpp +++ b/ndb/src/ndbapi/NdbOperationInt.cpp @@ -1010,26 +1010,31 @@ NdbOperation::insertCall(Uint32 aCall) int NdbOperation::branch_col(Uint32 type, - Uint32 ColId, const char * val, Uint32 len, + Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ if (initial_interpreterCheck() == -1) return -1; Interpreter::BinaryCondition c = (Interpreter::BinaryCondition)type; - - const NdbDictionary::Column * col = + + const NdbColumnImpl * col = m_currentTable->getColumn(ColId); if(col == 0){ abort(); } - Uint32 vc = col->getType() == NdbDictionary::Column::Varchar; - Uint32 colLen = col->getLength() + 2 * vc; - Uint32 al = (4 - (colLen & 3)) & 0x3; + Uint32 sizeInBytes = col->m_attrSize * col->m_arraySize; + if(len != 0 && len != sizeInBytes) + { + setErrorCodeAbort(4209); + return -1; + } - if (insertATTRINFO(Interpreter::BranchCol(c, al, vc, nopad)) == -1) + len = sizeInBytes; + + if (insertATTRINFO(Interpreter::BranchCol(c, 0, 0, false)) == -1) return -1; if (insertBranch(Label) == -1) @@ -1047,7 +1052,7 @@ NdbOperation::branch_col(Uint32 type, Uint32 tmp = 0; for (Uint32 i = 0; i < len-len2; i++) { char* p = (char*)&tmp; - p[i] = val[len2+i]; + p[i] = ((char*)val)[len2+i]; } insertATTRINFO(tmp); } @@ -1057,54 +1062,54 @@ NdbOperation::branch_col(Uint32 type, } int -NdbOperation::branch_col_eq(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_eq(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_eq %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::EQ, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_ne(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_ne(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_ne %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::NE, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_lt(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_lt(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_lt %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::LT, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_le(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_le(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_le %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::LE, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_gt(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_gt(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_gt %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::GT, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_ge(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_ge(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_ge %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::GE, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_like(Uint32 ColId, const char * val, Uint32 len, +NdbOperation::branch_col_like(Uint32 ColId, const void * val, Uint32 len, bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_like %u %.*s(%u,%d) -> %u", ColId, len, val, len, nopad, Label)); return branch_col(Interpreter::LIKE, ColId, val, len, nopad, Label); } int -NdbOperation::branch_col_notlike(Uint32 ColId, const char * val, Uint32 len, - bool nopad, Uint32 Label){ +NdbOperation::branch_col_notlike(Uint32 ColId, const void * val, Uint32 len, + bool nopad, Uint32 Label){ INT_DEBUG(("branch_col_notlike %u %.*s(%u,%d) -> %u", ColId,len,val,len,nopad,Label)); return branch_col(Interpreter::NOT_LIKE, ColId, val, len, nopad, Label); } diff --git a/ndb/src/ndbapi/NdbReceiver.cpp b/ndb/src/ndbapi/NdbReceiver.cpp index 8a3866af4bb..41619c6b14c 100644 --- a/ndb/src/ndbapi/NdbReceiver.cpp +++ b/ndb/src/ndbapi/NdbReceiver.cpp @@ -201,10 +201,11 @@ NdbReceiver::do_get_value(NdbReceiver * org, return; } -void +NdbRecAttr* NdbReceiver::copyout(NdbReceiver & dstRec){ - NdbRecAttr* src = m_rows[m_current_row++]; - NdbRecAttr* dst = dstRec.theFirstRecAttr; + NdbRecAttr *src = m_rows[m_current_row++]; + NdbRecAttr *dst = dstRec.theFirstRecAttr; + NdbRecAttr *start = src; Uint32 tmp = m_hidden_count; while(tmp--) src = src->next(); @@ -215,6 +216,8 @@ NdbReceiver::copyout(NdbReceiver & dstRec){ src = src->next(); dst = dst->next(); } + + return start; } int diff --git a/ndb/src/ndbapi/NdbScanFilter.cpp b/ndb/src/ndbapi/NdbScanFilter.cpp index 38b1c70c047..f462cc536e1 100644 --- a/ndb/src/ndbapi/NdbScanFilter.cpp +++ b/ndb/src/ndbapi/NdbScanFilter.cpp @@ -48,11 +48,8 @@ public: int cond_col(Interpreter::UnaryCondition, Uint32 attrId); - template - int cond_col_const(Interpreter::BinaryCondition, Uint32 attrId, T value); - int cond_col_const(Interpreter::BinaryCondition, Uint32 attrId, - const char * value, Uint32 len, bool nopad); + const void * value, Uint32 len); }; const Uint32 LabelExit = ~0; @@ -247,68 +244,7 @@ NdbScanFilter::isfalse(){ typedef int (NdbOperation:: * Branch1)(Uint32, Uint32 label); -typedef int (NdbOperation:: * Branch2)(Uint32, Uint32, Uint32 label); -typedef int (NdbOperation:: * StrBranch2)(Uint32, const char*,Uint32,bool,Uint32); - -struct tab { - Branch2 m_branches[5]; -}; - -static const tab table[] = { - /** - * EQ (AND, OR, NAND, NOR) - */ - { { 0, - &NdbOperation::branch_ne, - &NdbOperation::branch_eq, - &NdbOperation::branch_eq, - &NdbOperation::branch_ne } } - - /** - * NEQ - */ - ,{ { 0, - &NdbOperation::branch_eq, - &NdbOperation::branch_ne, - &NdbOperation::branch_ne, - &NdbOperation::branch_eq } } - - /** - * LT - */ - ,{ { 0, - &NdbOperation::branch_le, - &NdbOperation::branch_gt, - &NdbOperation::branch_gt, - &NdbOperation::branch_le } } - - /** - * LE - */ - ,{ { 0, - &NdbOperation::branch_lt, - &NdbOperation::branch_ge, - &NdbOperation::branch_ge, - &NdbOperation::branch_lt } } - - /** - * GT - */ - ,{ { 0, - &NdbOperation::branch_ge, - &NdbOperation::branch_lt, - &NdbOperation::branch_lt, - &NdbOperation::branch_ge } } - - /** - * GE - */ - ,{ { 0, - &NdbOperation::branch_gt, - &NdbOperation::branch_le, - &NdbOperation::branch_le, - &NdbOperation::branch_gt } } -}; +typedef int (NdbOperation:: * StrBranch2)(Uint32, const void*, Uint32, bool, Uint32); struct tab2 { Branch1 m_branches[5]; @@ -334,133 +270,8 @@ static const tab2 table2[] = { &NdbOperation::branch_col_eq_null } } }; -const int tab_sz = sizeof(table)/sizeof(table[0]); const int tab2_sz = sizeof(table2)/sizeof(table2[0]); -int -matchType(const NdbDictionary::Column * col){ - return 1; -} - -template int load_const(NdbOperation* op, T value, Uint32 reg); - -template<> -int -load_const(NdbOperation* op, Uint32 value, Uint32 reg){ - return op->load_const_u32(reg, value); -} - -template<> -int -load_const(NdbOperation* op, Uint64 value, Uint32 reg){ - return op->load_const_u64(reg, value); -} - -template -int -NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op, - Uint32 AttrId, T value){ - - if(op < 0 || op >= tab_sz){ - m_operation->setErrorCodeAbort(4262); - return -1; - } - - if(m_current.m_group < NdbScanFilter::AND || - m_current.m_group > NdbScanFilter::NOR){ - m_operation->setErrorCodeAbort(4260); - return -1; - } - - Branch2 branch = table[op].m_branches[m_current.m_group]; - const NdbDictionary::Column * col = - m_operation->m_currentTable->getColumn(AttrId); - - if(col == 0){ - m_operation->setErrorCodeAbort(4261); - return -1; - } - - if(!matchType(col)){ - /** - * Code not reached - */ - return -1; - } - - if(m_latestAttrib != AttrId){ - m_operation->read_attr(&NdbColumnImpl::getImpl(* col), 4); - m_latestAttrib = AttrId; - } - - load_const(m_operation, value, 5); - (m_operation->* branch)(4, 5, m_current.m_ownLabel); - - return 0; -}; - -int -NdbScanFilter::eq(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::EQ, AttrId, value); -} - -int -NdbScanFilter::ne(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::NE, AttrId, value); -} - -int -NdbScanFilter::lt(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::LT, AttrId, value); -} - -int -NdbScanFilter::le(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::LE, AttrId, value); -} - -int -NdbScanFilter::gt(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::GT, AttrId, value); -} - -int -NdbScanFilter::ge(int AttrId, Uint32 value){ - return m_impl.cond_col_const(Interpreter::GE, AttrId, value); -} - - -int -NdbScanFilter::eq(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::EQ, AttrId, value); -} - -int -NdbScanFilter::ne(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::NE, AttrId, value); -} - -int -NdbScanFilter::lt(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::LT, AttrId, value); -} - -int -NdbScanFilter::le(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::LE, AttrId, value); -} - -int -NdbScanFilter::gt(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::GT, AttrId, value); -} - -int -NdbScanFilter::ge(int AttrId, Uint64 value){ - return m_impl.cond_col_const(Interpreter::GE, AttrId, value); -} - - int NdbScanFilterImpl::cond_col(Interpreter::UnaryCondition op, Uint32 AttrId){ @@ -570,11 +381,10 @@ static const tab3 table3[] = { const int tab3_sz = sizeof(table3)/sizeof(table3[0]); - int NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op, Uint32 AttrId, - const char * value, Uint32 len, bool nopad){ + const void * value, Uint32 len){ if(op < 0 || op >= tab3_sz){ m_operation->setErrorCodeAbort(4260); return -1; @@ -595,49 +405,31 @@ NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition op, return -1; } - (m_operation->* branch)(AttrId, value, len, nopad, m_current.m_ownLabel); + (m_operation->* branch)(AttrId, value, len, false, m_current.m_ownLabel); return 0; } int -NdbScanFilter::eq(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::EQ, ColId, val, len, nopad); -} +NdbScanFilter::cmp(BinaryCondition cond, int ColId, + const void *val, Uint32 len) +{ + switch(cond){ + case COND_LE: + return m_impl.cond_col_const(Interpreter::LE, ColId, val, len); + case COND_LT: + return m_impl.cond_col_const(Interpreter::LT, ColId, val, len); + case COND_GE: + return m_impl.cond_col_const(Interpreter::GE, ColId, val, len); + case COND_GT: + return m_impl.cond_col_const(Interpreter::GT, ColId, val, len); + case COND_EQ: + return m_impl.cond_col_const(Interpreter::EQ, ColId, val, len); + case COND_NE: + return m_impl.cond_col_const(Interpreter::NE, ColId, val, len); + } + return -1; +} -int -NdbScanFilter::ne(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::NE, ColId, val, len, nopad); -} - -int -NdbScanFilter::lt(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::LT, ColId, val, len, nopad); -} - -int -NdbScanFilter::le(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::LE, ColId, val, len, nopad); -} - -int -NdbScanFilter::gt(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::GT, ColId, val, len, nopad); -} - -int -NdbScanFilter::ge(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::GE, ColId, val, len, nopad); -} - -int -NdbScanFilter::like(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::LIKE, ColId, val, len, nopad); -} - -int -NdbScanFilter::notlike(int ColId, const char * val, Uint32 len, bool nopad){ - return m_impl.cond_col_const(Interpreter::NOT_LIKE, ColId, val, len, nopad); -} #if 0 int @@ -778,10 +570,4 @@ main(void){ #endif template class Vector; -#if __SUNPRO_CC != 0x560 -#ifndef _FORTEC_ -template int NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition, Uint32 attrId, Uint32); -template int NdbScanFilterImpl::cond_col_const(Interpreter::BinaryCondition, Uint32 attrId, Uint64); -#endif -#endif diff --git a/ndb/src/ndbapi/NdbScanOperation.cpp b/ndb/src/ndbapi/NdbScanOperation.cpp index 006c9428321..f3368a89da0 100644 --- a/ndb/src/ndbapi/NdbScanOperation.cpp +++ b/ndb/src/ndbapi/NdbScanOperation.cpp @@ -160,8 +160,8 @@ NdbScanOperation::readTuples(NdbScanOperation::LockMode lm, m_keyInfo = lockExcl ? 1 : 0; bool range = false; - if (m_accessTable->m_indexType == NdbDictionary::Index::OrderedIndex || - m_accessTable->m_indexType == NdbDictionary::Index::UniqueOrderedIndex){ + if (m_accessTable->m_indexType == NdbDictionary::Index::OrderedIndex) + { if (m_currentTable == m_accessTable){ // Old way of scanning indexes, should not be allowed m_currentTable = theNdb->theDictionary-> @@ -424,6 +424,7 @@ int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend) int retVal = 2; Uint32 idx = m_current_api_receiver; Uint32 last = m_api_receivers_count; + m_curr_row = 0; if(DEBUG_NEXT_RESULT) ndbout_c("nextResult(%d) idx=%d last=%d", fetchAllowed, idx, last); @@ -434,7 +435,7 @@ int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend) for(; idx < last; idx++){ NdbReceiver* tRec = m_api_receivers[idx]; if(tRec->nextResult()){ - tRec->copyout(theReceiver); + m_curr_row = tRec->copyout(theReceiver); retVal = 0; break; } @@ -510,7 +511,7 @@ int NdbScanOperation::nextResultImpl(bool fetchAllowed, bool forceSend) for(; idx < last; idx++){ NdbReceiver* tRec = m_api_receivers[idx]; if(tRec->nextResult()){ - tRec->copyout(theReceiver); + m_curr_row = tRec->copyout(theReceiver); retVal = 0; break; } @@ -845,6 +846,7 @@ NdbScanOperation::doSendScan(int aProcessorId) } theStatus = WaitResponse; + m_curr_row = 0; m_sent_receivers_count = theParallelism; if(m_ordered) { @@ -878,16 +880,9 @@ NdbScanOperation::doSendScan(int aProcessorId) int NdbScanOperation::getKeyFromKEYINFO20(Uint32* data, unsigned size) { - Uint32 idx = m_current_api_receiver; - Uint32 last = m_api_receivers_count; - - Uint32 row; - NdbReceiver * tRec; - NdbRecAttr * tRecAttr; - if(idx < last && (tRec = m_api_receivers[idx]) - && ((row = tRec->m_current_row) <= tRec->m_defined_rows) - && (tRecAttr = tRec->m_rows[row-1])){ - + NdbRecAttr * tRecAttr = m_curr_row; + if(tRecAttr) + { const Uint32 * src = (Uint32*)tRecAttr->aRef(); memcpy(data, src, 4*size); return 0; @@ -896,18 +891,12 @@ NdbScanOperation::getKeyFromKEYINFO20(Uint32* data, unsigned size) } NdbOperation* -NdbScanOperation::takeOverScanOp(OperationType opType, NdbTransaction* pTrans){ +NdbScanOperation::takeOverScanOp(OperationType opType, NdbTransaction* pTrans) +{ - Uint32 idx = m_current_api_receiver; - Uint32 last = m_api_receivers_count; - - Uint32 row; - NdbReceiver * tRec; - NdbRecAttr * tRecAttr; - if(idx < last && (tRec = m_api_receivers[idx]) - && ((row = tRec->m_current_row) <= tRec->m_defined_rows) - && (tRecAttr = tRec->m_rows[row-1])){ - + NdbRecAttr * tRecAttr = m_curr_row; + if(tRecAttr) + { NdbOperation * newOp = pTrans->getNdbOperation(m_currentTable); if (newOp == NULL){ return NULL; @@ -1284,7 +1273,7 @@ NdbIndexScanOperation::compare(Uint32 skip, Uint32 cols, const NdbColumnImpl & col = NdbColumnImpl::getImpl(* r1->m_column); Uint32 len = r1->theAttrSize * r1->theArraySize; if(!r1_null){ - const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(col.m_extType); + const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(col.m_type); int r = (*sqlType.m_cmp)(col.m_cs, d1, len, d2, len, true); if(r){ assert(r != NdbSqlUtil::CmpUnknown); @@ -1302,6 +1291,7 @@ int NdbIndexScanOperation::next_result_ordered(bool fetchAllowed, bool forceSend){ + m_curr_row = 0; Uint32 u_idx = 0, u_last = 0; Uint32 s_idx = m_current_api_receiver; // first sorted Uint32 s_last = theParallelism; // last sorted @@ -1412,7 +1402,7 @@ NdbIndexScanOperation::next_result_ordered(bool fetchAllowed, tRec = m_api_receivers[s_idx]; if(s_idx < s_last && tRec->nextResult()){ - tRec->copyout(theReceiver); + m_curr_row = tRec->copyout(theReceiver); if(DEBUG_NEXT_RESULT) ndbout_c("return 0"); return 0; } @@ -1667,23 +1657,13 @@ NdbIndexScanOperation::end_of_bound(Uint32 no) int NdbIndexScanOperation::get_range_no() { - if(m_read_range_no) + NdbRecAttr* tRecAttr = m_curr_row; + if(m_read_range_no && tRecAttr) { - Uint32 idx = m_current_api_receiver; - Uint32 last = m_api_receivers_count; - - Uint32 row; - NdbReceiver * tRec; - NdbRecAttr * tRecAttr; - if(idx < last && (tRec = m_api_receivers[idx]) - && ((row = tRec->m_current_row) <= tRec->m_defined_rows) - && (tRecAttr = tRec->m_rows[row-1])){ - - if(m_keyInfo) - tRecAttr = tRecAttr->next(); - Uint32 ret = *(Uint32*)tRecAttr->aRef(); - return ret; - } + if(m_keyInfo) + tRecAttr = tRecAttr->next(); + Uint32 ret = *(Uint32*)tRecAttr->aRef(); + return ret; } return -1; } diff --git a/ndb/test/tools/hugoScanRead.cpp b/ndb/test/tools/hugoScanRead.cpp index 42180207a8a..d6cbc00164a 100644 --- a/ndb/test/tools/hugoScanRead.cpp +++ b/ndb/test/tools/hugoScanRead.cpp @@ -85,8 +85,7 @@ int main(int argc, const char** argv){ if(!pIdx) ndbout << " Index " << argv[optind+1] << " not found" << endl; else - if(pIdx->getType() != NdbDictionary::Index::UniqueOrderedIndex && - pIdx->getType() != NdbDictionary::Index::OrderedIndex) + if(pIdx->getType() != NdbDictionary::Index::OrderedIndex) { ndbout << " Index " << argv[optind+1] << " is not scannable" << endl; pIdx = 0; diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 7a4da55f851..be99a081bcb 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -41,7 +41,7 @@ c_hk="" i_ht="" c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls="" i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls="" -c_p="" +c_p="" c_pp="" # Check for old tables if test ! -f $mdata/db.frm @@ -69,14 +69,17 @@ then c_d="$c_d Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d PRIMARY KEY Host (Host,Db,User)," c_d="$c_d KEY User (User)" c_d="$c_d ) engine=MyISAM" c_d="$c_d CHARACTER SET utf8 COLLATE utf8_bin" c_d="$c_d comment='Database privileges';" - i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y'); - INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');" + i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N'); + INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');" fi if test ! -f $mdata/host.frm @@ -141,6 +144,8 @@ then c_u="$c_u Repl_client_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_u="$c_u Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_u="$c_u Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u ssl_type enum('','ANY','X509', 'SPECIFIED') DEFAULT '' NOT NULL," c_u="$c_u ssl_cipher BLOB NOT NULL," c_u="$c_u x509_issuer BLOB NOT NULL," @@ -155,24 +160,24 @@ then if test "$1" = "test" then - i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); INSERT INTO user (host,user) values ('localhost',''); INSERT INTO user (host,user) values ('$hostname','');" else - i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);" + i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);" if test "$windows" = "0" then i_u="$i_u - INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); INSERT INTO user (host,user) values ('$hostname',''); INSERT INTO user (host,user) values ('localhost','');" else i_u="$i_u - INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);" + INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('%','','','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','N','','','','',0,0,0);" fi fi fi @@ -236,6 +241,27 @@ then c_c="$c_c comment='Column privileges';" fi +if test ! -f $mdata/procs_priv.frm +then + if test "$1" = "verbose" ; then + echo "Preparing procs_priv table" 1>&2; + fi + + c_pp="$c_pp CREATE TABLE procs_priv (" + c_pp="$c_pp Host char(60) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Db char(64) binary DEFAULT '' NOT NULL," + c_pp="$c_pp User char(16) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Routine_name char(64) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Grantor char(77) DEFAULT '' NOT NULL," + c_pp="$c_pp Timestamp timestamp(14)," + c_pp="$c_pp Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL," + c_pp="$c_pp PRIMARY KEY (Host,Db,User,Routine_name)," + c_pp="$c_pp KEY Grantor (Grantor)" + c_pp="$c_pp ) engine=MyISAM" + c_pp="$c_pp CHARACTER SET utf8 COLLATE utf8_bin" + c_pp="$c_pp comment='Procedure privileges';" +fi + if test ! -f $mdata/help_topic.frm then if test "$1" = "verbose" ; then @@ -718,6 +744,7 @@ $c_tzls $i_tzls $c_p +$c_pp END_OF_DATA diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index cae6a1d07b9..d4f095f5201 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -15,6 +15,7 @@ ALTER TABLE host type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE func type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE columns_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE tables_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; +ALTER TABLE procs_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE user change Password Password char(41) binary not null default ''; ALTER TABLE user add File_priv enum('N','Y') NOT NULL; CREATE TABLE IF NOT EXISTS func ( @@ -170,9 +171,47 @@ ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Cre # UPDATE user SET Create_view_priv=Create_priv, Show_view_priv=Create_priv where user<>"" AND @hadCreateViewPriv = 0; +# +# +# +SET @hadCreateRoutinePriv:=0; +SELECT @hadCreateRoutinePriv:=1 FROM user WHERE Create_routine_priv LIKE '%'; + +# +# Create PROCEDUREs privileges (v5.0) +# +ALTER TABLE db ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv; +ALTER TABLE user ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv; + +# +# Alter PROCEDUREs privileges (v5.0) +# +ALTER TABLE db ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv; +ALTER TABLE user ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv; + +ALTER TABLE db ADD Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Alter_routine_priv; + +# +# Assign create/alter routine privileges to people who have create privileges +# +UPDATE user SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv where user<>"" AND @hadCreateRoutinePriv = 0; +UPDATE db SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv, Execute_priv=Select_priv where user<>"" AND @hadCreateRoutinePriv = 0; + # # Create some possible missing tables # +CREATE TABLE IF NOT EXISTS procs_priv ( +Host char(60) binary DEFAULT '' NOT NULL, +Db char(64) binary DEFAULT '' NOT NULL, +User char(16) binary DEFAULT '' NOT NULL, +Routine_name char(64) binary DEFAULT '' NOT NULL, +Grantor char(77) DEFAULT '' NOT NULL, +Timestamp timestamp(14), +Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL, +PRIMARY KEY (Host,Db,User,Routine_name), +KEY Grantor (Grantor) +) CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges'; + CREATE TABLE IF NOT EXISTS help_topic ( help_topic_id int unsigned not null, name varchar(64) not null, diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh index b4f59790e73..3be1320f0b0 100644 --- a/scripts/mysql_install_db.sh +++ b/scripts/mysql_install_db.sh @@ -3,7 +3,7 @@ # For a more info consult the file COPYRIGHT distributed with this file. # This scripts creates the privilege tables db, host, user, tables_priv, -# columns_priv in the mysql database, as well as the func table. +# columns_priv, procs_priv in the mysql database, as well as the func table. # # All unrecognized arguments to this script are passed to mysqld. diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index a15ff9321cb..c2bf501eca7 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -34,7 +34,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ -DDEFAULT_MONITORING_INTERVAL="5" \ - -DDEFAULT_PORT="33006" \ + -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc diff --git a/sql/derror.cc b/sql/derror.cc index 09f43d20044..4690e76d0e3 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -24,15 +24,43 @@ static bool read_texts(const char *file_name,const char ***point, uint error_messages); static void init_myfunc_errs(void); - /* Read messages from errorfile */ +/* + Read messages from errorfile. + + SYNOPSIS + init_errmessage() + + DESCRIPTION + This function can be called multiple times to reload the messages. + + RETURN + FALSE OK + TRUE Error +*/ bool init_errmessage(void) { + const char **errmsgs; DBUG_ENTER("init_errmessage"); - if (read_texts(ERRMSG_FILE,&my_errmsg[ERRMAPP],ER_ERROR_MESSAGES)) + /* + Get a pointer to the old error messages pointer array. + read_texts() tries to free it. + */ + errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); + + /* Read messages from file. */ + if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1)) DBUG_RETURN(TRUE); - errmesg=my_errmsg[ERRMAPP]; /* Init global variabel */ + + /* Register messages for use with my_error(). */ + if (my_error_register(errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST)) + { + x_free((gptr) errmsgs); + DBUG_RETURN(TRUE); + } + + errmesg= errmsgs; /* Init global variabel */ init_myfunc_errs(); /* Init myfunc messages */ DBUG_RETURN(FALSE); } @@ -148,20 +176,20 @@ static void init_myfunc_errs() init_glob_errs(); /* Initiate english errors */ if (!(specialflag & SPECIAL_ENGLISH)) { - globerrs[EE_FILENOTFOUND % ERRMOD] = ER(ER_FILE_NOT_FOUND); - globerrs[EE_CANTCREATEFILE % ERRMOD]= ER(ER_CANT_CREATE_FILE); - globerrs[EE_READ % ERRMOD] = ER(ER_ERROR_ON_READ); - globerrs[EE_WRITE % ERRMOD] = ER(ER_ERROR_ON_WRITE); - globerrs[EE_BADCLOSE % ERRMOD] = ER(ER_ERROR_ON_CLOSE); - globerrs[EE_OUTOFMEMORY % ERRMOD] = ER(ER_OUTOFMEMORY); - globerrs[EE_DELETE % ERRMOD] = ER(ER_CANT_DELETE_FILE); - globerrs[EE_LINK % ERRMOD] = ER(ER_ERROR_ON_RENAME); - globerrs[EE_EOFERR % ERRMOD] = ER(ER_UNEXPECTED_EOF); - globerrs[EE_CANTLOCK % ERRMOD] = ER(ER_CANT_LOCK); - globerrs[EE_DIR % ERRMOD] = ER(ER_CANT_READ_DIR); - globerrs[EE_STAT % ERRMOD] = ER(ER_CANT_GET_STAT); - globerrs[EE_GETWD % ERRMOD] = ER(ER_CANT_GET_WD); - globerrs[EE_SETWD % ERRMOD] = ER(ER_CANT_SET_WD); - globerrs[EE_DISK_FULL % ERRMOD] = ER(ER_DISK_FULL); + EE(EE_FILENOTFOUND) = ER(ER_FILE_NOT_FOUND); + EE(EE_CANTCREATEFILE) = ER(ER_CANT_CREATE_FILE); + EE(EE_READ) = ER(ER_ERROR_ON_READ); + EE(EE_WRITE) = ER(ER_ERROR_ON_WRITE); + EE(EE_BADCLOSE) = ER(ER_ERROR_ON_CLOSE); + EE(EE_OUTOFMEMORY) = ER(ER_OUTOFMEMORY); + EE(EE_DELETE) = ER(ER_CANT_DELETE_FILE); + EE(EE_LINK) = ER(ER_ERROR_ON_RENAME); + EE(EE_EOFERR) = ER(ER_UNEXPECTED_EOF); + EE(EE_CANTLOCK) = ER(ER_CANT_LOCK); + EE(EE_DIR) = ER(ER_CANT_READ_DIR); + EE(EE_STAT) = ER(ER_CANT_GET_STAT); + EE(EE_GETWD) = ER(ER_CANT_GET_WD); + EE(EE_SETWD) = ER(ER_CANT_SET_WD); + EE(EE_DISK_FULL) = ER(ER_DISK_FULL); } } diff --git a/sql/field.h b/sql/field.h index 6ce5cf2a526..9375fbc8d5a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -113,7 +113,7 @@ public: This trickery is used to decrease a number of malloc calls. */ virtual String *val_str(String*,String *)=0; - String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_flag); + String *val_int_as_str(String *val_buffer, my_bool unsigned_flag); virtual Item_result result_type () const=0; virtual Item_result cmp_type () const { return result_type(); } bool eq(Field *field) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index b13b8705fc5..b78da32357a 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -1873,7 +1873,7 @@ int ha_ndbcluster::filtered_scan(const byte *key, uint key_len, // Define scan filter if (field->real_type() == MYSQL_TYPE_STRING) - sf.eq(ndb_fieldnr, key_ptr, field_len); + sf.cmp(NdbScanFilter::COND_EQ, ndb_fieldnr, key_ptr, field_len); else { if (field_len == 8) @@ -4039,7 +4039,8 @@ ha_ndbcluster::ha_ndbcluster(TABLE *table_arg): HA_NULL_IN_KEY | HA_AUTO_PART_KEY | HA_NO_VARCHAR | - HA_NO_PREFIX_CHAR_KEYS), + HA_NO_PREFIX_CHAR_KEYS | + HA_NEED_READ_RANGE_BUFFER), m_share(0), m_use_write(FALSE), m_ignore_dup_key(FALSE), @@ -5030,18 +5031,16 @@ int ha_ndbcluster::write_ndb_file() DBUG_RETURN(error); } -#ifdef key_multi_range int -ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, - key_multi_range *ranges, +ha_ndbcluster::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, - handler_buffer *buffer) + HANDLER_BUFFER *buffer) { DBUG_ENTER("ha_ndbcluster::read_multi_range_first"); int res; - uint i; KEY* key_info= table->key_info + active_index; NDB_INDEX_TYPE index_type= get_index_type(active_index); ulong reclength= table->reclength; @@ -5065,8 +5064,9 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, /** * Copy arguments into member variables */ - multi_ranges= ranges; - multi_range_count= range_count; + m_multi_ranges= ranges; + multi_range_curr= ranges; + multi_range_end= ranges+range_count; multi_range_sorted= sorted; multi_range_buffer= buffer; @@ -5094,18 +5094,19 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, const NDBINDEX *idx= (NDBINDEX *) m_index[active_index].index; const NdbOperation* lastOp= m_active_trans->getLastDefinedOperation(); NdbIndexScanOperation* scanOp= 0; - for(i= 0; irange_flag |= UNIQUE_RANGE; if ((op= m_active_trans->getNdbOperation(tab)) && !op->readTuple(lm) && - !set_primary_key(op, ranges[i].start_key.key) && + !set_primary_key(op, multi_range_curr->start_key.key) && !define_read_attrs(curr, op) && - (op->setAbortOption(IgnoreError), true)) + (op->setAbortOption(AO_IgnoreError), true)) curr += reclength; else ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError()); @@ -5115,32 +5116,32 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, case UNIQUE_INDEX: sk: { - ranges[i].range_flag |= UNIQUE_RANGE; + multi_range_curr->range_flag |= UNIQUE_RANGE; if ((op= m_active_trans->getNdbIndexOperation(unique_idx, tab)) && !op->readTuple(lm) && - !set_index_key(op, key_info, ranges[i].start_key.key) && + !set_index_key(op, key_info, multi_range_curr->start_key.key) && !define_read_attrs(curr, op) && - (op->setAbortOption(IgnoreError), true)) + (op->setAbortOption(AO_IgnoreError), true)) curr += reclength; else ERR_RETURN(op ? op->getNdbError() : m_active_trans->getNdbError()); break; } case PRIMARY_KEY_ORDERED_INDEX: - if (ranges[i].start_key.length == key_info->key_length && - ranges[i].start_key.flag == HA_READ_KEY_EXACT) + if (multi_range_curr->start_key.length == key_info->key_length && + multi_range_curr->start_key.flag == HA_READ_KEY_EXACT) goto pk; goto range; case UNIQUE_ORDERED_INDEX: - if (ranges[i].start_key.length == key_info->key_length && - ranges[i].start_key.flag == HA_READ_KEY_EXACT && - !check_null_in_key(key_info, ranges[i].start_key.key, - ranges[i].start_key.length)) + if (multi_range_curr->start_key.length == key_info->key_length && + multi_range_curr->start_key.flag == HA_READ_KEY_EXACT && + !check_null_in_key(key_info, multi_range_curr->start_key.key, + multi_range_curr->start_key.length)) goto sk; goto range; case ORDERED_INDEX: range: - ranges[i].range_flag &= ~(uint)UNIQUE_RANGE; + multi_range_curr->range_flag &= ~(uint)UNIQUE_RANGE; if (scanOp == 0) { if (m_multi_cursor) @@ -5155,8 +5156,8 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, end_of_buffer -= reclength; } else if ((scanOp= m_active_trans->getNdbIndexScanOperation(idx, tab)) - && !scanOp->readTuples(lm, 0, parallelism, sorted, false, true) && - !define_read_attrs(end_of_buffer-reclength, scanOp)) + &&!scanOp->readTuples(lm, 0, parallelism, sorted, false, true) + &&!define_read_attrs(end_of_buffer-reclength, scanOp)) { m_multi_cursor= scanOp; m_multi_range_cursor_result_ptr= end_of_buffer-reclength; @@ -5167,14 +5168,15 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, m_active_trans->getNdbError()); } } - const key_range *keys[2]= { &ranges[i].start_key, &ranges[i].end_key }; - if ((res= set_bounds(scanOp, keys, i))) + const key_range *keys[2]= { &multi_range_curr->start_key, + &multi_range_curr->end_key }; + if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges))) DBUG_RETURN(res); break; } } - if (i != range_count) + if (multi_range_curr != multi_range_end) { /** * Mark that we're using entire buffer (even if might not) as @@ -5196,8 +5198,8 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, lastOp ? lastOp->next() : m_active_trans->getFirstDefinedOperation(); if (!(res= execute_no_commit_ie(this, m_active_trans))) { - multi_range_curr= 0; - m_multi_range_defined_count= i; + m_multi_range_defined= multi_range_curr; + multi_range_curr= ranges; m_multi_range_result_ptr= (byte*)buffer->buffer; DBUG_RETURN(read_multi_range_next(found_range_p)); } @@ -5211,7 +5213,7 @@ ha_ndbcluster::read_multi_range_first(key_multi_range **found_range_p, #endif int -ha_ndbcluster::read_multi_range_next(key_multi_range ** multi_range_found_p) +ha_ndbcluster::read_multi_range_next(KEY_MULTI_RANGE ** multi_range_found_p) { DBUG_ENTER("ha_ndbcluster::read_multi_range_next"); if (m_disable_multi_read) @@ -5223,9 +5225,9 @@ ha_ndbcluster::read_multi_range_next(key_multi_range ** multi_range_found_p) int range_no; ulong reclength= table->reclength; const NdbOperation* op= m_current_multi_operation; - for(;multi_range_curr < m_multi_range_defined_count; multi_range_curr++) + for(;multi_range_curr < m_multi_range_defined; multi_range_curr++) { - if (multi_ranges[multi_range_curr].range_flag & UNIQUE_RANGE) + if (multi_range_curr->range_flag & UNIQUE_RANGE) { if (op->getNdbError().code == 0) goto found_next; @@ -5257,13 +5259,14 @@ ha_ndbcluster::read_multi_range_next(key_multi_range ** multi_range_found_p) } range_no= m_multi_cursor->get_range_no(); - if (range_no == multi_range_curr) + uint current_range_no= multi_range_curr - m_multi_ranges; + if (range_no == current_range_no) { DBUG_MULTI_RANGE(4); // return current row goto found; } - else if (range_no > (int)multi_range_curr) + else if (range_no > (int)current_range_no) { DBUG_MULTI_RANGE(5); // wait with current row @@ -5308,16 +5311,15 @@ close_scan: } } - if (multi_range_curr == multi_range_count) + if (multi_range_curr == multi_range_end) DBUG_RETURN(HA_ERR_END_OF_FILE); /** * Read remaining ranges */ - uint left= multi_range_count - multi_range_curr; DBUG_RETURN(read_multi_range_first(multi_range_found_p, - multi_ranges + multi_range_curr, - left, + multi_range_curr, + multi_range_end - multi_range_curr, multi_range_sorted, multi_range_buffer)); @@ -5326,7 +5328,7 @@ found: * Found a record belonging to a scan */ m_active_cursor= m_multi_cursor; - * multi_range_found_p= multi_ranges + range_no; + * multi_range_found_p= m_multi_ranges + range_no; memcpy(table->record[0], m_multi_range_cursor_result_ptr, reclength); setup_recattr(m_active_cursor->getFirstRecAttr()); unpack_record(table->record[0]); @@ -5338,7 +5340,7 @@ found_next: * Found a record belonging to a pk/index op, * copy result and move to next to prepare for next call */ - * multi_range_found_p= multi_ranges + multi_range_curr; + * multi_range_found_p= multi_range_curr; memcpy(table->record[0], m_multi_range_result_ptr, reclength); setup_recattr(op->getFirstRecAttr()); unpack_record(table->record[0]); @@ -5372,7 +5374,6 @@ ha_ndbcluster::setup_recattr(const NdbRecAttr* curr) DBUG_RETURN(0); } -#endif /* Condition pushdown diff --git a/sql/ha_ndbcluster.h b/sql/ha_ndbcluster.h index 06aab6b537a..06d2b80f3f5 100644 --- a/sql/ha_ndbcluster.h +++ b/sql/ha_ndbcluster.h @@ -254,10 +254,10 @@ class ha_ndbcluster: public handler /** * Multi range stuff */ - int read_multi_range_first(struct key_multi_range **found_range_p, - struct key_multi_range *ranges, uint range_count, - bool sorted, struct handler_buffer *buffer); - int read_multi_range_next(struct key_multi_range **found_range_p); + int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE*ranges, uint range_count, + bool sorted, HANDLER_BUFFER *buffer); + int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); bool get_error_message(int error, String *buf); void info(uint); @@ -427,7 +427,8 @@ class ha_ndbcluster: public handler Ndb_cond_stack *m_cond_stack; bool m_disable_multi_read; byte *m_multi_range_result_ptr; - uint m_multi_range_defined_count; + KEY_MULTI_RANGE *m_multi_ranges; + KEY_MULTI_RANGE *m_multi_range_defined; const NdbOperation *m_current_multi_operation; NdbIndexScanOperation *m_multi_cursor; byte *m_multi_range_cursor_result_ptr; diff --git a/sql/handler.cc b/sql/handler.cc index e43f2c2e888..65b0f10d17c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -244,9 +244,99 @@ bool ha_caching_allowed(THD* thd, char* table_key, return 1; } + +/* + Register handler error messages for use with my_error(). + + SYNOPSIS + ha_init_errors() + + RETURN + 0 OK + != 0 Error +*/ + +static int ha_init_errors(void) +{ +#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg) + const char **errmsgs; + + /* Allocate a pointer array for the error message strings. */ + /* Zerofill it to avoid uninitialized gaps. */ + if (! (errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*), + MYF(MY_WME | MY_ZEROFILL)))) + return 1; + + /* Set the dedicated error messages. */ + SETMSG(HA_ERR_KEY_NOT_FOUND, ER(ER_KEY_NOT_FOUND)); + SETMSG(HA_ERR_FOUND_DUPP_KEY, ER(ER_DUP_KEY)); + SETMSG(HA_ERR_RECORD_CHANGED, "Update wich is recoverable"); + SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function"); + SETMSG(HA_ERR_CRASHED, ER(ER_NOT_KEYFILE)); + SETMSG(HA_ERR_WRONG_IN_RECORD, ER(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_OUT_OF_MEM, "Table handler out of memory"); + SETMSG(HA_ERR_NOT_A_TABLE, "Incorrect file format '%.64s'"); + SETMSG(HA_ERR_WRONG_COMMAND, "Command not supported"); + SETMSG(HA_ERR_OLD_FILE, ER(ER_OLD_KEYFILE)); + SETMSG(HA_ERR_NO_ACTIVE_RECORD, "No record read in update"); + SETMSG(HA_ERR_RECORD_DELETED, "Intern record deleted"); + SETMSG(HA_ERR_RECORD_FILE_FULL, ER(ER_RECORD_FILE_FULL)); + SETMSG(HA_ERR_INDEX_FILE_FULL, "No more room in index file '%.64s'"); + SETMSG(HA_ERR_END_OF_FILE, "End in next/prev/first/last"); + SETMSG(HA_ERR_UNSUPPORTED, ER(ER_ILLEGAL_HA)); + SETMSG(HA_ERR_TO_BIG_ROW, "Too big row"); + SETMSG(HA_WRONG_CREATE_OPTION, "Wrong create option"); + SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER(ER_DUP_UNIQUE)); + SETMSG(HA_ERR_UNKNOWN_CHARSET, "Can't open charset"); + SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER(ER_WRONG_MRG_TABLE)); + SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER(ER_CRASHED_ON_REPAIR)); + SETMSG(HA_ERR_CRASHED_ON_USAGE, ER(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER(ER_LOCK_WAIT_TIMEOUT)); + SETMSG(HA_ERR_LOCK_TABLE_FULL, ER(ER_LOCK_TABLE_FULL)); + SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION)); + SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK)); + SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN)); + SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW)); + SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED)); + SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name"); + SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size"); + SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'"); + SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR)); + SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine"); + + /* Register the error messages for use with my_error(). */ + return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); +} + + +/* + Unregister handler error messages. + + SYNOPSIS + ha_finish_errors() + + RETURN + 0 OK + != 0 Error +*/ + +static int ha_finish_errors(void) +{ + const char **errmsgs; + + /* Allocate a pointer array for the error message strings. */ + if (! (errmsgs= my_error_unregister(HA_ERR_FIRST, HA_ERR_LAST))) + return 1; + my_free((gptr) errmsgs, MYF(0)); + return 0; +} + + int ha_init() { int error= 0; + if (ha_init_errors()) + return 1; #ifdef HAVE_BERKELEY_DB if (have_berkeley_db == SHOW_OPTION_YES) { @@ -314,6 +404,8 @@ int ha_panic(enum ha_panic_function flag) if (have_ndbcluster == SHOW_OPTION_YES) error|=ndbcluster_end(); #endif + if (ha_finish_errors()) + error= 1; return error; } /* ha_panic */ @@ -1241,9 +1333,15 @@ void handler::print_error(int error, myf errflag) case HA_ERR_CRASHED: textno=ER_NOT_KEYFILE; break; + case HA_ERR_WRONG_IN_RECORD: + textno= ER_CRASHED_ON_USAGE; + break; case HA_ERR_CRASHED_ON_USAGE: textno=ER_CRASHED_ON_USAGE; break; + case HA_ERR_NOT_A_TABLE: + textno= error; + break; case HA_ERR_CRASHED_ON_REPAIR: textno=ER_CRASHED_ON_REPAIR; break; @@ -1262,6 +1360,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_RECORD_FILE_FULL: textno=ER_RECORD_FILE_FULL; break; + case HA_ERR_INDEX_FILE_FULL: + textno= errno; + break; case HA_ERR_LOCK_WAIT_TIMEOUT: textno=ER_LOCK_WAIT_TIMEOUT; break; @@ -1685,6 +1786,131 @@ int ha_table_exists(THD* thd, const char* db, const char* name) #endif +/* + Read the first row of a multi-range set. + + SYNOPSIS + read_multi_range_first() + found_range_p Returns a pointer to the element in 'ranges' that + corresponds to the returned row. + ranges An array of KEY_MULTI_RANGE range descriptions. + range_count Number of ranges in 'ranges'. + sorted If result should be sorted per key. + buffer A HANDLER_BUFFER for internal handler usage. + + NOTES + Record is read into table->record[0]. + *found_range_p returns a valid value only if read_multi_range_first() + returns 0. + Sorting is done within each range. If you want an overall sort, enter + 'ranges' with sorted ranges. + + RETURN + 0 OK, found a row + HA_ERR_END_OF_FILE No rows in range + # Error code +*/ + +int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, uint range_count, + bool sorted, HANDLER_BUFFER *buffer) +{ + int result= HA_ERR_END_OF_FILE; + DBUG_ENTER("handler::read_multi_range_first"); + multi_range_sorted= sorted; + multi_range_buffer= buffer; + + for (multi_range_curr= ranges, multi_range_end= ranges + range_count; + multi_range_curr < multi_range_end; + multi_range_curr++) + { + result= read_range_first(multi_range_curr->start_key.length ? + &multi_range_curr->start_key : 0, + multi_range_curr->end_key.length ? + &multi_range_curr->end_key : 0, + test(multi_range_curr->range_flag & EQ_RANGE), + multi_range_sorted); + if (result != HA_ERR_END_OF_FILE) + break; + } + + *found_range_p= multi_range_curr; + DBUG_PRINT("exit",("result %d", result)); + DBUG_RETURN(result); +} + + +/* + Read the next row of a multi-range set. + + SYNOPSIS + read_multi_range_next() + found_range_p Returns a pointer to the element in 'ranges' that + corresponds to the returned row. + + NOTES + Record is read into table->record[0]. + *found_range_p returns a valid value only if read_multi_range_next() + returns 0. + + RETURN + 0 OK, found a row + HA_ERR_END_OF_FILE No (more) rows in range + # Error code +*/ + +int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) +{ + int result; + DBUG_ENTER("handler::read_multi_range_next"); + + /* We should not be called after the last call returned EOF. */ + DBUG_ASSERT(multi_range_curr < multi_range_end); + + do + { + /* Save a call if there can be only one row in range. */ + if (multi_range_curr->range_flag != (UNIQUE_RANGE | EQ_RANGE)) + { + result= read_range_next(); + + /* On success or non-EOF errors jump to the end. */ + if (result != HA_ERR_END_OF_FILE) + break; + } + else + { + /* + We need to set this for the last range only, but checking this + condition is more expensive than just setting the result code. + */ + result= HA_ERR_END_OF_FILE; + } + + /* Try the next range(s) until one matches a record. */ + for (multi_range_curr++; + multi_range_curr < multi_range_end; + multi_range_curr++) + { + result= read_range_first(multi_range_curr->start_key.length ? + &multi_range_curr->start_key : 0, + multi_range_curr->end_key.length ? + &multi_range_curr->end_key : 0, + test(multi_range_curr->range_flag & EQ_RANGE), + multi_range_sorted); + if (result != HA_ERR_END_OF_FILE) + break; + } + } + while ((result == HA_ERR_END_OF_FILE) && + (multi_range_curr < multi_range_end)); + + *found_range_p= multi_range_curr; + DBUG_PRINT("exit",("handler::read_multi_range_next: result %d", result)); + DBUG_RETURN(result); +} + + /* Read first row between two ranges. Store ranges for future calls to read_range_next diff --git a/sql/handler.h b/sql/handler.h index 5a96db54381..c87586dcf4e 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -76,6 +76,7 @@ #define HA_FILE_BASED (1 << 26) #define HA_NO_VARCHAR (1 << 27) #define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */ +#define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */ /* bits in index_flags(index_number) for what you can do with index */ @@ -279,6 +280,21 @@ typedef struct st_ha_check_opt } HA_CHECK_OPT; +/* + This is a buffer area that the handler can use to store rows. + 'end_of_used_area' should be kept updated after calls to + read-functions so that other parts of the code can use the + remaining area (until next read calls is issued). +*/ + +typedef struct st_handler_buffer +{ + const byte *buffer; /* Buffer one can start using */ + const byte *buffer_end; /* End of buffer */ + byte *end_of_used_area; /* End of area that was used by handler */ +} HANDLER_BUFFER; + + class handler :public Sql_alloc { protected: @@ -313,6 +329,12 @@ public: time_t check_time; time_t update_time; + /* The following are for read_multi_range */ + bool multi_range_sorted; + KEY_MULTI_RANGE *multi_range_curr; + KEY_MULTI_RANGE *multi_range_end; + HANDLER_BUFFER *multi_range_buffer; + /* The following are for read_range() */ key_range save_end_range, *end_range; KEY_PART_INFO *range_key_part; @@ -424,6 +446,10 @@ public: virtual int index_next_same(byte *buf, const byte *key, uint keylen); virtual int index_read_last(byte * buf, const byte * key, uint key_len) { return (my_errno=HA_ERR_WRONG_COMMAND); } + virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, uint range_count, + bool sorted, HANDLER_BUFFER *buffer); + virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); virtual int read_range_first(const key_range *start_key, const key_range *end_key, bool eq_range, bool sorted); @@ -457,13 +483,6 @@ public: { return extra(operation); } virtual int reset() { return extra(HA_EXTRA_RESET); } virtual int external_lock(THD *thd, int lock_type) { return 0; } - /* - This is called to set transactional table lock to a table. - If the handler don't support this, then this function will - return HA_ERR_WRONG_COMMAND and MySQL will give - ER_ILLEGAL_HA error message. - */ - virtual int transactional_table_lock(THD *thd, int lock_type) {return HA_ERR_WRONG_COMMAND;} virtual void unlock_row() {} virtual int start_stmt(THD *thd) {return 0;} /* diff --git a/sql/item_func.cc b/sql/item_func.cc index dcb75fe8653..c15dfce8e43 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -22,6 +22,7 @@ #endif #include "mysql_priv.h" +#include "sql_acl.h" #include "slave.h" // for wait_for_master_pos #include #include @@ -3572,7 +3573,17 @@ Item_func_sp::execute(Item **itp) } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_procedure_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0)) + DBUG_RETURN(-1); sp_change_security_context(thd, m_sp, &save_ctx); + if (save_ctx.changed && + check_procedure_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0)) + { + sp_restore_security_context(thd, m_sp, &save_ctx); + DBUG_RETURN(-1); + } #endif /* diff --git a/sql/lex.h b/sql/lex.h index cf0059a1397..5b6c86bf0ed 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -399,6 +399,7 @@ static SYMBOL symbols[] = { { "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */ { "ROLLBACK", SYM(ROLLBACK_SYM)}, { "ROLLUP", SYM(ROLLUP_SYM)}, + { "ROUTINE", SYM(ROUTINE_SYM)}, { "ROW", SYM(ROW_SYM)}, { "ROWS", SYM(ROWS_SYM)}, { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 393cf4cf142..973e82b7b10 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -939,61 +939,3 @@ bool make_global_read_lock_block_commit(THD *thd) DBUG_RETURN(error); } -/* - Take transactional table lock for all tables in the list - - SYNOPSIS - transactional_lock_tables - thd Thread THD - tables list of tables - counter number of tables in the list - - NOTES - - RETURN - 0 - OK - -1 - error - -*/ -int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter) -{ - uint i; - int lock_type,error=0; - TABLE_LIST *table; - TABLE **start,**ptr; - - DBUG_ENTER("transactional_lock_tables"); - - if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*) * counter))) - return -1; - - for (table= tables; table; table= table->next_global) - { - if (!table->placeholder() && !table->schema_table) - *(ptr++)= table->table; - } - - for (i=1 ; i <= counter ; i++, start++) - { - DBUG_ASSERT((*start)->reginfo.lock_type >= TL_READ); - lock_type=F_WRLCK; /* Lock exclusive */ - - if ((*start)->db_stat & HA_READ_ONLY || - ((*start)->reginfo.lock_type >= TL_READ && - (*start)->reginfo.lock_type <= TL_READ_NO_INSERT)) - lock_type=F_RDLCK; - - if ((error=(*start)->file->transactional_table_lock(thd, lock_type))) - { - print_lock_error(error, (*start)->file->table_type()); - DBUG_RETURN(-1); - } - else - { - (*start)->db_stat &= ~ HA_BLOCK_LOCK; - (*start)->current_lock= lock_type; - } - } - - DBUG_RETURN(0); -} diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2fc82e05f31..d69669d097a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -420,6 +420,8 @@ void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0, TABLE *stopper= 0); bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); +bool check_procedure_access(THD *thd,ulong want_access,char *db,char *name, + bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); @@ -828,7 +830,6 @@ int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); bool open_and_lock_tables(THD *thd,TABLE_LIST *tables); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); -int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); bool rm_temporary_table(enum db_type base, char *path); @@ -1024,6 +1025,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_readonly, lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm; extern my_bool opt_secure_auth; +extern my_bool sp_automatic_privileges; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index e5ff53ceab6..f89cc923c97 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -299,6 +299,7 @@ my_bool opt_innodb_safe_binlog= 0; my_bool opt_large_pages= 0; uint opt_large_page_size= 0; volatile bool mqh_used = 0; +my_bool sp_automatic_privileges= 1; uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; uint delay_key_write_options, protocol_version; @@ -1004,7 +1005,9 @@ void clean_up(bool print_message) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif - x_free((gptr) my_errmsg[ERRMAPP]); /* Free messages */ + finish_client_errs(); + const char **errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); + x_free((gptr) errmsgs); /* Free messages */ DBUG_PRINT("quit", ("Error messages freed")); /* Tell main we are ready */ (void) pthread_mutex_lock(&LOCK_thread_count); @@ -4141,7 +4144,7 @@ enum options_mysqld OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, - OPT_MAX_ERROR_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, + OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, @@ -4200,6 +4203,7 @@ enum options_mysqld OPT_OPTIMIZER_SEARCH_DEPTH, OPT_OPTIMIZER_PRUNE_LEVEL, OPT_UPDATABLE_VIEWS_WITH_LIMIT, + OPT_SP_AUTOMATIC_PRIVILEGES, OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET, OPT_ENABLE_LARGE_PAGES }; @@ -4230,6 +4234,10 @@ struct my_option my_long_options[] = (gptr*) &global_system_variables.auto_increment_offset, (gptr*) &max_system_variables.auto_increment_offset, 0, GET_ULONG, OPT_ARG, 1, 1, 65535, 0, 1, 0 }, + {"automatic-sp-privileges", OPT_SP_AUTOMATIC_PRIVILEGES, + "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.", + (gptr*) &sp_automatic_privileges, (gptr*) &sp_automatic_privileges, + 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"basedir", 'b', "Path to installation directory. All paths are usually resolved relative to this.", (gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG, @@ -5138,6 +5146,11 @@ The minimum value for this variable is 4096.", "After this many write locks, allow some read locks to run in between.", (gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG, REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"multi_range_count", OPT_MULTI_RANGE_COUNT, + "Number of key ranges to request at once.", + (gptr*) &global_system_variables.multi_range_count, + (gptr*) &max_system_variables.multi_range_count, 0, + GET_ULONG, REQUIRED_ARG, 256, 1, ~0L, 0, 1, 0}, {"myisam_block_size", OPT_MYISAM_BLOCK_SIZE, "Block size to be used for MyISAM index pages.", (gptr*) &opt_myisam_block_size, @@ -6135,6 +6148,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; myisam_concurrent_insert=0; myisam_recover_options= HA_RECOVER_NONE; + sp_automatic_privileges=0; my_use_symdir=0; ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE); #ifdef HAVE_QUERY_CACHE diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e47c7e147a7..d5d67212c41 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -712,7 +712,7 @@ QUICK_SELECT_I::QUICK_SELECT_I() QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc) - :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0) + :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0),in_range(0) { sorted= 0; index= key_nr; @@ -720,6 +720,13 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, key_part_info= head->key_info[index].key_part; my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16); + /* 'thd' is not accessible in QUICK_RANGE_SELECT::get_next_init(). */ + multi_range_bufsiz= thd->variables.read_rnd_buff_size; + multi_range_count= thd->variables.multi_range_count; + multi_range_length= 0; + multi_range= NULL; + multi_range_buff= NULL; + if (!no_alloc && !parent_alloc) { // Allocates everything through the internal memroot @@ -736,6 +743,10 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, int QUICK_RANGE_SELECT::init() { DBUG_ENTER("QUICK_RANGE_SELECT::init"); + + if ((error= get_next_init())) + DBUG_RETURN(error); + if (file->inited == handler::NONE) DBUG_RETURN(error= file->ha_index_init(index)); error= 0; @@ -771,6 +782,10 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() delete_dynamic(&ranges); /* ranges are allocated in alloc */ free_root(&alloc,MYF(0)); } + if (multi_range) + my_free((char*) multi_range, MYF(0)); + if (multi_range_buff) + my_free((char*) multi_range_buff, MYF(0)); DBUG_VOID_RETURN; } @@ -5872,58 +5887,178 @@ int QUICK_ROR_UNION_SELECT::get_next() DBUG_RETURN(error); } - /* get next possible record using quick-struct */ + +/* + Initialize data structures needed by get_next(). + + SYNOPSIS + QUICK_RANGE_SELECT::get_next_init() + + DESCRIPTION + This is called from get_next() at its first call for an object. + It allocates memory buffers and sets size variables. + + RETURN + 0 OK. + != 0 Error. +*/ + +int QUICK_RANGE_SELECT::get_next_init(void) +{ + uint mrange_bufsiz; + byte *mrange_buff; + DBUG_ENTER("QUICK_RANGE_SELECT::get_next_init"); + + /* Do not allocate the buffers twice. */ + if (multi_range_length) + { + DBUG_ASSERT(multi_range_length == min(multi_range_count, ranges.elements)); + DBUG_RETURN(0); + } + + /* If the ranges are not yet initialized, wait for the next call. */ + if (! ranges.elements) + { + DBUG_RETURN(0); + } + + /* + Allocate the ranges array. + */ + multi_range_length= min(multi_range_count, ranges.elements); + DBUG_ASSERT(multi_range_length > 0); + while (multi_range_length && ! (multi_range= (KEY_MULTI_RANGE*) + my_malloc(multi_range_length * + sizeof(KEY_MULTI_RANGE), + MYF(MY_WME)))) + { + /* Try to shrink the buffers until it is 0. */ + multi_range_length/= 2; + } + if (! multi_range) + { + multi_range_length= 0; + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + /* + Allocate the handler buffer if necessary. + */ + if (file->table_flags() & HA_NEED_READ_RANGE_BUFFER) + { + mrange_bufsiz= min(multi_range_bufsiz, + (QUICK_SELECT_I::records + 1)* head->reclength); + + while (mrange_bufsiz && + ! my_multi_malloc(MYF(MY_WME), + &multi_range_buff, sizeof(*multi_range_buff), + &mrange_buff, mrange_bufsiz, + NullS)) + { + /* Try to shrink the buffers until both are 0. */ + mrange_bufsiz/= 2; + } + if (! multi_range_buff) + { + my_free((char*) multi_range, MYF(0)); + multi_range= NULL; + multi_range_length= 0; + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + /* Initialize the handler buffer. */ + multi_range_buff->buffer= mrange_buff; + multi_range_buff->buffer_end= mrange_buff + mrange_bufsiz; + multi_range_buff->end_of_used_area= mrange_buff; + } + + /* Initialize the current QUICK_RANGE pointer. */ + cur_range= (QUICK_RANGE**) ranges.buffer; + DBUG_RETURN(0); +} + + +/* + Get next possible record using quick-struct. + + SYNOPSIS + QUICK_RANGE_SELECT::get_next() + + NOTES + Record is read into table->record[0] + + RETURN + 0 Found row + HA_ERR_END_OF_FILE No (more) rows in range + # Error code +*/ int QUICK_RANGE_SELECT::get_next() { + int result; + KEY_MULTI_RANGE *mrange; + key_range *start_key; + key_range *end_key; DBUG_ENTER("QUICK_RANGE_SELECT::get_next"); + DBUG_ASSERT(multi_range_length && multi_range && + (cur_range >= (QUICK_RANGE**) ranges.buffer) && + (cur_range <= (QUICK_RANGE**) ranges.buffer + ranges.elements)); for (;;) { - int result; - key_range start_key, end_key; - if (range) + if (in_range) { - // Already read through key - result= file->read_range_next(); + /* We did already start to read this key. */ + result= file->read_multi_range_next(&mrange); if (result != HA_ERR_END_OF_FILE) + { + in_range= ! result; DBUG_RETURN(result); + } } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); + uint count= min(multi_range_length, ranges.elements - + (cur_range - (QUICK_RANGE**) ranges.buffer)); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + in_range= FALSE; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + KEY_MULTI_RANGE *mrange_slot, *mrange_end; + for (mrange_slot= multi_range, mrange_end= mrange_slot+count; + mrange_slot < mrange_end; + mrange_slot++) + { + start_key= &mrange_slot->start_key; + end_key= &mrange_slot->end_key; + range= *(cur_range++); - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + start_key->key= (const byte*) range->min_key; + start_key->length= range->min_length; + start_key->flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : + (range->flag & EQ_RANGE) ? + HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); + end_key->key= (const byte*) range->max_key; + end_key->length= range->max_length; + /* + We use HA_READ_AFTER_KEY here because if we are reading on a key + prefix. We want to find all keys with this prefix. + */ + end_key->flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : + HA_READ_AFTER_KEY); - start_key.key= (const byte*) range->min_key; - start_key.length= range->min_length; - start_key.flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (range->flag & EQ_RANGE) ? - HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - end_key.key= (const byte*) range->max_key; - end_key.length= range->max_length; - /* - We use READ_AFTER_KEY here because if we are reading on a key - prefix we want to find all keys with this prefix - */ - end_key.flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : - HA_READ_AFTER_KEY); - - result= file->read_range_first(range->min_length ? &start_key : 0, - range->max_length ? &end_key : 0, - test(range->flag & EQ_RANGE), - sorted); - if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) - range=0; // Stop searching + mrange_slot->range_flag= range->flag; + } + result= file->read_multi_range_first(&mrange, multi_range, count, + sorted, multi_range_buff); if (result != HA_ERR_END_OF_FILE) + { + in_range= ! result; DBUG_RETURN(result); - range=0; // No matching rows; go to next range + } + in_range= FALSE; /* No matching rows; go to next set of ranges. */ } } @@ -5974,15 +6109,14 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix) DBUG_RETURN(result); } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); /* First range. */ - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); /* Next range. */ - - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + range= 0; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + range= *(cur_range++); start_key.key= (const byte*) range->min_key; start_key.length= min(range->min_length, prefix_length); @@ -6030,15 +6164,14 @@ int QUICK_RANGE_SELECT_GEOM::get_next() DBUG_RETURN(result); } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); - - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + range= 0; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + range= *(cur_range++); result= file->index_read(record, (byte*) range->min_key, diff --git a/sql/opt_range.h b/sql/opt_range.h index 74d388128c8..71981dfb5c7 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -24,16 +24,6 @@ #pragma interface /* gcc class implementation */ #endif -#define NO_MIN_RANGE 1 -#define NO_MAX_RANGE 2 -#define NEAR_MIN 4 -#define NEAR_MAX 8 -#define UNIQUE_RANGE 16 -#define EQ_RANGE 32 -#define NULL_RANGE 64 -#define GEOM_FLAG 128 - - typedef struct st_key_part { uint16 key,part, store_length, length; uint8 null_bit; @@ -135,9 +125,24 @@ public: */ virtual int reset(void) = 0; + /* + Initialize get_next() for row retrieval. + SYNOPSIS + get_next_init() + + get_next_init() must be called before the first get_next(). + If get_next_init() call fails get_next() must not be called. + + RETURN + 0 OK + other Error code + */ + virtual int get_next_init() { return false; } + virtual int get_next() = 0; /* get next record to retrieve */ + /* Range end should be called when we have looped over the whole index */ virtual void range_end() {} - virtual int get_next() = 0; /* get next record to retrieve */ + virtual bool reverse_sorted() = 0; virtual bool unique_key_range() { return false; } @@ -236,6 +241,14 @@ protected: closed no later then this quick select is deleted. */ bool free_file; + bool in_range; + uint multi_range_count; /* copy from thd->variables.multi_range_count */ + uint multi_range_length; /* the allocated length for the array */ + uint multi_range_bufsiz; /* copy from thd->variables.read_rnd_buff_size */ + KEY_MULTI_RANGE *multi_range; /* the multi-range array (allocated and + freed by QUICK_RANGE_SELECT) */ + HANDLER_BUFFER *multi_range_buff; /* the handler buffer (allocated and + freed by QUICK_RANGE_SELECT) */ protected: friend class TRP_ROR_INTERSECT; @@ -270,18 +283,19 @@ public: MEM_ROOT *parent_alloc=NULL); ~QUICK_RANGE_SELECT(); + int init(); int reset(void) { next=0; range= NULL; - cur_range= NULL; + cur_range= (QUICK_RANGE**) ranges.buffer; /* Note: in opt_range.cc there are places where it is assumed that this function always succeeds */ return 0; } - int init(); + int get_next_init(void); int get_next(); void range_end(); int get_next_prefix(uint prefix_length, byte *cur_prefix); @@ -296,6 +310,13 @@ public: #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif + QUICK_RANGE_SELECT(const QUICK_RANGE_SELECT& org) : QUICK_SELECT_I() + { + bcopy(&org, this, sizeof(*this)); + multi_range_length= 0; + multi_range= NULL; + multi_range_buff= NULL; + } }; diff --git a/sql/records.cc b/sql/records.cc index 5963c36afd9..3c0143d2307 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -100,11 +100,19 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } else if (select && select->quick) { + int error; DBUG_PRINT("info",("using rr_quick")); if (!table->file->inited) table->file->ha_index_init(select->quick->index); info->read_record=rr_quick; + + if ((error= select->quick->get_next_init())) + { + /* Cannot return error code here. Instead print to error log. */ + table->file->print_error(error,MYF(ME_NOREFRESH)); + thd->fatal_error(); + } } else if (table->sort.record_pointers) { diff --git a/sql/set_var.cc b/sql/set_var.cc index 57f5cf2ddb1..0b73d31d9b9 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -133,6 +133,9 @@ sys_var_thd_ulong sys_auto_increment_increment("auto_increment_increment", sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset", &SV::auto_increment_offset); +sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges", + &sp_automatic_privileges); + sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", &binlog_cache_size); sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", @@ -256,6 +259,8 @@ sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables", &SV::max_tmp_tables); sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count", &max_write_lock_count); +sys_var_thd_ulong sys_multi_range_count("multi_range_count", + &SV::multi_range_count); sys_var_long_ptr sys_myisam_data_pointer_size("myisam_data_pointer_size", &myisam_data_pointer_size); sys_var_thd_ulonglong sys_myisam_max_extra_sort_file_size("myisam_max_extra_sort_file_size", &SV::myisam_max_extra_sort_file_size, fix_myisam_max_extra_sort_file_size, 1); @@ -512,6 +517,7 @@ sys_var *sys_variables[]= &sys_auto_increment_increment, &sys_auto_increment_offset, &sys_autocommit, + &sys_automatic_sp_privileges, &sys_big_tables, &sys_big_selects, &sys_binlog_cache_size, @@ -672,6 +678,7 @@ sys_var *sys_variables[]= struct show_var_st init_vars[]= { {"auto_increment_increment", (char*) &sys_auto_increment_increment, SHOW_SYS}, {"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS}, + {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS}, {"back_log", (char*) &back_log, SHOW_LONG}, {"basedir", mysql_home, SHOW_CHAR}, #ifdef HAVE_BERKELEY_DB diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index c93e203f0d5..5b48f27d2e3 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5168,8 +5168,8 @@ ER_VIEW_CHECK_FAILED eng "CHECK OPTION failed '%-.64s.%-.64s'" rus "ÐÒÏ×ÅÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÐÒÏ×ÁÌÉÌÁÓØ" ukr "ðÅÒÅצÒËÁ CHECK OPTION ÄÌÑ VIEW '%-.64s.%-.64s' ÎÅ ÐÒÏÊÛÌÁ" -ER_SP_ACCESS_DENIED_ERROR 42000 - eng "Access denied; you are not the procedure/function definer of '%s'" +ER_PROCACCESS_DENIED_ERROR 42000 + eng "%-.16s command denied to user '%-.32s'@'%-.64s' for routine '%-.64s'" ER_RELAY_LOG_FAIL eng "Failed purging old relay logs: %s" ER_PASSWD_LENGTH @@ -5232,3 +5232,9 @@ ER_CANNOT_USER eng "Operation %s failed for %.256s" ger "Das Kommando %s scheiterte für %.256s" norwegian-ny "Operation %s failed for '%.256s'" +ER_NONEXISTING_PROC_GRANT 42000 + eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on routine '%-.64s'" +ER_PROC_AUTO_GRANT_FAIL + eng "Failed to grant EXECUTE and ALTER ROUTINE privileges" +ER_PROC_AUTO_REVOKE_FAIL + eng "Failed to revoke all privileges to dropped routine" diff --git a/sql/sp.cc b/sql/sp.cc index 4605d49f3ab..88693de0497 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -738,6 +738,45 @@ sp_find_procedure(THD *thd, sp_name *name) } +int +sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) +{ + TABLE_LIST *table; + bool result= 0; + DBUG_ENTER("sp_exists_routine"); + for (table= tables; table; table= table->next_global) + { + sp_name *name; + LEX_STRING lex_db; + LEX_STRING lex_name; + lex_db.length= strlen(table->db); + lex_name.length= strlen(table->real_name); + lex_db.str= thd->strmake(table->db, lex_db.length); + lex_name.str= thd->strmake(table->real_name, lex_name.length); + name= new sp_name(lex_db, lex_name); + name->init_qname(thd); + if (sp_find_procedure(thd, name) != NULL || + sp_find_function(thd, name) != NULL) + { + if (any) + DBUG_RETURN(1); + result= 1; + } + else if (!any) + { + if (!no_error) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE", + table->real_name); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); + } + } + DBUG_RETURN(result); +} + + int sp_create_procedure(THD *thd, sp_head *sp) { diff --git a/sql/sp.h b/sql/sp.h index acdfe824b97..152c59d0d02 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -36,6 +36,9 @@ sp_drop_db_routines(THD *thd, char *db); sp_head * sp_find_procedure(THD *thd, sp_name *name); +int +sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error); + int sp_create_procedure(THD *thd, sp_head *sp); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d33faffb2c2..2de3fc4a711 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -33,6 +33,8 @@ #endif #include #include +#include "sp_head.h" +#include "sp.h" #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -59,7 +61,7 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; -static HASH acl_check_hosts, column_priv_hash; +static HASH acl_check_hosts, column_priv_hash, proc_priv_hash; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; @@ -307,6 +309,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) */ if (table->fields <= 31 && (user.access & CREATE_ACL)) user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); + + /* + if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on + CREATE PROCEDURE & ALTER PROCEDURE privileges + */ + if (table->fields <= 33 && (user.access & CREATE_ACL)) + user.access|= CREATE_PROC_ACL; + if (table->fields <= 33 && (user.access & ALTER_ACL)) + user.access|= ALTER_PROC_ACL; + user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); @@ -1859,13 +1871,25 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length, } -class GRANT_TABLE :public Sql_alloc +class GRANT_NAME :public Sql_alloc { public: char *host,*db, *user, *tname, *hash_key, *orig_host; - ulong privs, cols; + ulong privs; ulong sort; uint key_length; + GRANT_NAME(const char *h, const char *d,const char *u, + const char *t, ulong p); + GRANT_NAME (TABLE *form); + virtual ~GRANT_NAME() {}; + virtual bool ok() { return privs != 0; } +}; + + +class GRANT_TABLE :public GRANT_NAME +{ +public: + ulong cols; HASH hash_columns; GRANT_TABLE(const char *h, const char *d,const char *u, @@ -1877,9 +1901,9 @@ public: -GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, - const char *t, ulong p, ulong c) - :privs(p), cols(c) +GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, + const char *t, ulong p) + :privs(p) { /* Host given by user */ orig_host= strdup_root(&memex,h); @@ -1897,15 +1921,20 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); +} + + +GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, + const char *t, ulong p, ulong c) + :GRANT_NAME(h,d,u,t,p), cols(c) +{ (void) hash_init(&hash_columns,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); } -GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) +GRANT_NAME::GRANT_NAME(TABLE *form) { - byte key[MAX_KEY_LENGTH]; - orig_host= host= get_field(&memex, form->field[0]); db= get_field(&memex,form->field[1]); user= get_field(&memex,form->field[2]); @@ -1921,8 +1950,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) if (!db || !tname) { /* Wrong table row; Ignore it */ - hash_clear(&hash_columns); /* allow for destruction */ - privs= cols= 0; /* purecov: inspected */ + privs= 0; return; /* purecov: inspected */ } if (lower_case_table_names) @@ -1935,8 +1963,23 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (ulong) form->field[6]->val_int(); - cols = (ulong) form->field[7]->val_int(); privs = fix_rights_for_table(privs); +} + + +GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) + :GRANT_NAME(form) +{ + byte key[MAX_KEY_LENGTH]; + + if (!db || !tname) + { + /* Wrong table row; Ignore it */ + hash_clear(&hash_columns); /* allow for destruction */ + cols= 0; + return; + } + cols= (ulong) form->field[7]->val_int(); cols = fix_rights_for_column(cols); (void) hash_init(&hash_columns,system_charset_info, @@ -1995,7 +2038,7 @@ GRANT_TABLE::~GRANT_TABLE() } -static byte* get_grant_table(GRANT_TABLE *buff,uint *length, +static byte* get_grant_table(GRANT_NAME *buff,uint *length, my_bool not_used __attribute__((unused))) { *length=buff->key_length; @@ -2011,44 +2054,62 @@ void free_grant_table(GRANT_TABLE *grant_table) /* Search after a matching grant. Prefer exact grants before not exact ones */ -static GRANT_TABLE *table_hash_search(const char *host,const char* ip, +static GRANT_NAME *name_hash_search(HASH *name_hash, + const char *host,const char* ip, const char *db, const char *user, const char *tname, bool exact) { char helping [NAME_LEN*2+USERNAME_LENGTH+3]; uint len; - GRANT_TABLE *grant_table,*found=0; + GRANT_NAME *grant_name,*found=0; len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; - for (grant_table=(GRANT_TABLE*) hash_search(&column_priv_hash, + for (grant_name=(GRANT_NAME*) hash_search(name_hash, (byte*) helping, len) ; - grant_table ; - grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping, + grant_name ; + grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping, len)) { if (exact) { if ((host && - !my_strcasecmp(system_charset_info, host, grant_table->host)) || - (ip && !strcmp(ip,grant_table->host))) - return grant_table; + !my_strcasecmp(system_charset_info, host, grant_name->host)) || + (ip && !strcmp(ip,grant_name->host))) + return grant_name; } else { if (((host && !wild_case_compare(system_charset_info, - host,grant_table->host)) || + host,grant_name->host)) || (ip && !wild_case_compare(system_charset_info, - ip,grant_table->host))) && - (!found || found->sort < grant_table->sort)) - found=grant_table; // Host ok + ip,grant_name->host))) && + (!found || found->sort < grant_name->sort)) + found=grant_name; // Host ok } } return found; } +inline GRANT_NAME * +proc_hash_search(const char *host, const char *ip, const char *db, + const char *user, const char *tname, bool exact) +{ + return (GRANT_TABLE*) name_hash_search(&proc_priv_hash, host, ip, db, + user, tname, exact); +} + + +inline GRANT_TABLE * +table_hash_search(const char *host, const char *ip, const char *db, + const char *user, const char *tname, bool exact) +{ + return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db, + user, tname, exact); +} + inline GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, uint length) @@ -2358,6 +2419,117 @@ table_error: } +static int replace_proc_table(THD *thd, GRANT_NAME *grant_name, + TABLE *table, const LEX_USER &combo, + const char *db, const char *proc_name, + ulong rights, bool revoke_grant) +{ + char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + int old_row_exists= 1; + int error=0; + ulong store_proc_rights; + DBUG_ENTER("replace_proc_table"); + + if (!initialized) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(-1); + } + + strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); + + /* + The following should always succeed as new users are created before + this function is called! + */ + if (!find_acl_user(combo.host.str,combo.user.str)) + { + my_error(ER_PASSWORD_NO_MATCH,MYF(0)); + DBUG_RETURN(-1); + } + + restore_record(table,default_values); // Get empty record + table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); + table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); + table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); + table->field[3]->store(proc_name,(uint) strlen(proc_name), &my_charset_latin1); + store_record(table,record[1]); // store at pos 1 + + if (table->file->index_read_idx(table->record[0],0, + (byte*) table->field[0]->ptr,0, + HA_READ_KEY_EXACT)) + { + /* + The following should never happen as we first check the in memory + grant tables for the user. There is however always a small change that + the user has modified the grant tables directly. + */ + if (revoke_grant) + { // no row, no revoke + my_error(ER_NONEXISTING_PROC_GRANT, MYF(0), + combo.user.str, combo.host.str, proc_name); + DBUG_RETURN(-1); + } + old_row_exists= 0; + restore_record(table,record[1]); // Get saved record + } + + store_proc_rights= get_rights_for_procedure(rights); + if (old_row_exists) + { + ulong j; + store_record(table,record[1]); + j= (ulong) table->field[6]->val_int(); + + if (revoke_grant) + { + /* column rights are already fixed in mysql_table_grant */ + store_proc_rights=j & ~store_proc_rights; + } + else + { + store_proc_rights|= j; + } + } + + table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1); + table->field[6]->store((longlong) store_proc_rights); + rights=fix_rights_for_procedure(store_proc_rights); + + if (old_row_exists) + { + if (store_proc_rights) + { + if ((error=table->file->update_row(table->record[1],table->record[0]))) + goto table_error; + } + else if ((error= table->file->delete_row(table->record[1]))) + goto table_error; + } + else + { + error=table->file->write_row(table->record[0]); + if (error && error != HA_ERR_FOUND_DUPP_KEY) + goto table_error; + } + + if (rights) + { + grant_name->privs= rights; + } + else + { + hash_delete(&proc_priv_hash,(byte*) grant_name); + } + DBUG_RETURN(0); + + /* This should never happen */ +table_error: + table->file->print_error(error,MYF(0)); + DBUG_RETURN(-1); +} + + /* Store table level and column level grants in the privilege tables @@ -2601,6 +2773,161 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, } +/* + Store procedure level grants in the privilege tables + + SYNOPSIS + mysql_procedure_grant() + thd Thread handle + table_list List of procedures to give grant + user_list List of users to give grant + rights Table level grant + revoke_grant Set to 1 if this is a REVOKE command + + RETURN + 0 ok + 1 error +*/ + +bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list, + List &user_list, ulong rights, + bool revoke_grant, bool no_error) +{ + List_iterator str_list (user_list); + LEX_USER *Str; + TABLE_LIST tables[2]; + bool create_new_users=0, result=0; + char *db_name, *real_name; + DBUG_ENTER("mysql_procedure_grant"); + + if (!initialized) + { + if (!no_error) + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-grant-tables"); + DBUG_RETURN(TRUE); + } + if (rights & ~PROC_ACLS) + { + if (!no_error) + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), + MYF(0)); + DBUG_RETURN(TRUE); + } + + if (!revoke_grant) + { + if (sp_exists_routine(thd, table_list, 0, no_error)<0) + DBUG_RETURN(TRUE); + } + + /* open the mysql.user and mysql.procs_priv tables */ + + bzero((char*) &tables,sizeof(tables)); + tables[0].alias=tables[0].real_name= (char*) "user"; + tables[1].alias=tables[1].real_name= (char*) "procs_priv"; + tables[0].next_local= tables[0].next_global= tables+1; + tables[0].lock_type=tables[1].lock_type=TL_WRITE; + tables[0].db=tables[1].db=(char*) "mysql"; + +#ifdef HAVE_REPLICATION + /* + GRANT and REVOKE are applied the slave in/exclusion rules as they are + some kind of updates to the mysql.% tables. + */ + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(FALSE); + } +#endif + + if (simple_open_n_lock_tables(thd,tables)) + { // Should never happen + close_thread_tables(thd); + DBUG_RETURN(TRUE); + } + + if (!revoke_grant) + create_new_users= test_if_create_new_users(thd); + rw_wrlock(&LOCK_grant); + MEM_ROOT *old_root= thd->mem_root; + thd->mem_root= &memex; + + DBUG_PRINT("info",("now time to iterate and add users")); + + while ((Str= str_list++)) + { + int error; + GRANT_NAME *grant_name; + if (Str->host.length > HOSTNAME_LENGTH || + Str->user.length > USERNAME_LENGTH) + { + if (!no_error) + my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), + MYF(0)); + result= TRUE; + continue; + } + /* Create user if needed */ + pthread_mutex_lock(&acl_cache->lock); + error=replace_user_table(thd, tables[0].table, *Str, + 0, revoke_grant, create_new_users); + pthread_mutex_unlock(&acl_cache->lock); + if (error) + { + result= TRUE; // Remember error + continue; // Add next user + } + + db_name= table_list->db; + real_name= table_list->real_name; + + grant_name= proc_hash_search(Str->host.str, NullS, db_name, + Str->user.str, real_name, 1); + if (!grant_name) + { + if (revoke_grant) + { + if (!no_error) + my_error(ER_NONEXISTING_PROC_GRANT, MYF(0), + Str->user.str, Str->host.str, real_name); + result= TRUE; + continue; + } + grant_name= new GRANT_NAME(Str->host.str, db_name, + Str->user.str, real_name, + rights); + if (!grant_name) + { + result= TRUE; + continue; + } + my_hash_insert(&proc_priv_hash,(byte*) grant_name); + } + + if (replace_proc_table(thd, grant_name, tables[1].table, *Str, + db_name, real_name, rights, revoke_grant)) + { + result= TRUE; + continue; + } + } + grant_option=TRUE; + thd->mem_root= old_root; + rw_unlock(&LOCK_grant); + if (!result && !no_error) + send_ok(thd); + /* Tables are automatically closed */ + DBUG_RETURN(result); +} + + bool mysql_grant(THD *thd, const char *db, List &list, ulong rights, bool revoke_grant) { @@ -2713,6 +3040,7 @@ void grant_free(void) DBUG_ENTER("grant_free"); grant_option = FALSE; hash_free(&column_priv_hash); + hash_free(&proc_priv_hash); free_root(&memex,MYF(0)); DBUG_VOID_RETURN; } @@ -2723,11 +3051,11 @@ void grant_free(void) my_bool grant_init(THD *org_thd) { THD *thd; - TABLE_LIST tables[2]; + TABLE_LIST tables[3]; MYSQL_LOCK *lock; MEM_ROOT *memex_ptr; my_bool return_val= 1; - TABLE *t_table, *c_table; + TABLE *t_table, *c_table, *p_table; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; DBUG_ENTER("grant_init"); @@ -2735,6 +3063,9 @@ my_bool grant_init(THD *org_thd) (void) hash_init(&column_priv_hash,system_charset_info, 0,0,0, (hash_get_key) get_grant_table, (hash_free_key) free_grant_table,0); + (void) hash_init(&proc_priv_hash,system_charset_info, + 0,0,0, (hash_get_key) get_grant_table, + 0,0); init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); /* Don't do anything if running with --skip-grant */ @@ -2749,69 +3080,110 @@ my_bool grant_init(THD *org_thd) bzero((char*) &tables, sizeof(tables)); tables[0].alias=tables[0].real_name= (char*) "tables_priv"; tables[1].alias=tables[1].real_name= (char*) "columns_priv"; + tables[2].alias=tables[2].real_name= (char*) "procs_priv"; tables[0].next_local= tables[0].next_global= tables+1; - tables[0].lock_type=tables[1].lock_type=TL_READ; - tables[0].db=tables[1].db=thd->db; + tables[1].next_local= tables[1].next_global= tables+2; + tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; + tables[0].db=tables[1].db=tables[2].db=thd->db; uint counter; if (open_tables(thd, tables, &counter)) goto end; - TABLE *ptr[2]; // Lock tables for quick update + TABLE *ptr[3]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; - if (!(lock=mysql_lock_tables(thd,ptr,2))) + ptr[2]= tables[2].table; + if (!(lock=mysql_lock_tables(thd,ptr,3))) goto end; t_table = tables[0].table; c_table = tables[1].table; + p_table= tables[2].table; t_table->file->ha_index_init(0); - if (t_table->file->index_first(t_table->record[0])) + p_table->file->ha_index_init(0); + if (!t_table->file->index_first(t_table->record[0])) { - return_val= 0; - goto end_unlock; - } - grant_option= TRUE; - - /* Will be restored by org_thd->store_globals() */ - memex_ptr= &memex; - my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); - do - { - GRANT_TABLE *mem_check; - if (!(mem_check=new GRANT_TABLE(t_table,c_table))) + /* Will be restored by org_thd->store_globals() */ + memex_ptr= &memex; + my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); + do { - /* This could only happen if we are out memory */ - grant_option= FALSE; /* purecov: deadcode */ - goto end_unlock; - } - - if (check_no_resolve) - { - if (hostname_requires_resolving(mem_check->host)) + GRANT_TABLE *mem_check; + if (!(mem_check=new GRANT_TABLE(t_table,c_table))) { - sql_print_warning("'tables_priv' entry '%s %s@%s' " - "ignored in --skip-name-resolve mode.", - mem_check->tname, mem_check->user, - mem_check->host, mem_check->host); - continue; + /* This could only happen if we are out memory */ + grant_option= FALSE; + goto end_unlock; + } + + if (check_no_resolve) + { + if (hostname_requires_resolving(mem_check->host)) + { + sql_print_warning("'tables_priv' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + mem_check->tname, mem_check->user, + mem_check->host, mem_check->host); + continue; + } + } + + if (! mem_check->ok()) + delete mem_check; + else if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) + { + delete mem_check; + grant_option= FALSE; + goto end_unlock; } } - - if (! mem_check->ok()) - delete mem_check; - else if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) - { - delete mem_check; - grant_option= FALSE; - goto end_unlock; - } + while (!t_table->file->index_next(t_table->record[0])); } - while (!t_table->file->index_next(t_table->record[0])); + if (!p_table->file->index_first(p_table->record[0])) + { + /* Will be restored by org_thd->store_globals() */ + memex_ptr= &memex; + my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); + do + { + GRANT_NAME *mem_check; + if (!(mem_check=new GRANT_NAME(p_table))) + { + /* This could only happen if we are out memory */ + grant_option= FALSE; + goto end_unlock; + } + if (check_no_resolve) + { + if (hostname_requires_resolving(mem_check->host)) + { + sql_print_warning("'procs_priv' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + mem_check->tname, mem_check->user, + mem_check->host, mem_check->host); + continue; + } + } + + mem_check->privs= fix_rights_for_procedure(mem_check->privs); + if (! mem_check->ok()) + delete mem_check; + else if (my_hash_insert(&proc_priv_hash,(byte*) mem_check)) + { + delete mem_check; + grant_option= FALSE; + goto end_unlock; + } + } + while (!p_table->file->index_next(p_table->record[0])); + } + grant_option= TRUE; return_val=0; // Return ok end_unlock: t_table->file->ha_index_end(); + p_table->file->ha_index_end(); mysql_unlock_tables(thd, lock); thd->version--; // Force close to free memory @@ -2842,7 +3214,7 @@ end: void grant_reload(THD *thd) { - HASH old_column_priv_hash; + HASH old_column_priv_hash, old_proc_priv_hash; bool old_grant_option; MEM_ROOT old_mem; DBUG_ENTER("grant_reload"); @@ -2850,6 +3222,7 @@ void grant_reload(THD *thd) rw_wrlock(&LOCK_grant); grant_version++; old_column_priv_hash= column_priv_hash; + old_proc_priv_hash= proc_priv_hash; old_grant_option= grant_option; old_mem= memex; @@ -2858,12 +3231,14 @@ void grant_reload(THD *thd) DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ + proc_priv_hash= old_proc_priv_hash; grant_option= old_grant_option; /* purecov: deadcode */ memex= old_mem; /* purecov: deadcode */ } else { hash_free(&old_column_priv_hash); + hash_free(&old_proc_priv_hash); free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); @@ -3099,7 +3474,7 @@ err2: /* Check if a user has the right to access a database - Access is accepted if he has a grant for any table in the database + Access is accepted if he has a grant for any table/routine in the database Return 1 if access is denied */ @@ -3131,6 +3506,72 @@ bool check_grant_db(THD *thd,const char *db) return error; } + +/**************************************************************************** + Check procedure level grants + + SYNPOSIS + bool check_grant_procedure() + thd Thread handler + want_access Bits of privileges user needs to have + procs List of procedures to check. The user should have 'want_access' + no_errors If 0 then we write an error. The error is sent directly to + the client + + RETURN + 0 ok + 1 Error: User did not have the requested privielges +****************************************************************************/ + +bool check_grant_procedure(THD *thd, ulong want_access, + TABLE_LIST *procs, bool no_errors) +{ + TABLE_LIST *table; + char *user= thd->priv_user; + char *host= thd->priv_host; + DBUG_ENTER("check_grant_procedure"); + + want_access&= ~thd->master_access; + if (!want_access) + DBUG_RETURN(0); // ok + + rw_rdlock(&LOCK_grant); + for (table= procs; table; table= table->next_global) + { + GRANT_NAME *grant_proc; + if ((grant_proc= proc_hash_search(host,thd->ip, + table->db, user, table->real_name, 0))) + table->grant.privilege|= grant_proc->privs; + + if (want_access & ~table->grant.privilege) + { + want_access &= ~table->grant.privilege; + goto err; + } + } + rw_unlock(&LOCK_grant); + DBUG_RETURN(0); +err: + rw_unlock(&LOCK_grant); + if (!no_errors) + { + char buff[1024]; + const char *command=""; + if (table) + strxmov(buff, table->db, ".", table->real_name, NullS); + if (want_access & EXECUTE_ACL) + command= "execute"; + else if (want_access & ALTER_PROC_ACL) + command= "alter procedure"; + else if (want_access & GRANT_ACL) + command= "grant"; + my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0), + command, user, host, table ? buff : "unknown"); + } + DBUG_RETURN(1); +} + + /***************************************************************************** Functions to retrieve the grant for a table/column (for SHOW functions) *****************************************************************************/ @@ -3215,12 +3656,12 @@ static const char *command_array[]= "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX", "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", - "CREATE VIEW", "SHOW VIEW" + "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", }; static uint command_lengths[]= { - 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9 + 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, 14, 13 }; @@ -3565,6 +4006,74 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } + + /* Add procedure access */ + for (index=0 ; index < proc_priv_hash.records ; index++) + { + const char *user; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + index); + + if (!(user=grant_proc->user)) + user= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, + grant_proc->orig_host)) + { + ulong proc_access= grant_proc->privs; + if (proc_access != 0) + { + String global(buff, sizeof(buff), system_charset_info); + ulong test_access= proc_access & ~GRANT_ACL; + + global.length(0); + global.append("GRANT ",6); + + if (!test_access) + global.append("USAGE",5); + else + { + /* Add specific procedure access */ + int found= 0; + ulong j; + + for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1) + { + if (test_access & j) + { + if (found) + global.append(", ",2); + found= 1; + global.append(command_array[counter],command_lengths[counter]); + } + } + } + global.append(" ON ",4); + append_identifier(thd, &global, grant_proc->db, + strlen(grant_proc->db)); + global.append('.'); + append_identifier(thd, &global, grant_proc->tname, + strlen(grant_proc->tname)); + global.append(" TO '",5); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + global.append("'@'",3); + global.append(lex_user->host.str,lex_user->host.length, + system_charset_info); + global.append('\''); + if (proc_access & GRANT_ACL) + global.append(" WITH GRANT OPTION",18); + protocol->prepare_for_resend(); + protocol->store(global.ptr(),global.length(),global.charset()); + if (protocol->write()) + { + error= -1; + break; + } + } + } + } end: VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); @@ -3632,6 +4141,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) < 0 Error. */ +#define GRANT_TABLES 5 int open_grant_tables(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("open_grant_tables"); @@ -3642,17 +4152,21 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) DBUG_RETURN(-1); } - bzero((char*) tables, 4*sizeof(*tables)); + bzero((char*) tables, GRANT_TABLES*sizeof(*tables)); tables->alias= tables->real_name= (char*) "user"; (tables+1)->alias= (tables+1)->real_name= (char*) "db"; (tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv"; (tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv"; + (tables+4)->alias= (tables+4)->real_name= (char*) "procs_priv"; tables->next_local= tables->next_global= tables+1; (tables+1)->next_local= (tables+1)->next_global= tables+2; (tables+2)->next_local= (tables+2)->next_global= tables+3; + (tables+3)->next_local= (tables+3)->next_global= tables+4; tables->lock_type= (tables+1)->lock_type= - (tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE; - tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql"; + (tables+2)->lock_type= (tables+3)->lock_type= + (tables+4)->lock_type= TL_WRITE; + tables->db= (tables+1)->db= (tables+2)->db= + (tables+3)->db= (tables+4)->db= (char*) "mysql"; #ifdef HAVE_REPLICATION /* @@ -3665,10 +4179,12 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) The tables must be marked "updating" so that tables_ok() takes them into account in tests. */ - tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=1; + tables[0].updating=tables[1].updating=tables[2].updating= + tables[3].updating=tables[4].updating=1; if (!tables_ok(0, tables)) DBUG_RETURN(1); - tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=0; + tables[0].updating=tables[1].updating=tables[2].updating= + tables[3].updating=tables[4].updating=0;; } #endif @@ -3761,7 +4277,7 @@ static int modify_grant_table(TABLE *table, Field *host_field, SYNOPSIS handle_grant_table() tables The array with the four open tables. - table_no The number of the table to handle (0..3). + table_no The number of the table to handle (0..4). drop If user_from is to be dropped. user_from The the user to be searched/dropped/renamed. user_to The new name for the user if to be renamed, @@ -3779,6 +4295,7 @@ static int modify_grant_table(TABLE *table, Field *host_field, 1 db 2 tables_priv 3 columns_priv + 4 procs_priv RETURN > 0 At least one record matched. @@ -3922,6 +4439,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, 0 acl_users 1 acl_dbs 2 column_priv_hash + 3 procs_priv_hash RETURN > 0 At least one element matched. @@ -3938,11 +4456,11 @@ static int handle_grant_struct(uint struct_no, bool drop, const char *host; ACL_USER *acl_user; ACL_DB *acl_db; - GRANT_TABLE *grant_table; + GRANT_NAME *grant_name; DBUG_ENTER("handle_grant_struct"); LINT_INIT(acl_user); LINT_INIT(acl_db); - LINT_INIT(grant_table); + LINT_INIT(grant_name); DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'", struct_no, user_from->user.str, user_from->host.str)); @@ -3955,8 +4473,15 @@ static int handle_grant_struct(uint struct_no, bool drop, case 1: elements= acl_dbs.elements; break; - default: + case 2: elements= column_priv_hash.records; + break; + case 3: + elements= proc_priv_hash.records; + break; + default: + DBUG_ASSERT((struct_no < 0) || (struct_no > 3)); + return -1; } #ifdef EXTRA_DEBUG @@ -3985,10 +4510,17 @@ static int handle_grant_struct(uint struct_no, bool drop, host= acl_db->host.hostname; break; - default: - grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, idx); - user= grant_table->user; - host= grant_table->host; + case 2: + grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx); + user= grant_name->user; + host= grant_name->host; + break; + + case 3: + grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx); + user= grant_name->user; + host= grant_name->host; + break; } if (! user) user= ""; @@ -4015,8 +4547,13 @@ static int handle_grant_struct(uint struct_no, bool drop, delete_dynamic_element(&acl_dbs, idx); break; - default: - hash_delete(&column_priv_hash, (byte*) grant_table); + case 2: + hash_delete(&column_priv_hash, (byte*) grant_name); + break; + + case 3: + hash_delete(&proc_priv_hash, (byte*) grant_name); + break; } elements--; idx--; @@ -4035,9 +4572,11 @@ static int handle_grant_struct(uint struct_no, bool drop, acl_db->host.hostname= strdup_root(&mem, user_to->host.str); break; - default: - grant_table->user= strdup_root(&mem, user_to->user.str); - grant_table->host= strdup_root(&mem, user_to->host.str); + case 2: + case 3: + grant_name->user= strdup_root(&mem, user_to->user.str); + grant_name->host= strdup_root(&mem, user_to->host.str); + break; } } else @@ -4123,6 +4662,25 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, } } + /* Handle procedures table. */ + if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0) + { + /* Handle of table failed, don't touch in-memory array. */ + result= -1; + } + else + { + /* Handle procs array. */ + if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) || + found) && ! result) + { + result= 1; /* At least one record/element found. */ + /* If search is requested, we do not need to search further. */ + if (! drop && ! user_to) + goto end; + } + } + /* Handle tables table. */ if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0) { @@ -4191,7 +4749,7 @@ bool mysql_create_user(THD *thd, List &list) ulong sql_mode; LEX_USER *user_name; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); /* CREATE USER may be skipped on replication client. */ @@ -4253,7 +4811,7 @@ bool mysql_drop_user(THD *thd, List &list) String wrong_users; LEX_USER *user_name; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); /* DROP USER may be skipped on replication client. */ @@ -4302,7 +4860,7 @@ bool mysql_rename_user(THD *thd, List &list) LEX_USER *user_from; LEX_USER *user_to; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); /* RENAME USER may be skipped on replication client. */ @@ -4357,7 +4915,7 @@ bool mysql_revoke_all(THD *thd, List &list) uint counter, revoked; int result; ACL_DB *acl_db; - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); if ((result= open_grant_tables(thd, tables))) @@ -4467,6 +5025,35 @@ bool mysql_revoke_all(THD *thd, List &list) counter++; } } while (revoked); + + /* Remove procedure access */ + do { + for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; ) + { + const char *user,*host; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + counter); + if (!(user=grant_proc->user)) + user= ""; + if (!(host=grant_proc->host)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + if (!replace_proc_table(thd,grant_proc,tables[4].table,*lex_user, + grant_proc->db, + grant_proc->tname, + ~0, 1)) + { + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; + } + } while (revoked); } VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -4480,6 +5067,129 @@ bool mysql_revoke_all(THD *thd, List &list) } +/* + Revoke privileges for all users on a stored procedure + + SYNOPSIS + sp_revoke_privileges() + thd The current thread. + db DB of the stored procedure + name Name of the stored procedure + + RETURN + 0 OK. + < 0 Error. Error message not yet sent. +*/ + +bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name) +{ + uint counter, revoked; + int result; + ACL_DB *acl_db; + TABLE_LIST tables[GRANT_TABLES]; + DBUG_ENTER("sp_revoke_privileges"); + + if ((result= open_grant_tables(thd, tables))) + DBUG_RETURN(result != 1); + + rw_wrlock(&LOCK_grant); + VOID(pthread_mutex_lock(&acl_cache->lock)); + + /* Remove procedure access */ + do { + for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; ) + { + const char *db,*name; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + counter); + if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) && + !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name)) + { + LEX_USER lex_user; + lex_user.user.str= grant_proc->user; + lex_user.user.length= strlen(grant_proc->user); + lex_user.host.str= grant_proc->host; + lex_user.host.length= strlen(grant_proc->host); + if (!replace_proc_table(thd,grant_proc,tables[4].table,lex_user, + grant_proc->db, grant_proc->tname, ~0, 1)) + { + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; + } + } while (revoked); + + VOID(pthread_mutex_unlock(&acl_cache->lock)); + rw_unlock(&LOCK_grant); + close_thread_tables(thd); + + if (result) + my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); + + DBUG_RETURN(result); +} + + +/* + Grant EXECUTE,ALTER privilege for a stored procedure + + SYNOPSIS + sp_grant_privileges() + thd The current thread. + db DB of the stored procedure + name Name of the stored procedure + + RETURN + 0 OK. + < 0 Error. Error message not yet sent. +*/ + +bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name) +{ + LEX_USER *combo; + TABLE_LIST tables[1]; + List user_list; + bool result; + DBUG_ENTER("sp_grant_privileges"); + + if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + DBUG_RETURN(TRUE); + + combo->user.str= thd->user; + + if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str) && + !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str) && + !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str) && + !find_acl_user(combo->host.str=(char*)"%", combo->user.str)) + DBUG_RETURN(TRUE); + + bzero((char*)tables, sizeof(TABLE_LIST)); + user_list.empty(); + + tables->db= (char*)sp_db; + tables->real_name= tables->alias= (char*)sp_name; + + combo->host.length= strlen(combo->host.str); + combo->user.length= strlen(combo->user.str); + combo->host.str= thd->strmake(combo->host.str,combo->host.length); + combo->user.str= thd->strmake(combo->user.str,combo->user.length); + combo->password.str= (char*)""; + combo->password.length= 0; + + if (user_list.push_back(combo)) + DBUG_RETURN(TRUE); + + thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + + result= mysql_procedure_grant(thd, tables, user_list, + DEFAULT_CREATE_PROC_ACLS, 0, 1); + DBUG_RETURN(result); +} + + /***************************************************************************** Instantiate used templates *****************************************************************************/ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 8f3ee072f43..4ebc3ad7707 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -37,6 +37,8 @@ #define REPL_CLIENT_ACL (1L << 20) #define CREATE_VIEW_ACL (1L << 21) #define SHOW_VIEW_ACL (1L << 22) +#define CREATE_PROC_ACL (1L << 23) +#define ALTER_PROC_ACL (1L << 24) /* don't forget to update static struct show_privileges_st sys_privileges[] @@ -47,7 +49,8 @@ #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ - LOCK_TABLES_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) + LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ + CREATE_PROC_ACL | ALTER_PROC_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ @@ -57,43 +60,61 @@ #define COL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) +#define PROC_ACLS \ +(ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL) + #define GLOBAL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \ REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ - EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) + EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ + ALTER_PROC_ACL ) #define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) +#define DEFAULT_CREATE_PROC_ACLS \ +(ALTER_PROC_ACL | EXECUTE_ACL) + /* Defines to change the above bits to how things are stored in tables This is needed as the 'host' and 'db' table is missing a few privileges */ /* Continius bit-segments that needs to be shifted */ -#define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL) -#define DB_REL2 (GRANT_ACL | REFERENCES_ACL) -#define DB_REL3 (INDEX_ACL | ALTER_ACL) +#define DB_REL1 ((1L << 6) | (1L << 7) | (1L << 8) | (1L << 9)) +#define DB_REL2 ((1L << 10) | (1L << 11)) +#define DB_REL3 ((1L << 12) | (1L << 13) | (1L << 14) | (1L << 15)) +#define DB_REL4 ((1L << 16)) /* Privileges that needs to be reallocated (in continous chunks) */ #define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) #define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL) -#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) +#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ + CREATE_PROC_ACL | ALTER_PROC_ACL ) +#define DB_CHUNK4 (EXECUTE_ACL) #define fix_rights_for_db(A) (((A) & 63) | \ (((A) & DB_REL1) << 4) | \ (((A) & DB_REL2) << 6) | \ - (((A) & DB_REL3) << 9)) + (((A) & DB_REL3) << 9) | \ + (((A) & DB_REL4) << 2)) #define get_rights_for_db(A) (((A) & 63) | \ (((A) & DB_CHUNK1) >> 4) | \ (((A) & DB_CHUNK2) >> 6) | \ - (((A) & DB_CHUNK3) >> 9)) + (((A) & DB_CHUNK3) >> 9) | \ + (((A) & DB_CHUNK4) >> 2)) #define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4)) #define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4)) #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) #define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) +#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \ + (((A) << 23) & ALTER_PROC_ACL) | \ + (((A) << 8) & GRANT_ACL)) +#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \ + (((A) & ALTER_PROC_ACL) >> 23) | \ + (((A) & GRANT_ACL) >> 8)) /* Classes */ @@ -163,6 +184,9 @@ bool mysql_grant(THD *thd, const char *db, List &user_list, bool mysql_table_grant(THD *thd, TABLE_LIST *table, List &user_list, List &column_list, ulong rights, bool revoke); +bool mysql_procedure_grant(THD *thd, TABLE_LIST *table, + List &user_list, ulong rights, + bool revoke, bool no_error); my_bool grant_init(THD *thd); void grant_free(void); void grant_reload(THD *thd); @@ -174,6 +198,8 @@ bool check_grant_column (THD *thd, GRANT_INFO *grant, bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, char* db_name, char *table_name, Field_iterator *fields); +bool check_grant_procedure(THD *thd, ulong want_access, + TABLE_LIST *procs, bool no_error); bool check_grant_db(THD *thd,const char *db); ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, @@ -188,6 +214,8 @@ bool mysql_rename_user(THD *thd, List &list); bool mysql_revoke_all(THD *thd, List &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); +bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name); +bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name); #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 diff --git a/sql/sql_class.h b/sql/sql_class.h index 2e9440378d5..9f0b8015a20 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -388,6 +388,7 @@ struct system_variables ulong max_sort_length; ulong max_tmp_tables; ulong max_insert_delayed_threads; + ulong multi_range_count; ulong myisam_repair_threads; ulong myisam_sort_buff_size; ulong net_buffer_length; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 721febab548..c5b3abb93d2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -63,7 +63,7 @@ enum enum_sql_command { SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB, SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, - SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, + SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS, SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, @@ -87,8 +87,8 @@ enum enum_sql_command { SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, - SQLCOM_LOCK_TABLES_TRANSACTIONAL, /* This should be the last !!! */ + SQLCOM_END }; @@ -743,6 +743,7 @@ typedef struct st_lex sp_head *sphead; sp_name *spname; bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ + bool all_privileges; sp_pcontext *spcont; HASH spfuns; /* Called functions */ st_sp_chistics sp_chistics; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0dec6e820be..4a357c6eefe 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -69,7 +69,6 @@ static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); -static bool check_sp_definer_access(THD *thd, sp_head *sp); const char *any_db="*any*"; // Special symbol for check_access @@ -3253,27 +3252,6 @@ create_error: thd->options&= ~(ulong) (OPTION_TABLE_LOCK); thd->in_lock_tables=0; break; - case SQLCOM_LOCK_TABLES_TRANSACTIONAL: - { - uint counter = 0; - - if (check_db_used(thd, all_tables)) - goto error; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) - goto error; - - thd->in_lock_tables=1; - thd->options|= OPTION_TABLE_LOCK; - - if (open_tables(thd, all_tables, &counter) == 0 && - transactional_lock_tables(thd, all_tables, counter) == 0) - send_ok(thd); - else - thd->options&= ~(ulong) (OPTION_TABLE_LOCK); - - thd->in_lock_tables=0; - break; - } case SQLCOM_CREATE_DB: { char *alias; @@ -3285,12 +3263,12 @@ create_error: /* If in a slave thread : CREATE DATABASE DB was certainly not preceded by USE DB. - For that reason, db_ok() in sql/slave.cc did not check the + For that reason, db_ok() in sql/slave.cc did not check the do_db/ignore_db. And as this query involves no tables, tables_ok() above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && + if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) { @@ -3495,15 +3473,30 @@ create_error: } if (first_table) { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) - goto error; - if (!(res = mysql_table_grant(thd, all_tables, lex->users_list, - lex->columns, lex->grant, - lex->sql_command == SQLCOM_REVOKE)) && - mysql_bin_log.is_open()) + if (!lex->columns.elements && + sp_exists_routine(thd, all_tables, 1, 1)) + { + uint grants= lex->all_privileges + ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) + : lex->grant; + if (grant_option && + check_grant_procedure(thd, grants | GRANT_ACL, all_tables, 0)) + goto error; + res= mysql_procedure_grant(thd, all_tables, lex->users_list, + grants, lex->sql_command == SQLCOM_REVOKE,0); + } + else + { + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + all_tables, 0, UINT_MAX, 0)) + goto error; + res= mysql_table_grant(thd, all_tables, lex->users_list, + lex->columns, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + } + if (!res && mysql_bin_log.is_open()) { thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); @@ -3705,18 +3698,24 @@ create_error: case SQLCOM_CREATE_SPFUNCTION: { uint namelen; - char *name; + char *name, *db; int result; DBUG_ASSERT(lex->sphead); - if (! lex->sphead->m_db.str) + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0)) { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); delete lex->sphead; lex->sphead= 0; goto error; } + + if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) + { + lex->sphead->m_db.length= strlen(thd->db); + lex->sphead->m_db.str= strmake_root(thd->mem_root, thd->db, + lex->sphead->m_db.length); + } name= lex->sphead->name(&namelen); #ifdef HAVE_DLOPEN @@ -3742,13 +3741,28 @@ create_error: goto error; } + name= thd->strdup(name); + db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length); res= (result= lex->sphead->create(thd)); switch (result) { case SP_OK: - send_ok(thd); lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* only add privileges if really neccessary */ + if (sp_automatic_privileges && + check_procedure_access(thd, DEFAULT_CREATE_PROC_ACLS, + db, name, 1)) + { + close_thread_tables(thd); + if (sp_grant_privileges(thd, db, name)) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_GRANT_FAIL, + ER(ER_PROC_AUTO_GRANT_FAIL)); + } +#endif + send_ok(thd); break; case SP_WRITE_ROW_FAILED: my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); @@ -3815,7 +3829,26 @@ create_error: } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_procedure_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + goto error; + } sp_change_security_context(thd, sp, &save_ctx); + if (save_ctx.changed && + check_procedure_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + sp_restore_security_context(thd, sp, &save_ctx); + goto error; + } + #endif select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; @@ -3861,8 +3894,9 @@ create_error: result= SP_KEY_NOT_FOUND; else { - if (check_sp_definer_access(thd, sp)) - goto error; + if (check_procedure_access(thd, ALTER_PROC_ACL, sp->m_db.str, + sp->m_name.str, 0)) + goto error; memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics)); if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics); @@ -3890,6 +3924,7 @@ create_error: { sp_head *sp; int result; + char *db, *name; if (lex->sql_command == SQLCOM_DROP_PROCEDURE) sp= sp_find_procedure(thd, lex->spname); @@ -3898,8 +3933,19 @@ create_error: mysql_reset_errors(thd); if (sp) { - if (check_sp_definer_access(thd, sp)) + db= thd->strdup(sp->m_db.str); + name= thd->strdup(sp->m_name.str); + if (check_procedure_access(thd, ALTER_PROC_ACL, db, name, 0)) goto error; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (sp_automatic_privileges && + sp_revoke_privileges(thd, db, name)) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_REVOKE_FAIL, + ER(ER_PROC_AUTO_REVOKE_FAIL)); + } +#endif if (lex->sql_command == SQLCOM_DROP_PROCEDURE) result= sp_drop_procedure(thd, lex->spname); else @@ -4208,7 +4254,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || (grant_option && !dont_check_global_grants && - !(want_access & ~(db_access | TABLE_ACLS)))) + !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ DBUG_PRINT("error",("Access denied")); @@ -4304,6 +4350,30 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, } +bool +check_procedure_access(THD *thd, ulong want_access,char *db, char *name, + bool no_errors) +{ + TABLE_LIST tables[1]; + + bzero((char *)tables, sizeof(TABLE_LIST)); + tables->db= db; + tables->real_name= tables->alias= name; + + if ((thd->master_access & want_access) == want_access && !thd->db) + tables->grant.privilege= want_access; + else if (check_access(thd,want_access,db,&tables->grant.privilege, + 0, no_errors)) + return TRUE; + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (grant_option) + return check_grant_procedure(thd, want_access, tables, no_errors); +#endif + + return FALSE; +} + /* Check if the given table has any of the asked privileges @@ -4377,40 +4447,6 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) } -/* - Check if the given SP is owned by thd->priv_user/host, or priv_user is root. - QQ This is not quite complete, but it will do as a basic security check - for now. The question is exactly which rights should 'root' have? - Should root have access regardless of host for instance? - - SYNOPSIS - check_sp_definer_access() - thd Thread handler - sp The SP pointer - - RETURN - 0 ok - 1 error Error message has been sent -*/ - -static bool -check_sp_definer_access(THD *thd, sp_head *sp) -{ - LEX_STRING *usr, *hst; - - if (strcmp("root", thd->priv_user) == 0) - return FALSE; /* QQ Any root is ok now */ - usr= &sp->m_definer_user; - hst= &sp->m_definer_host; - if (strncmp(thd->priv_user, usr->str, usr->length) == 0 && - strncmp(thd->priv_host, hst->str, hst->length) == 0) - return FALSE; /* Both user and host must match */ - - my_error(ER_SP_ACCESS_DENIED_ERROR, MYF(0), sp->m_qname.str); - return TRUE; /* Not definer or root */ -} - - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 826bd2038f9..d075e428aa0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -138,13 +138,16 @@ struct show_privileges_st { static struct show_privileges_st sys_privileges[]= { {"Alter", "Tables", "To alter the table"}, + {"Alter routine", "Functions,Procedures", "To alter or drop stored functions/procedures"}, {"Create", "Databases,Tables,Indexes", "To create new databases and tables"}, + {"Create routine","Functions,Procedures","To use CREATE FUNCTION/PROCEDURE"}, {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"}, {"Create view", "Tables", "To create new views"}, {"Delete", "Tables", "To delete existing rows"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, + {"Execute", "Functions,Procedures", "To execute stored routines"}, {"File", "File access on server", "To read and write files on the server"}, - {"Grant option", "Databases,Tables", "To give to other users those privileges you possess"}, + {"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"}, {"Index", "Tables", "To create or drop indexes"}, {"Insert", "Tables", "To insert data into tables"}, {"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"}, @@ -2246,14 +2249,14 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, TABLE *show_table= tables->table; handler *file= show_table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); - if (table->tmp_table == TMP_TABLE) + if (show_table->tmp_table == TMP_TABLE) table->field[3]->store("TEMPORARY", 9, cs); else table->field[3]->store("BASE TABLE", 10, cs); for (int i= 4; i < 20; i++) { - if ((i > 12 && i < 17) || i == 18) + if (i == 7 || (i > 12 && i < 17) || i == 18) continue; table->field[i]->set_notnull(); } @@ -2265,7 +2268,11 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, (show_table->db_options_in_use & HA_OPTION_PACK_RECORD) ? "Dynamic" : "Fixed"); table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); - table->field[7]->store((longlong) file->records); + if (!tables->schema_table) + { + table->field[7]->store((longlong) file->records); + table->field[7]->set_notnull(); + } table->field[8]->store((longlong) file->mean_rec_length); table->field[9]->store((longlong) file->data_file_length); if (file->max_data_file_length) @@ -3453,7 +3460,7 @@ ST_FIELD_INFO schema_fields_info[]= { {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"SCHEMA_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"}, - {"DEFAULT_CHARACTER_SET_NAME", 60, MYSQL_TYPE_STRING, 0, 0, 0}, + {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3478,7 +3485,7 @@ ST_FIELD_INFO tables_fields_info[]= {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"}, {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"}, {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"}, - {"COLLATION", 60, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"}, {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"}, {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"}, @@ -3500,8 +3507,8 @@ ST_FIELD_INFO columns_fields_info[]= {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, - {"CHARACTER_SET_NAME", 40, MYSQL_TYPE_STRING, 0, 1, 0}, - {"COLLATION_NAME", 40, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"}, {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"}, {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"}, @@ -3513,8 +3520,8 @@ ST_FIELD_INFO columns_fields_info[]= ST_FIELD_INFO charsets_fields_info[]= { - {"CHARACTER_SET_NAME", 30, MYSQL_TYPE_STRING, 0, 0, "Charset"}, - {"DEFAULT_COLLATE_NAME", 60, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, + {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"}, {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} @@ -3523,11 +3530,11 @@ ST_FIELD_INFO charsets_fields_info[]= ST_FIELD_INFO collation_fields_info[]= { - {"COLLATION_NAME", 30, MYSQL_TYPE_STRING, 0, 0, "Collation"}, - {"CHARSET", 30, MYSQL_TYPE_STRING, 0, 0, "Charset"}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, {"ID", 11, MYSQL_TYPE_LONG, 0, 0, "Id"}, - {"DEFAULT", 30 ,MYSQL_TYPE_STRING, 0, 0, "Default"}, - {"COMPILED", 30 ,MYSQL_TYPE_STRING, 0, 0, "Compiled"}, + {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"}, + {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"}, {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3535,8 +3542,8 @@ ST_FIELD_INFO collation_fields_info[]= ST_FIELD_INFO coll_charset_app_fields_info[]= { - {"COLLATION_NAME", 30, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHARACTER_SET_NAME", 30, MYSQL_TYPE_STRING, 0, 0, 0}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 88cd3daf924..5c03a4c98ef 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -390,6 +390,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RESTORE_SYM %token RESTRICT %token REVOKE +%token ROUTINE_SYM %token ROWS_SYM %token ROW_FORMAT_SYM %token ROW_SYM @@ -790,7 +791,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_outer table_list table_name opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges opt_table grant_list grant_option - grant_privilege grant_privilege_list user_list rename_list + object_privilege object_privilege_list user_list rename_list clear_privileges flush_options flush_option equal optional_braces opt_key_definition key_usage_list2 opt_mi_check_type opt_to mi_check_types normal_join @@ -1301,6 +1302,7 @@ clear_privileges: lex->users_list.empty(); lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; lex->select_lex.db= 0; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; @@ -7031,6 +7033,7 @@ keyword: | RETURNS_SYM {} | ROLLBACK_SYM {} | ROLLUP_SYM {} + | ROUTINE_SYM {} | ROWS_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} @@ -7415,8 +7418,8 @@ lock: { Lex->sql_command=SQLCOM_LOCK_TABLES; } - table_lock_list lock_engine_opt - {} + table_lock_list + {} ; table_or_tables: @@ -7442,15 +7445,6 @@ lock_option: | READ_SYM LOCAL_SYM { $$= TL_READ; } ; -lock_engine_opt: - /* empty */ - | WHERE - { - Lex->sql_command=SQLCOM_LOCK_TABLES_TRANSACTIONAL; - } - ENGINE_SYM opt_equal storage_engines - ; - unlock: UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } ; @@ -7543,14 +7537,16 @@ revoke_command: grant: GRANT clear_privileges grant_privileges ON opt_table TO_SYM grant_list require_clause grant_options - { - Lex->sql_command = SQLCOM_GRANT; - } + { Lex->sql_command= SQLCOM_GRANT; } ; grant_privileges: - grant_privilege_list {} - | ALL opt_privileges { Lex->grant = GLOBAL_ACLS;} + object_privilege_list { } + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } ; opt_privileges: @@ -7558,11 +7554,11 @@ opt_privileges: | PRIVILEGES ; -grant_privilege_list: - grant_privilege - | grant_privilege_list ',' grant_privilege; +object_privilege_list: + object_privilege + | object_privilege_list ',' object_privilege; -grant_privilege: +object_privilege: SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} @@ -7587,6 +7583,8 @@ grant_privilege: | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } ; diff --git a/sql/table.cc b/sql/table.cc index c18a2557337..3cf6bbded58 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -76,6 +76,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, my_string record; const char **int_array; bool use_hash, null_field_first; + bool error_reported= FALSE; File file; Field **field_ptr,*reg_field; KEY *keyinfo; @@ -788,6 +789,11 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, error= 1; my_errno= ENOENT; } + else + { + outparam->file->print_error(err, MYF(0)); + error_reported= TRUE; + } goto err_not_open; /* purecov: inspected */ } } @@ -814,7 +820,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, err_end: /* Here when no file */ delete crypted; *root_ptr= old_root; - frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG); + if (! error_reported) + frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG); delete outparam->file; outparam->file=0; // For easier errorchecking outparam->db_stat=0; diff --git a/sql/unireg.h b/sql/unireg.h index 932bdf4dfc5..053ca393ad0 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -37,8 +37,8 @@ #define SHAREDIR "share/" #endif -#define ER(X) errmesg[(X)-1000] -#define ER_SAFE(X) (((X) >= 1000 && (X) < ER_ERROR_MESSAGES + 1000) ? ER(X) : "Invalid error code") +#define ER(X) errmesg[(X) - ER_ERROR_FIRST] +#define ER_SAFE(X) (((X) >= ER_ERROR_FIRST && (X) <= ER_ERROR_LAST) ? ER(X) : "Invalid error code") #define ERRMAPP 1 /* Errormap f|r my_error */ diff --git a/strings/decimal.c b/strings/decimal.c index c691bcb62fc..42e65c26ad1 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -14,6 +14,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#line __LINE__ "decimal.c" + /* ======================================================================= NOTE: this library implements SQL standard "exact numeric" type @@ -680,8 +682,12 @@ int decimal2bin(decimal *from, char *to, int precision, int frac) /* frac1x part */ if (frac1x) { - int i=dig2bytes[frac1x]; - dec1 x=(*buf1 / powers10[DIG_PER_DEC1 - frac1x]) ^ mask; + dec1 x; + int i=dig2bytes[frac1x], + lim=(frac1 < frac0 ? DIG_PER_DEC1 : frac0x); + while (frac1x < lim && dig2bytes[frac1x] == i) + frac1x++; + x=(*buf1 / powers10[DIG_PER_DEC1 - frac1x]) ^ mask; switch (i) { case 1: mi_int1store(to, x); break; @@ -2087,6 +2093,8 @@ main() test_d2b2d("-.000000012345000098765", 30, 20,"-.00000001234500009876"); test_d2b2d("1234500009876.5", 30, 5,"1234500009876.50000"); test_d2b2d("111111111.11", 10, 2,"11111111.11"); + full=1; + test_d2b2d("123.4", 10, 2, "123.40"); printf("==== decimal_cmp ====\n"); test_dc("12","13",-1);