Commit 9f45c9e3 authored by serg@serg.mylan's avatar serg@serg.mylan

followup to handler cleanup

parent cfd78595
......@@ -1181,3 +1181,12 @@ a
A
a
drop table t1;
set autocommit=0;
create table t1(b varchar(30)) engine=bdb;
insert into t1 values ('one');
commit;
select b FROM t1 outer_table where
exists (select 'two' from t1 where 'two' = outer_table.b);
b
drop table t1;
set autocommit=1;
......@@ -822,3 +822,28 @@ alter table t1 modify a char(10) binary;
explain select a from t1;
select a from t1;
drop table t1;
#
# Bug #4000: problem with active cursor.
#
set autocommit=0;
create table t1(b varchar(30)) engine=bdb;
insert into t1 values ('one');
commit;
select b FROM t1 outer_table where
exists (select 'two' from t1 where 'two' = outer_table.b);
drop table t1;
set autocommit=1;
#
# Bug #4089: subselect and open cursor.
#
#create table t1(a int primary key, b varchar(30)) engine=bdb;
#insert into t1 values (1,'one'), (2,'two'), (3,'three'), (4,'four');
#create table t2 like t1;
#insert into t2 (a, b)
# select a, b from t1 where (a, b) in (select a, b from t1);
#select * from t2;
#drop table t1, t2;
......@@ -481,13 +481,13 @@ int ha_archive::update_row(const byte * old_data, byte * new_data)
{
DBUG_ENTER("ha_archive::update_row");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::delete_row(const byte * buf)
{
DBUG_ENTER("ha_archive::delete_row");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_read(byte * buf, const byte * key,
......@@ -496,7 +496,7 @@ int ha_archive::index_read(byte * buf, const byte * key,
__attribute__((unused)))
{
DBUG_ENTER("ha_archive::index_read");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_read_idx(byte * buf, uint index, const byte * key,
......@@ -505,32 +505,32 @@ int ha_archive::index_read_idx(byte * buf, uint index, const byte * key,
__attribute__((unused)))
{
DBUG_ENTER("ha_archive::index_read_idx");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_next(byte * buf)
{
DBUG_ENTER("ha_archive::index_next");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_prev(byte * buf)
{
DBUG_ENTER("ha_archive::index_prev");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_first(byte * buf)
{
DBUG_ENTER("ha_archive::index_first");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_archive::index_last(byte * buf)
{
DBUG_ENTER("ha_archive::index_last");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -581,6 +581,6 @@ ha_rows ha_archive::records_in_range(int inx,
enum ha_rkey_function end_search_flag)
{
DBUG_ENTER("ha_archive::records_in_range ");
DBUG_RETURN(records); // HA_ERR_NOT_IMPLEMENTED
DBUG_RETURN(records); // HA_ERR_WRONG_COMMAND
}
#endif /* HAVE_ARCHIVE_DB */
......@@ -22,7 +22,7 @@
/*
Please read ha_archive.cc first. If you are looking for more general
answers on how storage engines work, look at ha_example.cc and
answers on how storage engines work, look at ha_example.cc and
ha_example.h.
*/
......@@ -36,7 +36,7 @@ typedef struct st_archive_share {
bool dirty; /* Flag for if a flush should occur */
} ARCHIVE_SHARE;
/*
/*
Version for file format.
1 - Initial Version
*/
......@@ -61,7 +61,7 @@ public:
/* The size of the offset value we will use for position() */
ref_length = sizeof(z_off_t);
}
~ha_archive()
~ha_archive()
{
}
const char *table_type() const { return "ARCHIVE"; }
......@@ -69,21 +69,18 @@ public:
const char **bas_ext() const;
ulong table_flags() const
{
return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED |
HA_NO_AUTO_INCREMENT);
return (HA_REC_NOT_IN_SEQ | HA_NOT_EXACT_COUNT | HA_NO_AUTO_INCREMENT |
HA_FILE_BASED);
}
ulong index_flags(uint inx) const
ulong index_flags(uint idx, uint part) const
{
return 0;
}
/*
This is just a default, there is no real limit as far as
/*
Have to put something here, there is no real limit as far as
archive is concerned.
*/
uint max_record_length() const { return HA_MAX_REC_LENGTH; }
uint max_keys() const { return 0; }
uint max_key_parts() const { return 0; }
uint max_key_length() const { return 0; }
uint max_supported_record_length() const { return UINT_MAX; }
/*
Called in test_quick_select to determine if indexes should be used.
*/
......
......@@ -14,24 +14,24 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
ha_example is a stubbed storage engine. It does nothing at this point. It
will let you create/open/delete tables but that is all. You can enable it
/*
ha_example is a stubbed storage engine. It does nothing at this point. It
will let you create/open/delete tables but that is all. You can enable it
in your buld by doing the following during your build process:
./configure --with-example-storage-engine
Once this is done mysql will let you create tables with:
CREATE TABLE A (...) ENGINE=EXAMPLE;
The example is setup to use table locks. It implements an example "SHARE"
that is inserted into a hash by table name. You can use this to store
that is inserted into a hash by table name. You can use this to store
information of state that any example handler object will be able to see
if it is using the same table.
Please read the object definition in ha_example.h before reading the rest
Please read the object definition in ha_example.h before reading the rest
if this file.
To get an idea of what occurs here is an example select that would do a
To get an idea of what occurs here is an example select that would do a
scan of an entire table:
ha_example::store_lock
ha_example::external_lock
......@@ -50,13 +50,13 @@
ha_example::rnd_next
ha_example::extra
ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
ha_example::external_lock
ha_example::external_lock
ha_example::extra
ENUM HA_EXTRA_RESET Reset database to after open
In the above example has 9 row called before rnd_next signalled that it was
at the end of its data. In the above example the table was already opened
(or you would have seen a call to ha_example::open(). Calls to
In the above example has 9 row called before rnd_next signalled that it was
at the end of its data. In the above example the table was already opened
(or you would have seen a call to ha_example::open(). Calls to
ha_example::extra() are hints as to what will be occuring to the request.
Happy coding!
......@@ -92,7 +92,7 @@ static byte* example_get_key(EXAMPLE_SHARE *share,uint *length,
/*
Example of simple lock controls. The "share" it creates is structure we will
pass to each example handler. Do you have to have one of these? Well, you have
pieces that are used for locking, and they are needed to function.
pieces that are used for locking, and they are needed to function.
*/
static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table)
{
......@@ -130,7 +130,7 @@ static EXAMPLE_SHARE *get_share(const char *table_name, TABLE *table)
my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
&share, sizeof(*share),
&tmp_name, length+1,
NullS)))
NullS)))
{
pthread_mutex_unlock(&example_mutex);
return NULL;
......@@ -161,7 +161,7 @@ error:
}
/*
/*
Free lock controls. We call this whenever we close a table. If the table had
the last reference to the share then we free memory associated with it.
*/
......@@ -182,7 +182,7 @@ static int free_share(EXAMPLE_SHARE *share)
/*
If frm_error() is called then we will use this to to find out what file extentions
If frm_error() is called then we will use this to to find out what file extentions
exist for the storage engine. This is also used by the default rename_table and
delete_table method in handler.cc.
*/
......@@ -190,10 +190,10 @@ const char **ha_example::bas_ext() const
{ static const char *ext[]= { NullS }; return ext; }
/*
/*
Used for opening tables. The name will be the name of the file.
A table is opened when it needs to be opened. For instance
when a request comes in for a select on the table (tables are not
when a request comes in for a select on the table (tables are not
open and closed for each request, they are cached).
Called from handler.cc by handler::ha_open(). The server opens all tables by
......@@ -212,12 +212,12 @@ int ha_example::open(const char *name, int mode, uint test_if_locked)
/*
Closes a table. We call the free_share() function to free any resources
Closes a table. We call the free_share() function to free any resources
that we have allocated in the "shared" structure.
Called from sql_base.cc, sql_select.cc, and table.cc.
In sql_select.cc it is only used to close up temporary tables or during
the process where a temporary table is converted over to being a
the process where a temporary table is converted over to being a
myisam table.
For sql_base.cc look at close_data_tables().
*/
......@@ -230,7 +230,7 @@ int ha_example::close(void)
/*
write_row() inserts a row. No extra() hint is given currently if a bulk load
is happeneding. buf() is a byte array of data. You can use the field
is happeneding. buf() is a byte array of data. You can use the field
information to extract the data from the native byte array type.
Example of this would be:
for (Field **field=table->field ; *field ; field++)
......@@ -238,20 +238,20 @@ int ha_example::close(void)
...
}
See ha_tina.cc for an example of extracting all of the data as strings.
See ha_tina.cc for an example of extracting all of the data as strings.
ha_berekly.cc has an example of how to store it intact by "packing" it
for ha_berkeley's own native storage type.
See the note for update_row() on auto_increments and timestamps. This
case also applied to write_row().
Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
Called from item_sum.cc, item_sum.cc, sql_acl.cc, sql_insert.cc,
sql_insert.cc, sql_select.cc, sql_table.cc, sql_udf.cc, and sql_update.cc.
*/
int ha_example::write_row(byte * buf)
{
DBUG_ENTER("ha_example::write_row");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -274,7 +274,7 @@ int ha_example::update_row(const byte * old_data, byte * new_data)
{
DBUG_ENTER("ha_example::update_row");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -282,8 +282,8 @@ int ha_example::update_row(const byte * old_data, byte * new_data)
This will delete a row. buf will contain a copy of the row to be deleted.
The server will call this right after the current row has been called (from
either a previous rnd_nexT() or index call).
If you keep a pointer to the last row or can access a primary key it will
make doing the deletion quite a bit easier.
If you keep a pointer to the last row or can access a primary key it will
make doing the deletion quite a bit easier.
Keep in mind that the server does no guarentee consecutive deletions. ORDER BY
clauses can be used.
......@@ -294,7 +294,7 @@ int ha_example::update_row(const byte * old_data, byte * new_data)
int ha_example::delete_row(const byte * buf)
{
DBUG_ENTER("ha_example::delete_row");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -309,7 +309,7 @@ int ha_example::index_read(byte * buf, const byte * key,
__attribute__((unused)))
{
DBUG_ENTER("ha_example::index_read");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -323,7 +323,7 @@ int ha_example::index_read_idx(byte * buf, uint index, const byte * key,
__attribute__((unused)))
{
DBUG_ENTER("ha_example::index_read_idx");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -333,7 +333,7 @@ int ha_example::index_read_idx(byte * buf, uint index, const byte * key,
int ha_example::index_next(byte * buf)
{
DBUG_ENTER("ha_example::index_next");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -343,40 +343,40 @@ int ha_example::index_next(byte * buf)
int ha_example::index_prev(byte * buf)
{
DBUG_ENTER("ha_example::index_prev");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
/*
index_first() asks for the first key in the index.
Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
and sql_select.cc.
*/
int ha_example::index_first(byte * buf)
{
DBUG_ENTER("ha_example::index_first");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
/*
index_last() asks for the last key in the index.
Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
Called from opt_range.cc, opt_sum.cc, sql_handler.cc,
and sql_select.cc.
*/
int ha_example::index_last(byte * buf)
{
DBUG_ENTER("ha_example::index_last");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
/*
/*
rnd_init() is called when the system wants the storage engine to do a table
scan.
See the example in the introduction at the top of this file to see when
scan.
See the example in the introduction at the top of this file to see when
rnd_init() is called.
Called from filesort.cc, records.cc, sql_handler.cc, sql_select.cc, sql_table.cc,
......@@ -385,11 +385,16 @@ int ha_example::index_last(byte * buf)
int ha_example::rnd_init(bool scan)
{
DBUG_ENTER("ha_example::rnd_init");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
int ha_example::rnd_end()
{
DBUG_ENTER("ha_example::rnd_end");
DBUG_RETURN(0);
}
/*
/*
This is called for each row of the table scan. When you run out of records
you should return HA_ERR_END_OF_FILE. Fill buff up with the row information.
The Field structure for the table is the key to getting data into buf
......@@ -415,8 +420,8 @@ int ha_example::rnd_next(byte *buf)
the size needed to store current_position. ref is just a byte array
that the server will maintain. If you are using offsets to mark rows, then
current_position should be the offset. If it is a primary key like in
BDB, then it needs to be a primary key.
BDB, then it needs to be a primary key.
Called from filesort.cc, sql_select.cc, sql_delete.cc and sql_update.cc.
*/
void ha_example::position(const byte *record)
......@@ -436,7 +441,7 @@ void ha_example::position(const byte *record)
int ha_example::rnd_pos(byte * buf, byte *pos)
{
DBUG_ENTER("ha_example::rnd_pos");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
......@@ -449,9 +454,9 @@ int ha_example::rnd_pos(byte * buf, byte *pos)
if (records < 2)
records = 2;
The reason is that the server will optimize for cases of only a single
record. If in a table scan you don't know the number of records
record. If in a table scan you don't know the number of records
it will probably be better to set records to two so you can return
as many records as you need.
as many records as you need.
Along with records a few more variables you may wish to set are:
records
deleted
......@@ -518,9 +523,9 @@ int ha_example::reset(void)
/*
Used to delete all rows in a table. Both for cases of truncate and
for cases where the optimizer realizes that all rows will be
removed as a result of a SQL statement.
removed as a result of a SQL statement.
Called from item_sum.cc by Item_func_group_concat::clear(),
Called from item_sum.cc by Item_func_group_concat::clear(),
Item_sum_count_distinct::clear(), and Item_func_group_concat::clear().
Called from sql_delete.cc by mysql_delete().
Called from sql_select.cc by JOIN::reinit().
......@@ -529,12 +534,12 @@ int ha_example::reset(void)
int ha_example::delete_all_rows()
{
DBUG_ENTER("ha_example::delete_all_rows");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
/*
First you should go read the section "locking functions for mysql" in
/*
First you should go read the section "locking functions for mysql" in
lock.cc to understand this.
This create a lock on the table. If you are implementing a storage engine
that can handle transacations look at ha_berkely.cc to see how you will
......@@ -564,7 +569,7 @@ int ha_example::external_lock(THD *thd, int lock_type)
lock (if we don't want to use MySQL table locks at all) or add locks
for many tables (like we do when we are using a MERGE handler).
Berkeley DB for example changes all WRITE locks to TL_WRITE_ALLOW_WRITE
Berkeley DB for example changes all WRITE locks to TL_WRITE_ALLOW_WRITE
(which signals that we are doing WRITES, but we are still allowing other
reader's and writer's.
......@@ -591,9 +596,9 @@ THR_LOCK_DATA **ha_example::store_lock(THD *thd,
}
/*
Used to delete a table. By the time delete_table() has been called all
Used to delete a table. By the time delete_table() has been called all
opened references to this table will have been closed (and your globally
shared references released. The variable name will just be the name of
shared references released. The variable name will just be the name of
the table. You will need to remove any files you have created at this point.
If you do not implement this, the default delete_table() is called from
......@@ -623,10 +628,10 @@ int ha_example::delete_table(const char *name)
int ha_example::rename_table(const char * from, const char * to)
{
DBUG_ENTER("ha_example::rename_table ");
DBUG_RETURN(HA_ERR_NOT_IMPLEMENTED);
DBUG_RETURN(HA_ERR_WRONG_COMMAND);
}
/*
/*
Given a starting key, and an ending key estimate the number of rows that
will exist between the two. end_key may be empty which in case determine
if start_key matches any rows.
......@@ -644,14 +649,14 @@ ha_rows ha_example::records_in_range(uint inx, key_range *min_key,
/*
create() is called to create a database. The variable name will have the name
of the table. When create() is called you do not need to worry about opening
the table. Also, the FRM file will have already been created so adjusting
the table. Also, the FRM file will have already been created so adjusting
create_info will not do you any good. You can overwrite the frm file at this
point if you wish to change the table definition, but there are no methods
point if you wish to change the table definition, but there are no methods
currently provided for doing that.
Called from handle.cc by ha_create_table().
*/
int ha_example::create(const char *name, TABLE *table_arg,
int ha_example::create(const char *name, TABLE *table_arg,
HA_CREATE_INFO *create_info)
{
DBUG_ENTER("ha_example::create");
......
......@@ -14,7 +14,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
/*
Please read ha_exmple.cc before reading this file.
Please keep in mind that the example storage engine implements all methods
that are required to be implemented. handler.h has a full list of methods
......@@ -48,55 +48,68 @@ public:
ha_example(TABLE *table): handler(table)
{
}
~ha_example()
~ha_example()
{
}
/* The name that will be used for display purposes */
const char *table_type() const { return "EXAMPLE"; }
/* The name of the index type that will be used for display */
const char *index_type(uint inx) { return "NONE"; }
const char *table_type() const { return "EXAMPLE"; }
/*
The name of the index type that will be used for display
don't implement this method unless you really have indexes
*/
const char *index_type(uint inx) { return "HASH"; }
const char **bas_ext() const;
/*
This is a list of flags that says what the storage engine
/*
This is a list of flags that says what the storage engine
implements. The current table flags are documented in
table_flags.
handler.h
*/
ulong table_flags() const
{
return 0;
}
/*
This is a list of flags that says how the storage engine
/*
This is a list of flags that says how the storage engine
implements indexes. The current index flags are documented in
handler.h. If you do not implement indexes, just return zero
handler.h. If you do not implement indexes, just return zero
here.
*/
ulong index_flags(uint inx) const
ulong index_flags(uint inx, uint part) const
{
return 0;
}
/*
/*
unireg.cc will call the following to make sure that the storage engine can
handle the data it is about to send.
Return *real* limits of your storage engine here. MySQL will do
min(your_limits, MySQL_limits) automatically
There is no need to implement ..._key_... methods if you don't suport
indexes.
*/
uint max_record_length() const { return HA_MAX_REC_LENGTH; }
uint max_keys() const { return 0; }
uint max_key_parts() const { return 0; }
uint max_key_length() const { return 0; }
uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; }
uint max_supported_keys() const { return 0; }
uint max_supported_key_parts() const { return 0; }
uint max_supported_key_length() const { return 0; }
/*
Called in test_quick_select to determine if indexes should be used.
*/
virtual double scan_time() { return (double) (records+deleted) / 20.0+10; }
/*
/*
The next method will never be called if you do not implement indexes.
*/
virtual double read_time(ha_rows rows) { return (double) rows / 20.0+1; }
/*
/*
Everything below are methods that we implment in ha_example.cc.
Most of these methods are not obligatory, skip them and
MySQL will treat them as not implemented
*/
int open(const char *name, int mode, uint test_if_locked);
int close(void);
int open(const char *name, int mode, uint test_if_locked); // required
int close(void); // required
int write_row(byte * buf);
int update_row(const byte * old_data, byte * new_data);
int delete_row(const byte * buf);
......@@ -108,21 +121,32 @@ public:
int index_prev(byte * buf);
int index_first(byte * buf);
int index_last(byte * buf);
int rnd_init(bool scan=1);
int rnd_next(byte *buf);
int rnd_pos(byte * buf, byte *pos);
void position(const byte *record);
void info(uint);
/*
unlike index_init(), rnd_init() can be called two times
without rnd_end() in between (it only makes sense if scan=1).
then the second call should prepare for the new table scan
(e.g if rnd_init allocates the cursor, second call should
position it to the start of the table, no need to deallocate
and allocate it again
*/
int rnd_init(bool scan); //required
int rnd_end();
int rnd_next(byte *buf); //required
int rnd_pos(byte * buf, byte *pos); //required
void position(const byte *record); //required
void info(uint); //required
int extra(enum ha_extra_function operation);
int reset(void);
int external_lock(THD *thd, int lock_type);
int external_lock(THD *thd, int lock_type); //required
int delete_all_rows(void);
ha_rows records_in_range(uint inx, key_range *min_key,
key_range *max_key);
int delete_table(const char *from);
int rename_table(const char * from, const char * to);
int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info);
int create(const char *name, TABLE *form,
HA_CREATE_INFO *create_info); //required
THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
enum thr_lock_type lock_type);
enum thr_lock_type lock_type); //required
};
......@@ -1583,7 +1583,7 @@ int ha_berkeley::index_last(byte * buf)
int ha_berkeley::rnd_init(bool scan)
{
DBUG_ENTER("rnd_init");
DBUG_ASSERT(active_index==MAX_KEY);
//DBUG_ASSERT(active_index==MAX_KEY);
current_row.flags=DB_DBT_REALLOC;
DBUG_RETURN(index_init(primary_key));
}
......
......@@ -88,7 +88,7 @@ class ha_berkeley: public handler
public:
ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), file(0),
int_table_flags(HA_REC_NOT_IN_SEQ | HA_FAST_KEY_READ |
HA_NULL_IN_KEY | HA_BLOB_KEY | HA_NOT_EXACT_COUNT |
HA_NULL_IN_KEY | HA_CAN_INDEX_BLOBS | HA_NOT_EXACT_COUNT |
HA_PRIMARY_KEY_IN_READ_INDEX | HA_FILE_BASED |
HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX),
changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) {}
......
......@@ -3872,12 +3872,8 @@ JOIN::join_free(bool full)
{
for (tab= join_tab, end= tab+tables; tab != end; tab++)
{
if (tab->table)
{
/* Don't free index if we are using read_record */
if (tab->table->file->inited==handler::RND)
if (tab->table && tab->table->file->inited == handler::RND)
tab->table->file->ha_rnd_end();
}
}
}
}
......
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