mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
2aecdd1a91
(All commit emails since 4.0.1 checked) This had to be done now, before the 4.1 tree changes to much, to make it easy to propagate bug fixes to the 4.1 tree.
278 lines
7.6 KiB
C++
278 lines
7.6 KiB
C++
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult 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 */
|
|
|
|
|
|
/* HANDLER ... commands - direct access to ISAM */
|
|
|
|
#include <assert.h>
|
|
#include "mysql_priv.h"
|
|
#include "sql_select.h"
|
|
|
|
/* TODO:
|
|
HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
|
|
|
|
the most natural (easiest, fastest) way to do it is to
|
|
compute List<Item> field_list not in mysql_ha_read
|
|
but in mysql_ha_open, and then store it in TABLE structure.
|
|
|
|
The problem here is that mysql_parse calls free_item to free all the
|
|
items allocated at the end of every query. The workaround would to
|
|
keep two item lists per THD - normal free_list and handler_items.
|
|
The second is to be freeed only on thread end. mysql_ha_open should
|
|
then do { handler_items=concat(handler_items, free_list); free_list=0; }
|
|
|
|
But !!! do_cammand calls free_root at the end of every query and frees up
|
|
all the sql_alloc'ed memory. It's harder to work around...
|
|
*/
|
|
|
|
#define HANDLER_TABLES_HACK(thd) { \
|
|
TABLE *tmp=thd->open_tables; \
|
|
thd->open_tables=thd->handler_tables; \
|
|
thd->handler_tables=tmp; }
|
|
|
|
static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
|
|
const char *table_name);
|
|
|
|
int mysql_ha_open(THD *thd, TABLE_LIST *tables)
|
|
{
|
|
HANDLER_TABLES_HACK(thd);
|
|
int err=open_tables(thd,tables);
|
|
HANDLER_TABLES_HACK(thd);
|
|
if (err)
|
|
return -1;
|
|
|
|
// there can be only one table in *tables
|
|
if (!(tables->table->file->table_flags() & HA_CAN_SQL_HANDLER))
|
|
{
|
|
my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->name);
|
|
mysql_ha_close(thd, tables,1);
|
|
return -1;
|
|
}
|
|
|
|
send_ok(&thd->net);
|
|
return 0;
|
|
}
|
|
|
|
int mysql_ha_close(THD *thd, TABLE_LIST *tables, bool dont_send_ok)
|
|
{
|
|
TABLE **ptr=find_table_ptr_by_name(thd, tables->db, tables->name);
|
|
|
|
if (*ptr)
|
|
{
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
close_thread_table(thd, ptr);
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
}
|
|
else
|
|
{
|
|
my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0),
|
|
tables->name,"HANDLER");
|
|
return -1;
|
|
}
|
|
if (!dont_send_ok)
|
|
send_ok(&thd->net);
|
|
return 0;
|
|
}
|
|
|
|
static enum enum_ha_read_modes rkey_to_rnext[]=
|
|
{ RNEXT, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV };
|
|
|
|
|
|
int mysql_ha_read(THD *thd, TABLE_LIST *tables,
|
|
enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr,
|
|
enum ha_rkey_function ha_rkey_mode, Item *cond,
|
|
ha_rows select_limit,ha_rows offset_limit)
|
|
{
|
|
int err, keyno=-1;
|
|
TABLE *table=*find_table_ptr_by_name(thd, tables->db, tables->name);
|
|
if (!table)
|
|
{
|
|
my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0),
|
|
tables->name,"HANDLER");
|
|
return -1;
|
|
}
|
|
tables->table=table;
|
|
|
|
if (cond && cond->fix_fields(thd,tables))
|
|
return -1;
|
|
|
|
if (keyname)
|
|
{
|
|
if ((keyno=find_type(keyname, &table->keynames, 1+2)-1)<0)
|
|
{
|
|
my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0),
|
|
keyname,tables->name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
List<Item> list;
|
|
list.push_front(new Item_field(NULL,NULL,"*"));
|
|
List_iterator<Item> it(list);
|
|
uint num_rows;
|
|
it++;
|
|
|
|
insert_fields(thd,tables,tables->db,tables->name,&it);
|
|
|
|
table->file->index_init(keyno);
|
|
|
|
select_limit+=offset_limit;
|
|
send_fields(thd,list,1);
|
|
|
|
HANDLER_TABLES_HACK(thd);
|
|
MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1);
|
|
HANDLER_TABLES_HACK(thd);
|
|
if (!lock)
|
|
goto err0; // mysql_lock_tables() printed error message already
|
|
|
|
for (num_rows=0; num_rows < select_limit; )
|
|
{
|
|
switch(mode) {
|
|
case RFIRST:
|
|
err=keyname ?
|
|
table->file->index_first(table->record[0]) :
|
|
table->file->rnd_init(1) ||
|
|
table->file->rnd_next(table->record[0]);
|
|
mode=RNEXT;
|
|
break;
|
|
case RLAST:
|
|
DBUG_ASSERT(keyname != 0);
|
|
err=table->file->index_last(table->record[0]);
|
|
mode=RPREV;
|
|
break;
|
|
case RNEXT:
|
|
err=keyname ?
|
|
table->file->index_next(table->record[0]) :
|
|
table->file->rnd_next(table->record[0]);
|
|
break;
|
|
case RPREV:
|
|
DBUG_ASSERT(keyname != 0);
|
|
err=table->file->index_prev(table->record[0]);
|
|
break;
|
|
case RKEY:
|
|
{
|
|
DBUG_ASSERT(keyname != 0);
|
|
KEY *keyinfo=table->key_info+keyno;
|
|
KEY_PART_INFO *key_part=keyinfo->key_part;
|
|
uint key_len;
|
|
byte *key;
|
|
if (key_expr->elements > keyinfo->key_parts)
|
|
{
|
|
my_printf_error(ER_TOO_MANY_KEY_PARTS,ER(ER_TOO_MANY_KEY_PARTS),
|
|
MYF(0),keyinfo->key_parts);
|
|
goto err;
|
|
}
|
|
List_iterator_fast<Item> it_ke(*key_expr);
|
|
Item *item;
|
|
for (key_len=0 ; (item=it_ke++) ; key_part++)
|
|
{
|
|
item->save_in_field(key_part->field);
|
|
key_len+=key_part->store_length;
|
|
}
|
|
if (!(key= (byte*) sql_calloc(ALIGN_SIZE(key_len))))
|
|
{
|
|
send_error(&thd->net,ER_OUTOFMEMORY);
|
|
goto err;
|
|
}
|
|
key_copy(key, table, keyno, key_len);
|
|
err=table->file->index_read(table->record[0],
|
|
key,key_len,ha_rkey_mode);
|
|
mode=rkey_to_rnext[(int)ha_rkey_mode];
|
|
break;
|
|
}
|
|
default:
|
|
send_error(&thd->net,ER_ILLEGAL_HA);
|
|
goto err;
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE)
|
|
{
|
|
sql_print_error("mysql_ha_read: Got error %d when reading table",
|
|
err);
|
|
table->file->print_error(err,MYF(0));
|
|
goto err;
|
|
}
|
|
goto ok;
|
|
}
|
|
if (cond)
|
|
{
|
|
err=err;
|
|
if (!cond->val_int())
|
|
continue;
|
|
}
|
|
if (num_rows>=offset_limit)
|
|
{
|
|
if (!err)
|
|
{
|
|
String *packet = &thd->packet;
|
|
Item *item;
|
|
packet->length(0);
|
|
it.rewind();
|
|
while ((item=it++))
|
|
{
|
|
if (item->send(thd,packet))
|
|
{
|
|
packet->free(); // Free used
|
|
my_error(ER_OUT_OF_RESOURCES,MYF(0));
|
|
goto err;
|
|
}
|
|
}
|
|
my_net_write(&thd->net, (char*)packet->ptr(), packet->length());
|
|
}
|
|
}
|
|
num_rows++;
|
|
}
|
|
ok:
|
|
mysql_unlock_tables(thd,lock);
|
|
send_eof(&thd->net);
|
|
return 0;
|
|
err:
|
|
mysql_unlock_tables(thd,lock);
|
|
err0:
|
|
return -1;
|
|
}
|
|
|
|
/**************************************************************************
|
|
2Monty: It could easily happen, that the following service functions are
|
|
already defined somewhere in the code, but I failed to find them.
|
|
If this is the case, just say a word and I'll use old functions here.
|
|
**************************************************************************/
|
|
|
|
/* Note: this function differs from find_locked_table() because we're looking
|
|
here for alias, not real table name
|
|
*/
|
|
static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
|
|
const char *table_name)
|
|
{
|
|
int dblen;
|
|
TABLE **ptr;
|
|
|
|
if (!db || ! *db)
|
|
db= thd->db ? thd->db : "";
|
|
dblen=strlen(db)+1;
|
|
ptr=&(thd->handler_tables);
|
|
|
|
for (TABLE *table=*ptr; table ; table=*ptr)
|
|
{
|
|
if (!memcmp(table->table_cache_key, db, dblen) &&
|
|
!my_strcasecmp(table->table_name,table_name))
|
|
break;
|
|
ptr=&(table->next);
|
|
}
|
|
return ptr;
|
|
}
|