Commit a5ec80e3 authored by unknown's avatar unknown

Merge serg@work.mysql.com:/home/bk/mysql

into infomag.ape.relarn.ru:/usr/home/serg/src/mysql.safe/bk/mysql


Docs/manual.texi:
  Auto merged
parents c9fe3da0 712f6430
......@@ -13435,6 +13435,13 @@ mysql> CREATE TABLE test (
For @code{BLOB} and @code{TEXT} columns, you must index a prefix of the
column, you cannot index the entire thing.
In @strong{MySQL} 3.23.23 or later, you can also create special
@strong{fulltext} indexes. They are used for full-text search. Only
@code{MyISAM} table type supports fulltext indexes. They can be created
only from @code{VARCHAR}, @code{BLOB}, and @code{TEXT} columns.
Indexing always happens over the entire column, partial indexing is not
supported. See @ref{MySQL full-text search} for details of operation.
@node Multiple-column indexes, Other-vendor column types, Indexes, Column types
@subsection Multiple-column indexes
......@@ -14150,6 +14157,17 @@ mysql> select STRCMP('text2', 'text');
mysql> select STRCMP('text', 'text');
-> 0
@end example
@findex MATCH ... AGAINST()
@item MATCH (col1,col2,...) AGAINST (expr)
@code{MATCH ... AGAINST()} is used for full-text search and returns
relevance - similarity measure between the text in columns
@code{(col1,col2,...)} and the query @code{expr}. Relevance is a
positive floating point number. Zero relevance means no similarity.
For @code{MATCH ... AGAINST()} to work, a @strong{fulltext index}
must be created first. @xref{CREATE TABLE, , @code{CREATE TABLE}}.
@code{MATCH ... AGAINST()} is available in @code{MySQL} 3.23.23 or later.
For details and usage examples see @xref{MySQL full-text search}.
@end table
@findex Casts
......@@ -16159,6 +16177,7 @@ create_definition:
or KEY [index_name] (index_col_name,...)
or INDEX [index_name] (index_col_name,...)
or UNIQUE [INDEX] [index_name] (index_col_name,...)
or FULLTEXT [INDEX] [index_name] (index_col_name,...)
or [CONSTRAINT symbol] FOREIGN KEY index_name (index_col_name,...)
[reference_definition]
or CHECK (expr)
......@@ -16401,6 +16420,14 @@ When you use @code{ORDER BY} or @code{GROUP BY} with a @code{TEXT} or
@code{BLOB} column, only the first @code{max_sort_length} bytes are used.
@xref{BLOB, , @code{BLOB}}.
@item
In @strong{MySQL} 3.23.23 or later, you can also create special
@strong{fulltext} indexes. They are used for full-text search. Only
@code{MyISAM} table type supports fulltext indexes. They can be created
only from @code{VARCHAR}, @code{BLOB}, and @code{TEXT} columns.
Indexing always happens over the entire column, partial indexing is not
supported. See @ref{MySQL full-text search} for details of operation.
@item
The @code{FOREIGN KEY}, @code{CHECK} and @code{REFERENCES} clauses don't
actually do anything. The syntax for them is provided only for compatibility,
......@@ -16570,6 +16597,7 @@ alter_specification:
or ADD INDEX [index_name] (index_col_name,...)
or ADD PRIMARY KEY (index_col_name,...)
or ADD UNIQUE [index_name] (index_col_name,...)
or ADD FULLTEXT [index_name] (index_col_name,...)
or ALTER [COLUMN] col_name @{SET DEFAULT literal | DROP DEFAULT@}
or CHANGE [COLUMN] old_col_name create_definition
or MODIFY [COLUMN] create_definition
......@@ -19741,7 +19769,7 @@ dropped only with explicit @code{REVOKE} commands or by manipulating the
@section @code{CREATE INDEX} syntax
@example
CREATE [UNIQUE] INDEX index_name ON tbl_name (col_name[(length)],... )
CREATE [UNIQUE|FULLTEXT] INDEX index_name ON tbl_name (col_name[(length)],... )
@end example
The @code{CREATE INDEX} statement doesn't do anything in @strong{MySQL} prior
......@@ -19775,13 +19803,17 @@ which could save a lot of disk space and might also speed up @code{INSERT}
operations!
Note that you can only add an index on a column that can have @code{NULL}
values or on a @code{BLOB}/@code{TEXT} column if you are useing
values or on a @code{BLOB}/@code{TEXT} column if you are using
@strong{MySQL} version 3.23.2 or newer and are using the @code{MyISAM}
table type.
For more information about how @strong{MySQL} uses indexes, see
@ref{MySQL indexes, , @strong{MySQL} indexes}.
Fulltext indexes can index only @code{VARCHAR}, @code{BLOB}, and
@code{TEXT} columns, and only in @code{MyISAM} tables. Fulltext indexes
are available from @strong{MySQL} 3.23.23. @ref{MySQL full-text search}.
@findex DROP INDEX
@node DROP INDEX, Comments, CREATE INDEX, Reference
@section @code{DROP INDEX} syntax
......@@ -34010,9 +34042,10 @@ working on the @strong{MySQL} code.
@menu
* MySQL threads:: MySQL threads
* MySQL full-text search:: MySQL full-text search
@end menu
@node MySQL threads, , MySQL internals, MySQL internals
@node MySQL threads, MySQL full-text search, MySQL internals, MySQL internals
@section MySQL threads
The @strong{MySQL} server creates the the following threads:
......@@ -34051,6 +34084,77 @@ started to read and apply updates from the master.
@code{mysqladmin processlist} only shows the connection and @code{INSERT
DELAYED} threads.
@node MySQL full-text search, , MySQL threads, MySQL internals
@section MySQL full-text search
Since version 3.23.23, @strong{MySQL} has support for full-text indexing
and searching. Full-text index in @strong{MySQL} is an
index of type @code{FULLTEXT}. Fulltext indexes can be created from
@code{VARCHAR}, @code{TEXT}, and @code{BLOB} columns at
@code{CREATE TABLE} time or added later with @code{ALTER TABLE} or
@code{CREATE INDEX}. Full-text search is performed with @code{MATCH} function.
@example
mysql> CREATE TABLE t (a VARCHAR(200), b TEXT, FULLTEXT (a,b));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t VALUES
-> ('MySQL has now support', 'for full-text search'),
-> ('Full-text indexes', 'are called collections'),
-> ('Only MyISAM tables','support collections'),
-> ('Function MATCH ... AGAINST()','is used to do a search'),
-> ('Full-text search in MySQL', 'implements vector space model');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM t WHERE MATCH (a,b) AGAINST ('MySQL');
+---------------------------+-------------------------------+
| a | b |
+---------------------------+-------------------------------+
| MySQL has now support | for full-text search |
| Full-text search in MySQL | implements vector-space-model |
+---------------------------+-------------------------------+
2 rows in set (0.00 sec)
mysql> SELECT *,MATCH a,b AGAINST ('collections support') as x FROM t;
+------------------------------+-------------------------------+--------+
| a | b | x |
+------------------------------+-------------------------------+--------+
| MySQL has now support | for full-text search | 0.3834 |
| Full-text indexes | are called collections | 0.3834 |
| Only MyISAM tables | support collections | 0.7668 |
| Function MATCH ... AGAINST() | is used to do a search | 0 |
| Full-text search in MySQL | implements vector space model | 0 |
+------------------------------+-------------------------------+--------+
5 rows in set (0.00 sec)
@end example
Function @code{MATCH} matches a natural language query @code{AGAINST} a
text collection (which is simply the columns that are covered
by fulltext index). For every row in a table it returns relevance -
similarity measure between the text in that row (in the columns, that
are part of the collection) and the query. When it used in a
@code{WHERE} clause (see example above) the rows returned are
automatically sorted with relevance decreasing. Relevance is a non-
negative floating point number. Zero relevance means no similarity.
Relevance is computed based on number of words in the row and number of
unique words in that row, total number of words in the collection,
number of documents (rows), that contain a particular word, etc.
MySQL uses very simple parser to split text into words. "Word" is
any sequence of letters, numbers, @code{'}, and @code{_}. Any "word"
that is present in stopword list or just too short (3 characters or less)
is ignored.
Every correct word in the collection and in the query is weighted,
according to their significance in the query or collection. This way, a
word that is present in many documents will have lower weight (and may
even have a zero weight), because it has lower semantic value in this
particular collection. Otherwise, if the word is rare, it will receive a
higher weight. Weights of the words are then combined to compute the
relevance.
Such a technique works best with big collections (in fact, it was
carefully tuned up this way). For very small tables word distribution
does not reflect adequately their semantical value, and this model
may sometimes produce bizarre results.
@page
@node Environment variables, Users, MySQL internals, Top
......@@ -45,6 +45,7 @@ my_config.h: ../config.h
# This should be changed in the source and removed.
my_global.h: global.h
-$(RM) my_global.h
$(CP) global.h my_global.h
# These files should not be included in distributions since they are
......
......@@ -40,6 +40,8 @@ typedef struct st_ft_doclist {
FT_DOC doc[1];
} FT_DOCLIST;
extern const char *ft_precompiled_stopwords[];
int ft_init_stopwords(const char **);
FT_DOCLIST * ft_init_search(void *, uint, byte *, uint, my_bool);
......
......@@ -158,6 +158,7 @@ FT_DOCLIST * ft_init_search(void *info, uint keynr, byte *key,
ALL_IN_ONE aio;
FT_DOCLIST *dlist;
FT_DOC *dptr;
my_off_t saved_lastpos;
/* black magic ON */
if ((int) (keynr = _mi_check_index((MI_INFO *)info,keynr)) < 0)
......@@ -173,6 +174,8 @@ FT_DOCLIST * ft_init_search(void *info, uint keynr, byte *key,
aio.keyinfo=aio.info->s->keyinfo+keynr;
aio.key_root=aio.info->s->state.key_root[keynr];
saved_lastpos=aio.info->lastpos;
if(!(wtree=ft_parse(NULL,key,key_len))) return NULL;
init_tree(&aio.dtree,0,sizeof(FT_SUPERDOC),(qsort_cmp)&FT_SUPERDOC_cmp,0,
......@@ -199,6 +202,7 @@ FT_DOCLIST * ft_init_search(void *info, uint keynr, byte *key,
}
err:
aio.info->lastpos=saved_lastpos;
delete_tree(&aio.dtree);
delete_tree(wtree);
free(wtree);
......@@ -217,7 +221,8 @@ int ft_read_next(FT_DOCLIST *handler, char *record)
info->update&= (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED);
if (!(*info->read_record)(info,handler->doc[handler->curdoc].dpos,record))
info->lastpos=handler->doc[handler->curdoc].dpos;
if (!(*info->read_record)(info,info->lastpos,record))
{
info->update|= HA_STATE_AKTIV; /* Record is read */
return 0;
......
......@@ -35,8 +35,6 @@
extern const MI_KEYSEG ft_keysegs[FT_SEGS];
extern const char *ft_precompiled_stopwords[];
int _mi_ft_cmp(MI_INFO *, uint, const byte *, const byte *);
int _mi_ft_add(MI_INFO *, uint, byte *, const byte *, my_off_t);
int _mi_ft_del(MI_INFO *, uint, byte *, const byte *, my_off_t);
......@@ -1083,7 +1083,7 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
if (!(sort_info->record=(byte*) my_malloc((uint) share->base.pack_reclength,
MYF(0))))
{
mi_check_print_error(param,"Not Enough memory for extra record");
mi_check_print_error(param,"Not enough memory for extra record");
goto err;
}
......@@ -1141,6 +1141,14 @@ int mi_repair(MI_CHECK *param, register MI_INFO *info,
for (i=0 ; i < share->state.header.max_block_size ; i++)
share->state.key_del[i]= HA_OFFSET_ERROR;
/* I think mi_repair and mi_repair_by_sort should do the same
(according, e.g. to ha_myisam::repair), but as mi_repair doesn't
touch key_map it cannot be used to T_CREATE_MISSING_KEYS.
That is the next line for... (serg)
*/
share->state.key_map= ((ulonglong)1L << share->base.keys)-1;
info->state->key_file_length=share->base.keystart;
lock_memory(param); /* Everything is alloced */
......@@ -1270,11 +1278,18 @@ static int writekeys(register MI_INFO *info,byte *buff,my_off_t filepos)
for (i=0 ; i < info->s->base.keys ; i++)
{
if (((ulonglong) 1 << i) & info->s->state.key_map)
{
if (info->s->keyinfo[i].flag & HA_FULLTEXT )
{
if (_mi_ft_add(info,i,(char*) key,buff,filepos)) goto err;
}
else
{
uint key_length=_mi_make_key(info,i,key,buff,filepos);
if (_mi_ck_write(info,i,key,key_length)) goto err;
}
}
}
DBUG_RETURN(0);
err:
......@@ -1284,12 +1299,19 @@ static int writekeys(register MI_INFO *info,byte *buff,my_off_t filepos)
while ( i-- > 0 )
{
if (((ulonglong) 1 << i) & info->s->state.key_map)
{
if (info->s->keyinfo[i].flag & HA_FULLTEXT)
{
if (_mi_ft_del(info,i,(char*) key,buff,filepos)) break;
}
else
{
uint key_length=_mi_make_key(info,i,key,buff,filepos);
if (_mi_ck_delete(info,i,key,key_length)) break;
}
}
}
}
DBUG_PRINT("error",("errno: %d",my_errno));
DBUG_RETURN(-1);
} /* writekeys */
......@@ -1919,8 +1941,22 @@ static int sort_key_read(SORT_INFO *sort_info, void *key)
"Found too many records; Can`t continue");
DBUG_RETURN(1);
}
/* Hmm, repair_by_sort uses find_all_keys, and find_all_keys strictly
implies "one row - one key per keynr", while for ft_key one row/keynr
can produce as many keys as the number of unique words in the text
that's why I disabled repair_by_sort for ft-keys. (serg)
*/
if (sort_info->keyinfo->flag & HA_FULLTEXT )
{
mi_check_print_error(sort_info->param,
"Can`t use repair_by_sort with FULLTEXT key");
DBUG_RETURN(1);
}
else
{
VOID(_mi_make_key(info,sort_info->key,key,sort_info->record,
sort_info->filepos));
}
DBUG_RETURN(sort_write_record(sort_info));
} /* sort_key_read */
......@@ -2984,7 +3020,14 @@ my_bool mi_test_if_sort_rep(MI_INFO *info, ha_rows rows)
return FALSE; /* Can't use sort */
for (i=0 ; i < share->base.keys ; i++,key++)
{
if (mi_too_big_key_for_sort(key,rows))
/* It's to disable repair_by_sort for ft-keys.
Another solution would be to make ft-keys just too_big_key_for_sort,
but then they won't be disabled by dectivate_non_unique_index
and so they will be created at the first stage. As ft-key creation
is very time-consuming process, it's better to leave it to repair stage
but this repair shouldn't be repair_by_sort (serg)
*/
if (mi_too_big_key_for_sort(key,rows) || (key->flag & HA_FULLTEXT))
return FALSE;
}
return TRUE;
......
......@@ -372,6 +372,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK &param)
param.table_name = table->table_name;
param.tmpfile_createflag = O_RDWR | O_TRUNC;
param.using_global_keycache = 1;
param.thd=thd;
VOID(fn_format(fixed_name,file->filename,"",MI_NAME_IEXT,
4+ (param.opt_follow_links ? 16 : 0)));
......
......@@ -1837,26 +1837,8 @@ longlong Item_func_inet_aton::val_int()
double Item_func_match::val()
{
my_off_t docid=table->file->row_position(); // HAVE to do it here...
if (first_call)
{
if (join_key=(table->file->get_index() == key &&
(ft_handler=(FT_DOCLIST *)table->file->ft_handler)))
;
else
{
/* join won't use this ft-key, but we must to init it anyway */
String *ft_tmp=0;
char tmp1[FT_QUERY_MAXLEN];
String tmp2(tmp1,sizeof(tmp1));
ft_tmp=key_item()->val_str(&tmp2);
ft_handler=(FT_DOCLIST *)
table->file->ft_init_ext(key, (byte*) ft_tmp->ptr(), ft_tmp->length());
}
first_call=0;
}
init_search();
// Don't know how to return an error from val(), so NULL will be returned
if ((null_value=(ft_handler==NULL)))
......@@ -1873,6 +1855,7 @@ double Item_func_match::val()
int a,b,c;
FT_DOC *docs=ft_handler->doc;
my_off_t docid=table->file->row_position();
if ((null_value=(docid==HA_OFFSET_ERROR)))
return 0.0;
......@@ -1893,6 +1876,36 @@ double Item_func_match::val()
}
}
void Item_func_match::init_search()
{
if (!first_call)
return;
first_call=false;
if (master)
{
master->init_search();
ft_handler=master->ft_handler;
join_key=master->join_key;
return;
}
if (join_key)
{
ft_handler=((FT_DOCLIST *)table->file->ft_handler);
return;
}
/* join won't use this ft-key, but we must to init it anyway */
String *ft_tmp=0;
char tmp1[FT_QUERY_MAXLEN];
String tmp2(tmp1,sizeof(tmp1));
ft_tmp=key_item()->val_str(&tmp2);
ft_handler=(FT_DOCLIST *)
table->file->ft_init_ext(key, (byte*) ft_tmp->ptr(), ft_tmp->length());
}
bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist)
{
List_iterator<Item> li(fields);
......@@ -1982,6 +1995,24 @@ bool Item_func_match::fix_index()
this->key=max_key;
first_call=1;
maybe_null=1;
join_key=0;
return 0;
}
bool Item_func_match::eq(const Item *item) const
{
if (item->type() != FUNC_ITEM)
return 0;
if (func_name() != ((Item_func*)item)->func_name())
return 0;
Item_func_match *ifm=(Item_func_match*) item;
if (key == ifm->key && table == ifm->table &&
key_item()->eq(ifm->key_item()))
return 1;
return 0;
}
......
......@@ -839,19 +839,28 @@ class Item_func_match :public Item_real_func
TABLE *table;
uint key;
bool first_call, join_key;
Item_func_match *master;
FT_DOCLIST *ft_handler;
Item_func_match(List<Item> &a, Item *b): Item_real_func(b),
fields(a), table(0), ft_handler(0)
{}
~Item_func_match() { ft_close_search(ft_handler);
if(join_key) table->file->ft_handler=0; }
fields(a), table(0), ft_handler(0), master(0) {}
~Item_func_match()
{
if (!master)
{
ft_close_search(ft_handler);
if(join_key)
table->file->ft_handler=0;
}
}
const char *func_name() const { return "match"; }
enum Functype functype() const { return FT_FUNC; }
void update_used_tables() {}
bool fix_fields(THD *thd,struct st_table_list *tlist);
bool fix_index();
bool eq(const Item *) const;
double val();
longlong val_int() { return val()!=0.0; }
bool fix_index();
void init_search();
};
......@@ -81,7 +81,6 @@ static SYMBOL symbols[] = {
{ "CHANGED", SYM(CHANGED),0,0},
{ "CHECK", SYM(CHECK_SYM),0,0},
{ "CHECKSUM", SYM(CHECKSUM_SYM),0,0},
{ "COLLECTION", SYM(COLLECTION),0,0},
{ "COLUMN", SYM(COLUMN_SYM),0,0},
{ "COLUMNS", SYM(COLUMNS),0,0},
{ "COMMENT", SYM(COMMENT_SYM),0,0},
......@@ -142,6 +141,7 @@ static SYMBOL symbols[] = {
{ "FROM", SYM(FROM),0,0},
{ "FOR", SYM(FOR_SYM),0,0},
{ "FULL", SYM(FULL),0,0},
{ "FULLTEXT", SYM(FULLTEXT_SYM),0,0},
{ "FUNCTION", SYM(UDF_SYM),0,0},
{ "GRANT", SYM(GRANT),0,0},
{ "GRANTS", SYM(GRANTS),0,0},
......
......@@ -1455,7 +1455,7 @@ int main(int argc, char **argv)
sql_print_error("Can't init databases");
exit(1);
}
ft_init_stopwords(NULL); /* SerG */
ft_init_stopwords(ft_precompiled_stopwords); /* SerG */
#ifdef __WIN__
#define MYSQL_ERR_FILE "mysql.err"
......
......@@ -2172,18 +2172,22 @@ bool remove_table_from_cache(THD *thd, const char *db,const char *table_name)
DBUG_RETURN(result);
}
/*
Will be used for ft-query optimization someday.
SerG.
*/
int setup_ftfuncs(THD *thd,TABLE_LIST *tables, List<Item_func_match> &ftfuncs)
{
List_iterator<Item_func_match> li(ftfuncs);
Item_func_match *ftf;
List_iterator<Item_func_match> li(ftfuncs), li2(ftfuncs);
Item_func_match *ftf, *ftf2;
while ((ftf=li++))
{
if (ftf->fix_index())
return 1;
li2.rewind();
while ((ftf2=li2++) != ftf)
{
if (ftf->eq(ftf2) && !ftf2->master)
ftf2->master=ftf;
}
}
return 0;
}
......@@ -1284,11 +1284,11 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
KEYUSE keyuse;
keyuse.table= cond_func->table;
keyuse.val = cond_func->key_item();
keyuse.val = cond_func;
keyuse.key = cond_func->key;
#define FT_KEYPART (MAX_REF_PARTS+10)
keyuse.keypart=FT_KEYPART;
keyuse.used_tables=keyuse.val->used_tables();
keyuse.used_tables=cond_func->key_item()->used_tables();
VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
}
......@@ -1670,7 +1670,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
else
tmp=best_time; // Do nothing
}
} /* not ftkey */
} /* not ft_key */
if (tmp < best_time - records/(double) TIME_FOR_COMPARE)
{
best_time=tmp + records/(double) TIME_FOR_COMPARE;
......@@ -1882,9 +1882,12 @@ get_best_combination(JOIN *join)
keyinfo=table->key_info+key;
if (ftkey)
{
ft_tmp=keyuse->val->val_str(&tmp2);
Item_func_match *ifm=(Item_func_match *)keyuse->val;
ft_tmp=ifm->key_item()->val_str(&tmp2);
length=ft_tmp->length();
keyparts=1;
ifm->join_key=1;
}
else
{
......@@ -1924,7 +1927,7 @@ get_best_combination(JOIN *join)
byte *key_buff=j->ref.key_buff;
if (ftkey)
{
j->ref.items[0]=keyuse->val;
j->ref.items[0]=((Item_func*)(keyuse->val))->key_item();
if (!keyuse->used_tables &&
!(join->select_options & SELECT_DESCRIBE))
{
......
......@@ -137,7 +137,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token CHANGED_FILES
%token CHECKSUM_SYM
%token CHECK_SYM
%token COLLECTION
%token COLUMNS
%token COLUMN_SYM
%token CONSTRAINT
......@@ -162,6 +161,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token FOREIGN
%token FROM
%token FULL
%token FULLTEXT_SYM
%token GRANT
%token GRANTS
%token GREATEST_SYM
......@@ -457,7 +457,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
expr_list udf_expr_list when_list ident_list
%type <key_type>
key_type opt_unique
key_type opt_unique_or_fulltext
%type <string_list>
key_usage_list
......@@ -628,7 +628,7 @@ create:
}
create2
| CREATE opt_unique INDEX ident ON table_ident
| CREATE opt_unique_or_fulltext INDEX ident ON table_ident
{
Lex->sql_command= SQLCOM_CREATE_INDEX;
if (!add_table_to_list($6,NULL))
......@@ -643,21 +643,6 @@ create:
Lex->key_list.push_back(new Key($2,$4.str,Lex->col_list));
Lex->col_list.empty();
}
| CREATE COLLECTION ident ON table_ident
{
Lex->sql_command= SQLCOM_CREATE_INDEX;
if (!add_table_to_list($5,NULL))
YYABORT;
Lex->create_list.empty();
Lex->key_list.empty();
Lex->col_list.empty();
Lex->change=NullS;
}
'(' key_list ')'
{
Lex->key_list.push_back(new Key(Key::FULLTEXT,$3.str,Lex->col_list));
Lex->col_list.empty();
}
| CREATE DATABASE opt_if_not_exists ident
{
Lex->sql_command=SQLCOM_CREATE_DB;
......@@ -964,7 +949,8 @@ delete_option:
key_type:
opt_constraint PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; }
| key_or_index { $$= Key::MULTIPLE; }
| COLLECTION { $$= Key::FULLTEXT; }
| FULLTEXT_SYM { $$= Key::FULLTEXT; }
| FULLTEXT_SYM key_or_index { $$= Key::FULLTEXT; }
| opt_constraint UNIQUE_SYM { $$= Key::UNIQUE; }
| opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; }
......@@ -976,9 +962,10 @@ keys_or_index:
KEYS {}
| INDEX {}
opt_unique:
opt_unique_or_fulltext:
/* empty */ { $$= Key::MULTIPLE; }
| UNIQUE_SYM { $$= Key::UNIQUE; }
| FULLTEXT_SYM { $$= Key::FULLTEXT; }
key_list:
key_list ',' key_part order_dir { Lex->col_list.push_back($3); }
......@@ -2443,7 +2430,6 @@ keyword:
| WORK_SYM {}
| YEAR_SYM {}
| SLAVE {}
| COLLECTION {}
/* Option functions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment