Below is the list of changes that have just been committed into a local
5.1 repository of cmiller. When cmiller does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-07-12 10:49:37-04:00, cmiller@stripped +7 -0
Bug#27175: invalid colum attributes in create/alter table
One could define columns with contradictory or duplicate attri-
butes. E.g. "NULL NOT NULL DEFAULT 'foo' DEFAULT 'bar' NULL NULL"
Now, check that explicitly-set or -unset flags are never reset
in the same statement.
include/mysql_com.h@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +3 -0
Add flags to assist with deteting contradiction and conflicts.
mysql-test/r/column_creation.result@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +53 -0
Verify duplication and contradiction raises errors.
mysql-test/r/column_creation.result@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +0 -0
mysql-test/r/multi_update.result@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +1 -1
"NULL AUTO INCREMENT" is nonsensical, and should have raised an
error.
mysql-test/t/column_creation.test@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +69 -0
Verify duplication and contradiction raises errors.
mysql-test/t/column_creation.test@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +0 -0
mysql-test/t/multi_update.test@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +1 -1
"NULL AUTO INCREMENT" is nonsensical, and should have raised an
error.
sql/sql_lex.h@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +74 -0
Add members and methods to assist with setting flags and noticing
when column attributes are contradictory.
sql/sql_yacc.yy@stripped, 2007-07-12 10:49:34-04:00, cmiller@stripped +96 -48
Insert an intermediate rule so that we reset flags that represent
the used attributes, and can then detect conflict, duplication,
and contradiction.
Use some functions to set flags and notice when flags cause errors.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: cmiller
# Host: zippy.cornsilk.net
# Root: /home/cmiller/work/mysql/bug27175/my51-bug27175
--- 1.122/include/mysql_com.h 2007-06-05 11:31:37 -04:00
+++ 1.123/include/mysql_com.h 2007-07-12 10:49:34 -04:00
@@ -103,6 +103,9 @@ enum enum_server_command
#define FIELD_IN_PART_FUNC_FLAG (1 << 19)/* Field part of partition func */
#define FIELD_IN_ADD_INDEX (1<< 20) /* Intern: Field used in ADD INDEX */
#define FIELD_IS_RENAMED (1<< 21) /* Intern: Field is being renamed */
+#define COL_HAS_DEFAULT (1<< 22) /* Intern: Used by sql_yacc */
+#define COL_SERIAL_VAL (1<< 23) /* Intern: Used by sql_yacc */
+#define COL_HAS_COMMENT (1<< 24) /* Intern: Used by sql_yacc */
#define REFRESH_GRANT 1 /* Refresh grant tables */
#define REFRESH_LOG 2 /* Start on new log file */
--- 1.283/sql/sql_lex.h 2007-06-21 11:12:55 -04:00
+++ 1.284/sql/sql_lex.h 2007-07-12 10:49:34 -04:00
@@ -1733,6 +1733,80 @@ typedef struct st_lex : public Query_tab
bool table_or_sp_used();
bool is_partition_management() const;
+
+ /**
+ Mask of bits used in "type", to keep track of each bit which has been
+ explicitly set to 0 or 1. This allows to detect conflicts between user
+ options. Otherwise a user could specify contradictory attributes as in
+ "c int PRIMARY KEY NULL" .
+ */
+ ulong type_explicit;
+
+ /**
+ Set attribute bits in "type", and answer if attempting to add these is an
+ error. It would be an error if any given bits were previously explicitly
+ unset.
+
+ @retval FALSE All bits were set successfully
+ @retval TRUE Some bits could not be set. Contradiction!
+ */
+ inline my_bool set_type_bits(ulong bits)
+ {
+ if ((type_explicit & bits) != (type_explicit & bits & type))
+ {
+ DBUG_PRINT("warning", ("type 0x%lx and bits 0x%lx", type, bits));
+ return TRUE; /* some bits we want to set were explicitly unset */
+ }
+ type|= bits;
+ type_explicit|= bits;
+ return FALSE;
+ }
+
+ /**
+ Unset attribute bits in "type", and answer if attempting to add these is
+ an error. It would be an error if any given bits were previously
+ explicitly set.
+
+ @retval FALSE All bits were unset successfully
+ @retval TRUE Some bits could not be unset. Contradiction!
+ */
+ inline my_bool unset_type_bits(ulong bits)
+ {
+ if ((type_explicit & bits) != (type_explicit & bits & (~type)))
+ {
+ DBUG_PRINT("warning", ("type 0x%lx and bits 0x%lx", type, bits));
+ return TRUE; /* some we want to unset were explicitly set */
+ }
+ type&= ~bits;
+ type_explicit|= bits;
+ return FALSE;
+ }
+
+ /**
+ Mask of attribute bits used in column definition.
+ */
+ ulong attrib_used;
+
+ /**
+ Note that an attribute is to be used, and return whether it
+ was already set, returning an answer "is this an error?".
+ For example, erroneous: "c char(1) default 'a' default 'b'" .
+
+ @retval TRUE This attribute was already given.
+ @retval FALSE New attribute noted.
+ */
+ inline my_bool set_attrib_is_used(ulong attrib)
+ {
+ if ((attrib_used & attrib) != 0)
+ {
+ DBUG_PRINT("warning", ("attribute used = 0x%lx setting = 0x%lx",
+ attrib_used, attrib));
+ return TRUE;
+ }
+ attrib_used|= attrib;
+ return FALSE;
+ }
+
} LEX;
struct st_lex_local: public st_lex
--- 1.581/sql/sql_yacc.yy 2007-06-21 12:41:29 -04:00
+++ 1.582/sql/sql_yacc.yy 2007-07-12 10:49:34 -04:00
@@ -1107,7 +1107,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
text_string opt_gconcat_separator
%type <num>
- type int_type real_type order_dir lock_option
+ type type_continued int_type real_type order_dir lock_option
udf_type if_exists opt_local opt_table_options table_options
table_option opt_if_not_exists opt_no_write_to_binlog
delete_option opt_temporary all_or_any opt_distinct
@@ -4564,6 +4564,14 @@ field_spec:
};
type:
+ {
+ Lex->type_explicit= 0;
+ Lex->attrib_used= 0;
+ }
+ type_continued { $$=$2; };
+
+
+type_continued:
int_type opt_len field_options { $$=$1; }
| real_type opt_precision field_options { $$=$1; }
| FLOAT_SYM float_options field_options { $$=MYSQL_TYPE_FLOAT; }
@@ -4610,6 +4618,10 @@ type:
{
/*
Unlike other types TIMESTAMP fields are NOT NULL by default.
+
+ Don't use set_type_bits() here, as the magical non-nullness
+ should never cause an error if someone naturally wishes to
+ specify "NOT NULL" .
*/
Lex->type|= NOT_NULL_FLAG;
$$=MYSQL_TYPE_TIMESTAMP;
@@ -4657,7 +4669,7 @@ type:
| SERIAL_SYM
{
$$=MYSQL_TYPE_LONGLONG;
- Lex->type|= (AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
+ Lex->set_type_bits(AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG |
UNIQUE_FLAG);
}
;
@@ -4732,9 +4744,22 @@ field_opt_list:
| field_option {};
field_option:
- SIGNED_SYM {}
- | UNSIGNED { Lex->type|= UNSIGNED_FLAG;}
- | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; };
+ SIGNED_SYM
+ {
+ DBUG_PRINT("chad", ("signed"));
+ MYSQL_YYABORT_UNLESS(! (Lex->type_explicit & UNSIGNED_FLAG) &&
+ ! (Lex->unset_type_bits(UNSIGNED_FLAG)));
+ }
+ | UNSIGNED {
+ DBUG_PRINT("chad", ("unsigned"));
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(UNSIGNED_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(UNSIGNED_FLAG));
+ }
+ | ZEROFILL {
+ DBUG_PRINT("chad", ("zerofill"));
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(ZEROFILL_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(UNSIGNED_FLAG | ZEROFILL_FLAG));
+ };
opt_len:
/* empty */ { Lex->length=(char*) 0; } /* use default length */
@@ -4753,51 +4778,74 @@ opt_attribute_list:
| attribute;
attribute:
- NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
- | not NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
- | DEFAULT now_or_signed_literal { Lex->default_value=$2; }
- | ON UPDATE_SYM NOW_SYM optional_braces
- { Lex->on_update_value= new Item_func_now_local(); }
- | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
- | SERIAL_SYM DEFAULT VALUE_SYM
- {
- LEX *lex=Lex;
- lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | opt_primary KEY_SYM
- {
- LEX *lex=Lex;
- lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | UNIQUE_SYM
- {
- LEX *lex=Lex;
- lex->type|= UNIQUE_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | UNIQUE_SYM KEY_SYM
- {
- LEX *lex=Lex;
- lex->type|= UNIQUE_KEY_FLAG;
- lex->alter_info.flags|= ALTER_ADD_INDEX;
- }
- | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; }
- | COLLATE_SYM collation_name
- {
- if (Lex->charset && !my_charset_same(Lex->charset,$2))
- {
+ NULL_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(NOT_NULL_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->unset_type_bits(NOT_NULL_FLAG));
+ }
+ | not NULL_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(NOT_NULL_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(NOT_NULL_FLAG));
+ }
+ | DEFAULT now_or_signed_literal
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(COL_HAS_DEFAULT));
+ Lex->default_value=$2;
+ }
+ | ON UPDATE_SYM NOW_SYM optional_braces
+ {
+ MYSQL_YYABORT_UNLESS(Lex->on_update_value == 0);
+ Lex->on_update_value= new Item_func_now_local();
+ }
+ | AUTO_INC
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(AUTO_INCREMENT_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(AUTO_INCREMENT_FLAG | NOT_NULL_FLAG));
+ }
+ | SERIAL_SYM DEFAULT VALUE_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(COL_SERIAL_VAL));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_FLAG));
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | opt_primary KEY_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(PRI_KEY_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(PRI_KEY_FLAG | NOT_NULL_FLAG));
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | UNIQUE_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(UNIQUE_FLAG));
+ MYSQL_YYABORT_UNLESS(! Lex->set_type_bits(UNIQUE_FLAG));
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | UNIQUE_SYM KEY_SYM
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(UNIQUE_FLAG));
+ Lex->type|= UNIQUE_KEY_FLAG;
+ Lex->alter_info.flags|= ALTER_ADD_INDEX;
+ }
+ | COMMENT_SYM TEXT_STRING_sys
+ {
+ MYSQL_YYABORT_UNLESS(! Lex->set_attrib_is_used(COL_HAS_COMMENT));
+ Lex->comment= $2;
+ }
+ | COLLATE_SYM collation_name
+ {
+ if (Lex->charset && !my_charset_same(Lex->charset,$2))
+ {
my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0),
$2->name,Lex->charset->csname);
- MYSQL_YYABORT;
- }
- else
- {
- Lex->charset=$2;
- }
- }
- ;
+ MYSQL_YYABORT;
+ }
+ else
+ {
+ Lex->charset=$2;
+ }
+ }
+ ;
now_or_signed_literal:
NOW_SYM optional_braces { $$= new Item_func_now_local(); }
--- New file ---
+++ mysql-test/r/column_creation.result 07/07/12 10:49:34
drop table if exists t1;
create table t1 (a int(11) unsigned unsigned );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'unsigned )' at line 1
create table t1 (a int(11) signed unsigned );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'unsigned )' at line 1
create table t1 (a int(11) unsigned signed );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'signed )' at line 1
create table t1 (a int(11) signed signed );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'signed )' at line 1
create table t1 (a int(11) zerofill zerofill );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'zerofill )' at line 1
create table t1 (a int(11) not null not null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int(11) null null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int(11) null not null )
--error ER_PARSE_ERROR
create table t1 (a int(11) not null null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )
--error ER_PARSE_ERROR
create table t1 (a int(11) not null null )' at line 1
create table t1 (a timestamp on update now() on update now() );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ') )' at line 1
create table t1 (a int(11) comment 'a' comment 'b' );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''b' )' at line 1
create table t1 (a int(11) default 1 default 2 );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '2 )' at line 1
create table t1 (a int(11) primary key primary key );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'key )' at line 1
create table t1 (a int(11) primary key key );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'key )' at line 1
create table t1 (a int(11) unique key unique key );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'key )' at line 1
create table t1 (a int(11) unique key unique );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1
create table t1 (a int(11) primary key unique key );
drop table t1;
create table t1 (a timestamp not null);
drop table t1;
create table t1 (a int(11) not null primary key null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int(11) auto_increment unique null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int(11) unique key auto_increment null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a serial null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int(11) serial default value null );
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null )' at line 1
create table t1 (a int null);
alter table t1 change a a int null not null;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'null' at line 1
drop table t1;
--- New file ---
+++ mysql-test/t/column_creation.test 07/07/12 10:49:34
#
# Bug#27175: invalid column attributes in create/alter table
#
# The parser for column type/attribute is often not strict enough. This
# results in the ability to declare nonsense and/or invalid columns.
--disable_warnings
drop table if exists t1;
--enable_warnings
--error ER_PARSE_ERROR
create table t1 (a int(11) unsigned unsigned );
--error ER_PARSE_ERROR
create table t1 (a int(11) signed unsigned );
--error ER_PARSE_ERROR
create table t1 (a int(11) unsigned signed );
--error ER_PARSE_ERROR
create table t1 (a int(11) signed signed );
--error ER_PARSE_ERROR
create table t1 (a int(11) zerofill zerofill );
--error ER_PARSE_ERROR
create table t1 (a int(11) not null not null );
--error ER_PARSE_ERROR
create table t1 (a int(11) null null );
--error ER_PARSE_ERROR
create table t1 (a int(11) null not null )
--error ER_PARSE_ERROR
create table t1 (a int(11) not null null );
--error ER_PARSE_ERROR
create table t1 (a timestamp on update now() on update now() );
--error ER_PARSE_ERROR
create table t1 (a int(11) comment 'a' comment 'b' );
--error ER_PARSE_ERROR
create table t1 (a int(11) default 1 default 2 );
--error ER_PARSE_ERROR
create table t1 (a int(11) primary key primary key );
--error ER_PARSE_ERROR
create table t1 (a int(11) primary key key );
--error ER_PARSE_ERROR
create table t1 (a int(11) unique key unique key );
--error ER_PARSE_ERROR
create table t1 (a int(11) unique key unique );
# next should work
create table t1 (a int(11) primary key unique key );
drop table t1;
create table t1 (a timestamp not null);
drop table t1;
--error ER_PARSE_ERROR
create table t1 (a int(11) not null primary key null );
--error ER_PARSE_ERROR
create table t1 (a int(11) auto_increment unique null );
--error ER_PARSE_ERROR
create table t1 (a int(11) unique key auto_increment null );
--error ER_PARSE_ERROR
create table t1 (a serial null );
--error ER_PARSE_ERROR
create table t1 (a int(11) serial default value null );
create table t1 (a int null);
--error ER_PARSE_ERROR
alter table t1 change a a int null not null;
drop table t1;
--- 1.50/mysql-test/r/multi_update.result 2007-06-21 15:23:19 -04:00
+++ 1.51/mysql-test/r/multi_update.result 2007-07-12 10:49:34 -04:00
@@ -427,7 +427,7 @@ a
2
DROP TABLE t1,t2;
create table `t1` (`p_id` int(10) unsigned NOT NULL auto_increment, `p_code` varchar(20) NOT NULL default '', `p_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`p_id`) );
-create table `t2` (`c2_id` int(10) unsigned NULL auto_increment, `c2_p_id` int(10) unsigned NOT NULL default '0', `c2_note` text NOT NULL, `c2_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`c2_id`), KEY `c2_p_id` (`c2_p_id`) );
+create table `t2` (`c2_id` int(10) unsigned NOT NULL auto_increment, `c2_p_id` int(10) unsigned NOT NULL default '0', `c2_note` text NOT NULL, `c2_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`c2_id`), KEY `c2_p_id` (`c2_p_id`) );
insert into t1 values (0,'A01-Comp',1);
insert into t1 values (0,'B01-Comp',1);
insert into t2 values (0,1,'A Note',1);
--- 1.56/mysql-test/t/multi_update.test 2007-06-21 15:02:09 -04:00
+++ 1.57/mysql-test/t/multi_update.test 2007-07-12 10:49:34 -04:00
@@ -383,7 +383,7 @@ DROP TABLE t1,t2;
# Test update with const tables
#
create table `t1` (`p_id` int(10) unsigned NOT NULL auto_increment, `p_code` varchar(20) NOT NULL default '', `p_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`p_id`) );
-create table `t2` (`c2_id` int(10) unsigned NULL auto_increment, `c2_p_id` int(10) unsigned NOT NULL default '0', `c2_note` text NOT NULL, `c2_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`c2_id`), KEY `c2_p_id` (`c2_p_id`) );
+create table `t2` (`c2_id` int(10) unsigned NOT NULL auto_increment, `c2_p_id` int(10) unsigned NOT NULL default '0', `c2_note` text NOT NULL, `c2_active` tinyint(1) unsigned NOT NULL default '1', PRIMARY KEY (`c2_id`), KEY `c2_p_id` (`c2_p_id`) );
insert into t1 values (0,'A01-Comp',1);
insert into t1 values (0,'B01-Comp',1);
insert into t2 values (0,1,'A Note',1);