2003-02-04 21:52:14 +02:00
|
|
|
/* Copyright (C) 2000-2003 MySQL AB
|
2001-04-07 00:18:33 +02:00
|
|
|
|
|
|
|
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 "mysql_priv.h"
|
|
|
|
#include "sql_select.h"
|
|
|
|
|
2001-04-13 16:18:44 +02:00
|
|
|
/* TODO:
|
|
|
|
HANDLER blabla OPEN [ AS foobar ] [ (column-list) ]
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2001-04-13 16:18:44 +02:00
|
|
|
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.
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2001-04-13 16:18:44 +02:00
|
|
|
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; }
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2003-09-09 19:23:01 +02:00
|
|
|
But !!! do_command calls free_root at the end of every query and frees up
|
2001-04-13 16:18:44 +02:00
|
|
|
all the sql_alloc'ed memory. It's harder to work around...
|
2004-03-10 13:46:11 +02:00
|
|
|
*/
|
2001-04-13 16:18:44 +02:00
|
|
|
|
|
|
|
#define HANDLER_TABLES_HACK(thd) { \
|
|
|
|
TABLE *tmp=thd->open_tables; \
|
|
|
|
thd->open_tables=thd->handler_tables; \
|
|
|
|
thd->handler_tables=tmp; }
|
|
|
|
|
2003-01-28 14:36:22 +01:00
|
|
|
static TABLE **find_table_ptr_by_name(THD *thd,const char *db,
|
2004-06-24 15:06:56 +02:00
|
|
|
const char *table_name,
|
|
|
|
bool is_alias, bool dont_lock,
|
|
|
|
bool *was_flushed);
|
2001-04-07 00:18:33 +02:00
|
|
|
|
|
|
|
int mysql_ha_open(THD *thd, TABLE_LIST *tables)
|
|
|
|
{
|
2001-04-13 16:18:44 +02:00
|
|
|
HANDLER_TABLES_HACK(thd);
|
2004-02-09 14:44:03 +02:00
|
|
|
uint counter;
|
2004-09-07 10:42:23 +03:00
|
|
|
|
|
|
|
/* for now HANDLER can be used only for real TABLES */
|
|
|
|
tables->required_type= FRMTYPE_TABLE;
|
2004-02-09 14:44:03 +02:00
|
|
|
int err=open_tables(thd, tables, &counter);
|
2004-09-07 10:42:23 +03:00
|
|
|
|
2001-04-13 16:18:44 +02:00
|
|
|
HANDLER_TABLES_HACK(thd);
|
|
|
|
if (err)
|
|
|
|
return -1;
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2001-12-21 13:28:51 +00:00
|
|
|
// there can be only one table in *tables
|
2002-04-12 21:35:46 +03:00
|
|
|
if (!(tables->table->file->table_flags() & HA_CAN_SQL_HANDLER))
|
2001-12-21 13:28:51 +00:00
|
|
|
{
|
2002-09-20 14:05:18 +03:00
|
|
|
my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->alias);
|
2001-12-22 15:44:44 +02:00
|
|
|
mysql_ha_close(thd, tables,1);
|
2001-12-21 13:28:51 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2002-10-02 13:33:08 +03:00
|
|
|
send_ok(thd);
|
2001-04-13 16:18:44 +02:00
|
|
|
return 0;
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2004-06-24 15:06:56 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Close a HANDLER table.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_ha_close()
|
|
|
|
thd Thread identifier.
|
|
|
|
tables A list of tables with the first entry to close.
|
|
|
|
dont_send_ok Suppresses the commands' ok message and
|
|
|
|
error message and error return.
|
|
|
|
dont_lock Suppresses the normal locking of LOCK_open.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Though this function takes a list of tables, only the first list entry
|
|
|
|
will be closed. Broadcasts a COND_refresh condition.
|
|
|
|
If mysql_ha_close() is not called from the parser, 'dont_send_ok'
|
|
|
|
must be set.
|
|
|
|
If the caller did already lock LOCK_open, it must set 'dont_lock'.
|
|
|
|
|
|
|
|
IMPLEMENTATION
|
|
|
|
find_table_ptr_by_name() closes the table, if a FLUSH TABLE is outstanding.
|
|
|
|
It returns a NULL pointer in this case, but flags the situation in
|
|
|
|
'was_flushed'. In that case the normal ER_UNKNOWN_TABLE error messages
|
|
|
|
is suppressed.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
-1 error
|
|
|
|
*/
|
|
|
|
|
|
|
|
int mysql_ha_close(THD *thd, TABLE_LIST *tables,
|
|
|
|
bool dont_send_ok, bool dont_lock, bool no_alias)
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
TABLE **table_ptr;
|
|
|
|
bool was_flushed;
|
2001-04-15 22:56:20 +02:00
|
|
|
|
2004-06-24 15:06:56 +02:00
|
|
|
table_ptr= find_table_ptr_by_name(thd, tables->db, tables->alias,
|
|
|
|
!no_alias, dont_lock, &was_flushed);
|
|
|
|
if (*table_ptr)
|
2001-11-29 21:46:51 +02:00
|
|
|
{
|
2004-06-25 21:56:23 +03:00
|
|
|
(*table_ptr)->file->ha_index_or_rnd_end();
|
2004-06-24 15:06:56 +02:00
|
|
|
if (!dont_lock)
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
|
|
|
if (close_thread_table(thd, table_ptr))
|
2003-09-09 19:23:01 +02:00
|
|
|
{
|
|
|
|
/* Tell threads waiting for refresh that something has happened */
|
|
|
|
VOID(pthread_cond_broadcast(&COND_refresh));
|
|
|
|
}
|
2004-06-24 15:06:56 +02:00
|
|
|
if (!dont_lock)
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
2001-11-29 21:46:51 +02:00
|
|
|
}
|
2004-06-24 15:06:56 +02:00
|
|
|
else if (!was_flushed && !dont_send_ok)
|
2001-12-22 18:40:26 +00:00
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
my_printf_error(ER_UNKNOWN_TABLE, ER(ER_UNKNOWN_TABLE), MYF(0),
|
|
|
|
tables->alias, "HANDLER");
|
2001-12-22 18:40:26 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2001-12-22 15:44:44 +02:00
|
|
|
if (!dont_send_ok)
|
2002-10-02 13:33:08 +03:00
|
|
|
send_ok(thd);
|
2001-04-07 00:18:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-06-24 15:06:56 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Close a list of HANDLER tables.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
mysql_ha_close_list()
|
|
|
|
thd Thread identifier.
|
|
|
|
tables The list of tables to close. If NULL,
|
|
|
|
close all HANDLER tables.
|
|
|
|
flushed Close only tables which are marked flushed.
|
|
|
|
Used only if tables is NULL.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
The list of HANDLER tables may be NULL, in which case all HANDLER
|
|
|
|
tables are closed. Broadcasts a COND_refresh condition, for
|
|
|
|
every table closed. If 'tables' is NULL and 'flushed' is set,
|
|
|
|
all HANDLER tables marked for flush are closed.
|
|
|
|
The caller must lock LOCK_open.
|
|
|
|
|
|
|
|
IMPLEMENTATION
|
|
|
|
find_table_ptr_by_name() closes the table, if it is marked for flush.
|
|
|
|
It returns a NULL pointer in this case, but flags the situation in
|
|
|
|
'was_flushed'. In that case the normal ER_UNKNOWN_TABLE error messages
|
|
|
|
is suppressed.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
0 ok
|
|
|
|
*/
|
|
|
|
|
|
|
|
int mysql_ha_close_list(THD *thd, TABLE_LIST *tables, bool flushed)
|
2003-01-28 14:36:22 +01:00
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
TABLE_LIST *tl_item;
|
|
|
|
TABLE **table_ptr;
|
|
|
|
|
|
|
|
if (tables)
|
2003-09-09 19:23:01 +02:00
|
|
|
{
|
2004-07-16 01:15:55 +03:00
|
|
|
for (tl_item= tables ; tl_item; tl_item= tl_item->next_local)
|
2004-06-23 12:29:05 +02:00
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
mysql_ha_close(thd, tl_item, /*dont_send_ok*/ 1,
|
|
|
|
/*dont_lock*/ 1, /*no_alias*/ 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2003-09-09 19:23:01 +02:00
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
table_ptr= &(thd->handler_tables);
|
|
|
|
while (*table_ptr)
|
|
|
|
{
|
|
|
|
if (! flushed || ((*table_ptr)->version != refresh_version))
|
|
|
|
{
|
2004-06-25 20:13:05 +03:00
|
|
|
(*table_ptr)->file->ha_index_or_rnd_end();
|
2004-06-24 15:06:56 +02:00
|
|
|
if (close_thread_table(thd, table_ptr))
|
|
|
|
{
|
|
|
|
/* Tell threads waiting for refresh that something has happened */
|
|
|
|
VOID(pthread_cond_broadcast(&COND_refresh));
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
table_ptr= &((*table_ptr)->next);
|
2004-06-23 12:29:05 +02:00
|
|
|
}
|
2003-09-09 19:23:01 +02:00
|
|
|
}
|
2003-01-28 14:36:22 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-12-21 13:28:51 +00:00
|
|
|
static enum enum_ha_read_modes rkey_to_rnext[]=
|
2004-07-15 04:19:07 +03:00
|
|
|
{ RNEXT_SAME, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV, RPREV };
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2002-01-29 18:32:16 +02:00
|
|
|
|
2001-04-07 00:18:33 +02:00
|
|
|
int mysql_ha_read(THD *thd, TABLE_LIST *tables,
|
2004-07-15 04:19:07 +03:00
|
|
|
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)
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
2001-04-13 16:18:44 +02:00
|
|
|
int err, keyno=-1;
|
2004-06-24 15:06:56 +02:00
|
|
|
bool was_flushed;
|
|
|
|
TABLE *table= *find_table_ptr_by_name(thd, tables->db, tables->alias,
|
|
|
|
/*is_alias*/ 1, /*dont_lock*/ 0,
|
|
|
|
&was_flushed);
|
2001-04-07 00:18:33 +02:00
|
|
|
if (!table)
|
|
|
|
{
|
|
|
|
my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0),
|
2002-09-20 14:05:18 +03:00
|
|
|
tables->alias,"HANDLER");
|
2001-04-07 00:18:33 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
tables->table=table;
|
2001-04-13 16:18:44 +02:00
|
|
|
|
2003-01-21 13:55:26 +02:00
|
|
|
if (cond && (cond->fix_fields(thd, tables, &cond) || cond->check_cols(1)))
|
2001-04-13 16:18:44 +02:00
|
|
|
return -1;
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2003-01-13 18:20:59 +02:00
|
|
|
/* InnoDB needs to know that this table handle is used in the HANDLER */
|
|
|
|
|
|
|
|
table->file->init_table_handle_for_HANDLER();
|
|
|
|
|
2001-04-13 16:18:44 +02:00
|
|
|
if (keyname)
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
2001-04-13 16:18:44 +02:00
|
|
|
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),
|
2004-07-15 04:19:07 +03:00
|
|
|
keyname,tables->alias);
|
2001-04-13 16:18:44 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-06-23 12:29:05 +02:00
|
|
|
table->file->ha_index_or_rnd_end();
|
|
|
|
table->file->ha_index_init(keyno);
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
List<Item> list;
|
|
|
|
list.push_front(new Item_field(NULL,NULL,"*"));
|
|
|
|
List_iterator<Item> it(list);
|
2002-12-11 09:17:51 +02:00
|
|
|
Protocol *protocol= thd->protocol;
|
|
|
|
char buff[MAX_FIELD_WIDTH];
|
|
|
|
String buffer(buff, sizeof(buff), system_charset_info);
|
2002-01-29 18:32:16 +02:00
|
|
|
uint num_rows;
|
2004-05-20 00:54:52 +03:00
|
|
|
byte *key;
|
|
|
|
uint key_len;
|
2004-06-23 12:29:05 +02:00
|
|
|
LINT_INIT(key);
|
|
|
|
LINT_INIT(key_len);
|
2004-05-20 00:54:52 +03:00
|
|
|
|
|
|
|
it++; // Skip first NULL field
|
2001-04-07 00:18:33 +02:00
|
|
|
|
2004-09-10 02:22:44 +03:00
|
|
|
insert_fields(thd, tables, tables->db, tables->alias, &it, 0, 0);
|
2001-04-13 16:18:44 +02:00
|
|
|
|
|
|
|
select_limit+=offset_limit;
|
Port of cursors to be pushed into 5.0 tree:
- client side part is simple and may be considered stable
- server side part now just joggles with THD state to save execution
state and has no additional locking wisdom.
Lot's of it are to be rewritten.
include/mysql.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new statement attribute STMT_ATTR_CURSOR_TYPE
- MYSQL_STMT::flags to store statement cursor type
- MYSQL_STMT::server_status to store server status (i. e. if the server
was able to open a cursor for this query).
include/mysql_com.h:
Cursor patch to push into the main tree, client library part (considered
stable):
- new COMmand, COM_FETCH, to fetch K rows from read-only cursor.
By design should support scrollable cursors as well.
- a few new server statuses:
SERVER_STATUS_CURSOR_EXISTS is sent by server in reply to COM_EXECUTE,
when cursor was successfully opened for this query
SERVER_STATUS_LAST_ROW_SENT is sent along with the last row to prevent one
more round trip just for finding out that all rows were fetched from
this cursor (this is server mem savier also).
- and finally, all possible values of STMT_ATTR_CURSOR_TYPE,
while now we support only CURSORT_TYPE_NO_CURSOR and
CURSOR_TYPE_READ_ONLY
libmysql/libmysql.c:
Cursor patch to push into the main tree, client library part (considered
stable):
- simple additions to mysql_stmt_fetch implementation to read data
from an opened cursor: we can read up to iteration count rows per
one request; read rows are buffered in the same way as rows of
mysql_stmt_store_result.
- now send stmt->flags to server to let him now if we wish to have
a cursor for this statement.
- support for setting/getting statement cursor type.
libmysqld/examples/Makefile.am:
Testing cursors was originally implemented in C++. Now when these tests
go into client_test, it's time to convert it to C++ as well.
libmysqld/lib_sql.cc:
- cleanup: send_fields flags are now named.
sql/ha_innodb.cc:
- cleanup: send_fields flags are now named.
sql/mysql_priv.h:
- cursors support: declaration for server-side handler of COM_FETCH
sql/protocol.cc:
- cleanup: send_fields flags are now named.
- we can't anymore assert that field_types[field_pos] is sensible:
if we have COM_EXCUTE(stmt1), COM_EXECUTE(stmt2), COM_FETCH(stmt1)
field_types[field_pos] will point to fields of stmt2.
sql/protocol.h:
- cleanup: send_fields flag_s_ are now named.
sql/protocol_cursor.cc:
- cleanup: send_fields flags are now named.
sql/repl_failsafe.cc:
- cleanup: send_fields flags are now named.
sql/slave.cc:
- cleanup: send_fields flags are now named.
sql/sp.cc:
- cleanup: send_fields flags are now named.
sql/sp_head.cc:
- cleanup: send_fields flags are now named.
sql/sql_acl.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.cc:
- cleanup: send_fields flags are now named.
sql/sql_class.h:
- cleanup: send_fields flags are now named.
sql/sql_error.cc:
- cleanup: send_fields flags are now named.
sql/sql_handler.cc:
- cleanup: send_fields flags are now named.
sql/sql_help.cc:
- cleanup: send_fields flags are now named.
sql/sql_parse.cc:
Server side support for cursors:
- handle COM_FETCH
- enforce assumption that whenever we free thd->free_list,
we reset it to zero. This way it's much easier to handle free_list
in prepared statements implementation.
sql/sql_prepare.cc:
Server side support for cursors:
- implementation of mysql_stmt_fetch (fetch some rows from open cursor).
- management of cursors memory is quite tricky now.
- execute_stmt can't be reused anymore in mysql_stmt_execute and
mysql_sql_stmt_execute
sql/sql_repl.cc:
- cleanup: send_fields flags are now named.
sql/sql_select.cc:
Server side support for cursors:
- implementation of Cursor::open, Cursor::fetch (buggy when it comes to
non-equi joins), cursor cleanups.
- -4 -3 -0 constants indicating return value of sub_select and end_send are
to be renamed to something more readable:
it turned out to be not so simple, so it should come with the other patch.
sql/sql_select.h:
Server side support for cursors:
- declaration of Cursor class.
- JOIN::fetch_limit contains runtime value of rows fetched via cursor.
sql/sql_show.cc:
- cleanup: send_fields flags are now named.
sql/sql_table.cc:
- cleanup: send_fields flags are now named.
sql/sql_union.cc:
- if there was a cursor, don't cleanup unit: we'll need it to fetch
the rest of the rows.
tests/Makefile.am:
Now client_test is in C++.
tests/client_test.cc:
A few elementary tests for cursors.
BitKeeper/etc/ignore:
Added libmysqld/examples/client_test.cc to the ignore list
2004-08-03 03:32:21 -07:00
|
|
|
protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2002-01-03 14:31:54 +00:00
|
|
|
HANDLER_TABLES_HACK(thd);
|
2001-04-13 16:18:44 +02:00
|
|
|
MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1);
|
2002-01-03 14:31:54 +00:00
|
|
|
HANDLER_TABLES_HACK(thd);
|
|
|
|
if (!lock)
|
2004-07-15 04:19:07 +03:00
|
|
|
goto err0; // mysql_lock_tables() printed error message already
|
2001-04-09 15:37:19 +02:00
|
|
|
|
2003-02-07 15:47:24 +02:00
|
|
|
/*
|
|
|
|
In ::external_lock InnoDB resets the fields which tell it that
|
|
|
|
the handle is used in the HANDLER interface. Tell it again that
|
|
|
|
we are using it for HANDLER.
|
|
|
|
*/
|
2003-01-13 18:20:59 +02:00
|
|
|
|
|
|
|
table->file->init_table_handle_for_HANDLER();
|
|
|
|
|
2002-01-29 18:32:16 +02:00
|
|
|
for (num_rows=0; num_rows < select_limit; )
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
2002-12-11 09:17:51 +02:00
|
|
|
switch (mode) {
|
2002-01-29 18:32:16 +02:00
|
|
|
case RFIRST:
|
2003-02-22 18:00:34 +01:00
|
|
|
if (keyname)
|
|
|
|
err=table->file->index_first(table->record[0]);
|
|
|
|
else
|
|
|
|
{
|
2004-06-23 12:29:05 +02:00
|
|
|
table->file->ha_index_or_rnd_end();
|
|
|
|
if (!(err=table->file->ha_rnd_init(1)))
|
2003-02-22 18:00:34 +01:00
|
|
|
err=table->file->rnd_next(table->record[0]);
|
|
|
|
}
|
2002-01-29 18:32:16 +02:00
|
|
|
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]);
|
2004-07-15 04:19:07 +03:00
|
|
|
break;
|
2002-01-29 18:32:16 +02:00
|
|
|
case RPREV:
|
|
|
|
DBUG_ASSERT(keyname != 0);
|
|
|
|
err=table->file->index_prev(table->record[0]);
|
|
|
|
break;
|
2004-05-19 02:18:54 +04:00
|
|
|
case RNEXT_SAME:
|
|
|
|
/* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */
|
|
|
|
DBUG_ASSERT(keyname != 0);
|
|
|
|
err= table->file->index_next_same(table->record[0], key, key_len);
|
|
|
|
break;
|
2002-01-29 18:32:16 +02:00
|
|
|
case RKEY:
|
2001-04-09 15:37:19 +02:00
|
|
|
{
|
2002-01-29 18:32:16 +02:00
|
|
|
DBUG_ASSERT(keyname != 0);
|
|
|
|
KEY *keyinfo=table->key_info+keyno;
|
|
|
|
KEY_PART_INFO *key_part=keyinfo->key_part;
|
|
|
|
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;
|
|
|
|
}
|
2004-02-18 01:08:52 +02:00
|
|
|
List_iterator<Item> it_ke(*key_expr);
|
2002-01-29 18:32:16 +02:00
|
|
|
Item *item;
|
|
|
|
for (key_len=0 ; (item=it_ke++) ; key_part++)
|
|
|
|
{
|
2004-02-18 01:08:52 +02:00
|
|
|
// 'item' can be changed by fix_fields() call
|
|
|
|
if (item->fix_fields(thd, tables, it_ke.ref()) ||
|
|
|
|
(item= *it_ke.ref())->check_cols(1))
|
2003-07-04 12:55:25 +02:00
|
|
|
goto err;
|
|
|
|
if (item->used_tables() & ~RAND_TABLE_BIT)
|
|
|
|
{
|
|
|
|
my_error(ER_WRONG_ARGUMENTS,MYF(0),"HANDLER ... READ");
|
|
|
|
goto err;
|
|
|
|
}
|
2002-12-05 19:38:42 +02:00
|
|
|
(void) item->save_in_field(key_part->field, 1);
|
2002-01-29 18:32:16 +02:00
|
|
|
key_len+=key_part->store_length;
|
|
|
|
}
|
2002-12-03 13:08:25 +02:00
|
|
|
if (!(key= (byte*) thd->calloc(ALIGN_SIZE(key_len))))
|
2002-01-29 18:32:16 +02:00
|
|
|
{
|
2002-10-02 13:33:08 +03:00
|
|
|
send_error(thd,ER_OUTOFMEMORY);
|
2002-01-29 18:32:16 +02:00
|
|
|
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:
|
2002-10-02 13:33:08 +03:00
|
|
|
send_error(thd,ER_ILLEGAL_HA);
|
2002-01-29 18:32:16 +02:00
|
|
|
goto err;
|
2001-04-09 15:37:19 +02:00
|
|
|
}
|
|
|
|
|
2003-07-04 14:41:01 +05:00
|
|
|
if (err == HA_ERR_RECORD_DELETED)
|
|
|
|
continue;
|
2001-04-13 16:18:44 +02:00
|
|
|
if (err)
|
2001-04-09 15:37:19 +02:00
|
|
|
{
|
2001-04-13 16:18:44 +02:00
|
|
|
if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE)
|
|
|
|
{
|
2003-11-12 22:33:28 +01:00
|
|
|
sql_print_error("mysql_ha_read: Got error %d when reading table '%s'",
|
|
|
|
err, tables->real_name);
|
2001-04-13 16:18:44 +02:00
|
|
|
table->file->print_error(err,MYF(0));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
goto ok;
|
|
|
|
}
|
2003-07-04 14:41:01 +05:00
|
|
|
if (cond && !cond->val_int())
|
|
|
|
continue;
|
2003-07-04 23:06:19 +03:00
|
|
|
if (num_rows >= offset_limit)
|
2001-12-21 13:28:51 +00:00
|
|
|
{
|
2003-07-04 14:41:01 +05:00
|
|
|
Item *item;
|
|
|
|
protocol->prepare_for_resend();
|
|
|
|
it.rewind();
|
|
|
|
while ((item=it++))
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
2003-07-04 14:41:01 +05:00
|
|
|
if (item->send(thd->protocol, &buffer))
|
|
|
|
{
|
|
|
|
protocol->free(); // Free used
|
|
|
|
my_error(ER_OUT_OF_RESOURCES,MYF(0));
|
|
|
|
goto err;
|
|
|
|
}
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
2003-07-04 14:41:01 +05:00
|
|
|
protocol->write();
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
2001-04-13 16:18:44 +02:00
|
|
|
num_rows++;
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
2001-04-13 16:18:44 +02:00
|
|
|
ok:
|
|
|
|
mysql_unlock_tables(thd,lock);
|
2002-10-02 13:33:08 +03:00
|
|
|
send_eof(thd);
|
2001-04-07 00:18:33 +02:00
|
|
|
return 0;
|
2001-04-13 16:18:44 +02:00
|
|
|
err:
|
|
|
|
mysql_unlock_tables(thd,lock);
|
2002-01-03 14:31:54 +00:00
|
|
|
err0:
|
2001-04-13 16:18:44 +02:00
|
|
|
return -1;
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2002-12-11 09:17:51 +02:00
|
|
|
|
2004-06-24 15:06:56 +02:00
|
|
|
/*
|
|
|
|
Find a HANDLER table by name.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
find_table_ptr_by_name()
|
|
|
|
thd Thread identifier.
|
|
|
|
db Database (schema) name.
|
|
|
|
table_name Table name ;-).
|
|
|
|
is_alias Table name may be an alias name.
|
|
|
|
dont_lock Suppresses the normal locking of LOCK_open.
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
Find the table 'db'.'table_name' in the list of HANDLER tables of the
|
|
|
|
thread 'thd'. If the table has been marked by FLUSH TABLE(S), close it,
|
|
|
|
flag this situation in '*was_flushed' and broadcast a COND_refresh
|
|
|
|
condition.
|
|
|
|
An empty database (schema) name matches all database (schema) names.
|
|
|
|
If the caller did already lock LOCK_open, it must set 'dont_lock'.
|
|
|
|
|
|
|
|
IMPLEMENTATION
|
|
|
|
Just in case that the table is twice in 'thd->handler_tables' (!?!),
|
|
|
|
the loop does not break when the table was flushed. If another table
|
|
|
|
by that name was found and not flushed, '*was_flushed' is cleared again,
|
|
|
|
since a pointer to an open HANDLER table is returned.
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
*was_flushed Table has been closed due to FLUSH TABLE.
|
|
|
|
NULL A HANDLER Table by that name does not exist (any more).
|
|
|
|
!= NULL Pointer to the TABLE structure.
|
|
|
|
*/
|
|
|
|
|
2001-05-23 23:47:08 +03:00
|
|
|
static TABLE **find_table_ptr_by_name(THD *thd, const char *db,
|
2004-06-24 15:06:56 +02:00
|
|
|
const char *table_name,
|
|
|
|
bool is_alias, bool dont_lock,
|
|
|
|
bool *was_flushed)
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
|
|
|
int dblen;
|
2004-06-24 15:06:56 +02:00
|
|
|
TABLE **table_ptr;
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2004-01-13 12:31:25 +01:00
|
|
|
DBUG_ASSERT(db);
|
After merge fixes
Added more DBUG statements
Ensure that we are comparing end space with BINARY strings
Use 'any_db' instead of '' to mean any database. (For HANDLER command)
Only strip ' ' when comparing CHAR, not other space-like characters (like \t)
BitKeeper/deleted/.del-ctype_tis620.result-old~3578ceb0b8284685:
Delete: mysql-test/r/ctype_tis620.result-old
BitKeeper/deleted/.del-ctype_tis620.test-old~ffb1bbd2935d1aba:
Delete: mysql-test/t/ctype_tis620.test-old
client/mysqlbinlog.cc:
Added DBUG statements
Added call of my_end() to free all used memory on exit
heap/hp_info.c:
After merge fixes
heap/hp_open.c:
After merge fixes
include/heap.h:
After merge fixes
include/m_ctype.h:
Use pchar instead of 'int' for character parameters.
Added 'my_binary_compare()'
include/m_string.h:
Fixed wrong define
innobase/ibuf/ibuf0ibuf.c:
After merge fixes
innobase/srv/srv0start.c:
After merge fixes
mysql-test/r/alter_table.result:
Fixed results after merge
mysql-test/r/auto_increment.result:
Fixed results after merge
mysql-test/r/bdb.result:
Fixed results after merge
mysql-test/r/binary.result:
Fixed results after merge
mysql-test/r/create.result:
Fixed results after merge
mysql-test/r/ctype_mb.result:
Fixed results after merge
mysql-test/r/ctype_tis620.result:
Fixed results after merge
mysql-test/r/ctype_utf8.result:
Fixed results after merge
mysql-test/r/delete.result:
Fixed results after merge
mysql-test/r/func_compress.result:
Fixed results after merge
mysql-test/r/func_gconcat.result:
Fixed results after merge
mysql-test/r/func_group.result:
Fixed results after merge
mysql-test/r/func_str.result:
Fixed results after merge
mysql-test/r/innodb.result:
Fixed results after merge
mysql-test/r/insert.result:
Fixed results after merge
mysql-test/r/insert_select.result:
Fixed results after merge
mysql-test/r/key.result:
Fixed results after merge
mysql-test/r/loaddata.result:
Fixed results after merge
mysql-test/r/lock.result:
Fixed results after merge
mysql-test/r/myisam.result:
Fixed results after merge
mysql-test/r/null.result:
Fixed results after merge
mysql-test/r/null_key.result:
Fixed results after merge
mysql-test/r/order_by.result:
Fixed results after merge
mysql-test/r/query_cache.result:
Fixed results after merge
mysql-test/r/range.result:
Fixed results after merge
mysql-test/r/rpl_multi_delete.result:
Fixed results after merge
mysql-test/r/rpl_until.result:
Fixed results after merge
mysql-test/r/subselect.result:
Fixed results after merge
mysql-test/r/subselect_innodb.result:
Fixed results after merge
mysql-test/r/type_blob.result:
Fixed results after merge
mysql-test/r/type_datetime.result:
Fixed results after merge
mysql-test/r/type_decimal.result:
Fixed results after merge
mysql-test/r/type_enum.result:
Fixed results after merge
mysql-test/r/type_float.result:
Fixed results after merge
mysql-test/r/type_ranges.result:
Fixed results after merge
mysql-test/r/type_time.result:
Fixed results after merge
mysql-test/r/type_timestamp.result:
Fixed results after merge
mysql-test/r/type_uint.result:
Fixed results after merge
mysql-test/r/type_year.result:
Fixed results after merge
mysql-test/r/variables.result:
Fixed results after merge
mysql-test/r/warnings.result:
Fixed results after merge
mysql-test/t/case.test:
Fixed shifted error messages
mysql-test/t/create.test:
Fixed shifted error messages
mysql-test/t/ctype_collate.test:
Fixed shifted error messages
mysql-test/t/ctype_tis620.test:
Merge with 4.0 ctype_tis620 test
mysql-test/t/delete.test:
Fixed shifted error messages
mysql-test/t/derived.test:
Fixed shifted error messages
mysql-test/t/fulltext.test:
Fixed shifted error messages
mysql-test/t/func_in.test:
Fixed shifted error messages
mysql-test/t/func_str.test:
Fixed shifted error messages
mysql-test/t/func_test.test:
Fixed shifted error messages
mysql-test/t/grant.test:
Fixed shifted error messages
mysql-test/t/innodb.test:
Change to 4.1 syntax
mysql-test/t/key_cache.test:
Fixed shifted error messages
mysql-test/t/myisam.test:
New test of blob and end space
mysql-test/t/row.test:
Fixed shifted error messages
mysql-test/t/rpl_until.test:
Fixed shifted error messages
mysql-test/t/subselect.test:
Fixed shifted error messages
mysql-test/t/subselect_innodb.test:
Fix test to take into account foreign key constraints
mysql-test/t/union.test:
Fixed shifted error messages
mysql-test/t/user_var.test:
Fixed shifted error messages
mysql-test/t/variables.test:
Fixed shifted error messages
mysys/my_handler.c:
Merge with 4.0 code
sql/ha_heap.cc:
After merge fixes
sql/handler.cc:
After merge fixes
sql/item.cc:
After merge fixes
sql/item_cmpfunc.cc:
Ensure that we are comparing end space with BINARY strings
sql/item_cmpfunc.h:
Ensure that we are comparing end space with BINARY strings
sql/log_event.cc:
More DBUG statements
Ensure that we use all options to LOAD DATA in replication
sql/opt_range.cc:
After merge fixes
sql/sql_db.cc:
After merge fixes
sql/sql_handler.cc:
After merge fixes
Use 'any_db' instead of '' to mean 'no database comparison'
sql/sql_parse.cc:
After merge fixes
sql/sql_select.cc:
After merge fixes
Added function comment for setup_group()
sql/sql_string.cc:
Added stringcmp() for binary comparison.
Added function comments for sortcmp() and stringcmp()
sql/sql_string.h:
Added stringcmp()
sql/sql_table.cc:
After merge fixes
sql/sql_update.cc:
After merge fixes
sql/sql_yacc.yy:
Use 'any_db' instead of '' to mean any database. Using "" causes a 'wrong db name' error.
strings/ctype-big5.c:
Strip only end space, not other space characters.
strings/ctype-bin.c:
Removed some not needed functions.
Added function comments
Don't remove end space in comparisons
Change my_wildcmp_bin() to be 'identical' with other similar code
strings/ctype-czech.c:
Strip only end space, not other space characters.
strings/ctype-gbk.c:
Strip only end space, not other space characters.
strings/ctype-latin1.c:
Strip only end space, not other space characters.
strings/ctype-mb.c:
Strip only end space, not other space characters.
strings/ctype-simple.c:
Strip only end space, not other space characters.
strings/ctype-sjis.c:
Strip only end space, not other space characters.
strings/ctype-tis620.c:
Added usage of my_instr_simple. This needs to be cleaned up!
strings/ctype-utf8.c:
Strip only end space, not other space characters.
strings/ctype-win1250ch.c:
Strip only end space, not other space characters.
Fixed indentation
strings/strto.c:
Code cleanup
2004-02-16 10:03:25 +02:00
|
|
|
dblen= strlen(db);
|
2004-06-24 15:06:56 +02:00
|
|
|
table_ptr= &(thd->handler_tables);
|
|
|
|
*was_flushed= FALSE;
|
2001-12-06 14:10:51 +02:00
|
|
|
|
2004-06-25 20:13:05 +03:00
|
|
|
for (TABLE *table= *table_ptr; table ; table= *table_ptr)
|
2001-04-07 00:18:33 +02:00
|
|
|
{
|
After merge fixes
Added more DBUG statements
Ensure that we are comparing end space with BINARY strings
Use 'any_db' instead of '' to mean any database. (For HANDLER command)
Only strip ' ' when comparing CHAR, not other space-like characters (like \t)
BitKeeper/deleted/.del-ctype_tis620.result-old~3578ceb0b8284685:
Delete: mysql-test/r/ctype_tis620.result-old
BitKeeper/deleted/.del-ctype_tis620.test-old~ffb1bbd2935d1aba:
Delete: mysql-test/t/ctype_tis620.test-old
client/mysqlbinlog.cc:
Added DBUG statements
Added call of my_end() to free all used memory on exit
heap/hp_info.c:
After merge fixes
heap/hp_open.c:
After merge fixes
include/heap.h:
After merge fixes
include/m_ctype.h:
Use pchar instead of 'int' for character parameters.
Added 'my_binary_compare()'
include/m_string.h:
Fixed wrong define
innobase/ibuf/ibuf0ibuf.c:
After merge fixes
innobase/srv/srv0start.c:
After merge fixes
mysql-test/r/alter_table.result:
Fixed results after merge
mysql-test/r/auto_increment.result:
Fixed results after merge
mysql-test/r/bdb.result:
Fixed results after merge
mysql-test/r/binary.result:
Fixed results after merge
mysql-test/r/create.result:
Fixed results after merge
mysql-test/r/ctype_mb.result:
Fixed results after merge
mysql-test/r/ctype_tis620.result:
Fixed results after merge
mysql-test/r/ctype_utf8.result:
Fixed results after merge
mysql-test/r/delete.result:
Fixed results after merge
mysql-test/r/func_compress.result:
Fixed results after merge
mysql-test/r/func_gconcat.result:
Fixed results after merge
mysql-test/r/func_group.result:
Fixed results after merge
mysql-test/r/func_str.result:
Fixed results after merge
mysql-test/r/innodb.result:
Fixed results after merge
mysql-test/r/insert.result:
Fixed results after merge
mysql-test/r/insert_select.result:
Fixed results after merge
mysql-test/r/key.result:
Fixed results after merge
mysql-test/r/loaddata.result:
Fixed results after merge
mysql-test/r/lock.result:
Fixed results after merge
mysql-test/r/myisam.result:
Fixed results after merge
mysql-test/r/null.result:
Fixed results after merge
mysql-test/r/null_key.result:
Fixed results after merge
mysql-test/r/order_by.result:
Fixed results after merge
mysql-test/r/query_cache.result:
Fixed results after merge
mysql-test/r/range.result:
Fixed results after merge
mysql-test/r/rpl_multi_delete.result:
Fixed results after merge
mysql-test/r/rpl_until.result:
Fixed results after merge
mysql-test/r/subselect.result:
Fixed results after merge
mysql-test/r/subselect_innodb.result:
Fixed results after merge
mysql-test/r/type_blob.result:
Fixed results after merge
mysql-test/r/type_datetime.result:
Fixed results after merge
mysql-test/r/type_decimal.result:
Fixed results after merge
mysql-test/r/type_enum.result:
Fixed results after merge
mysql-test/r/type_float.result:
Fixed results after merge
mysql-test/r/type_ranges.result:
Fixed results after merge
mysql-test/r/type_time.result:
Fixed results after merge
mysql-test/r/type_timestamp.result:
Fixed results after merge
mysql-test/r/type_uint.result:
Fixed results after merge
mysql-test/r/type_year.result:
Fixed results after merge
mysql-test/r/variables.result:
Fixed results after merge
mysql-test/r/warnings.result:
Fixed results after merge
mysql-test/t/case.test:
Fixed shifted error messages
mysql-test/t/create.test:
Fixed shifted error messages
mysql-test/t/ctype_collate.test:
Fixed shifted error messages
mysql-test/t/ctype_tis620.test:
Merge with 4.0 ctype_tis620 test
mysql-test/t/delete.test:
Fixed shifted error messages
mysql-test/t/derived.test:
Fixed shifted error messages
mysql-test/t/fulltext.test:
Fixed shifted error messages
mysql-test/t/func_in.test:
Fixed shifted error messages
mysql-test/t/func_str.test:
Fixed shifted error messages
mysql-test/t/func_test.test:
Fixed shifted error messages
mysql-test/t/grant.test:
Fixed shifted error messages
mysql-test/t/innodb.test:
Change to 4.1 syntax
mysql-test/t/key_cache.test:
Fixed shifted error messages
mysql-test/t/myisam.test:
New test of blob and end space
mysql-test/t/row.test:
Fixed shifted error messages
mysql-test/t/rpl_until.test:
Fixed shifted error messages
mysql-test/t/subselect.test:
Fixed shifted error messages
mysql-test/t/subselect_innodb.test:
Fix test to take into account foreign key constraints
mysql-test/t/union.test:
Fixed shifted error messages
mysql-test/t/user_var.test:
Fixed shifted error messages
mysql-test/t/variables.test:
Fixed shifted error messages
mysys/my_handler.c:
Merge with 4.0 code
sql/ha_heap.cc:
After merge fixes
sql/handler.cc:
After merge fixes
sql/item.cc:
After merge fixes
sql/item_cmpfunc.cc:
Ensure that we are comparing end space with BINARY strings
sql/item_cmpfunc.h:
Ensure that we are comparing end space with BINARY strings
sql/log_event.cc:
More DBUG statements
Ensure that we use all options to LOAD DATA in replication
sql/opt_range.cc:
After merge fixes
sql/sql_db.cc:
After merge fixes
sql/sql_handler.cc:
After merge fixes
Use 'any_db' instead of '' to mean 'no database comparison'
sql/sql_parse.cc:
After merge fixes
sql/sql_select.cc:
After merge fixes
Added function comment for setup_group()
sql/sql_string.cc:
Added stringcmp() for binary comparison.
Added function comments for sortcmp() and stringcmp()
sql/sql_string.h:
Added stringcmp()
sql/sql_table.cc:
After merge fixes
sql/sql_update.cc:
After merge fixes
sql/sql_yacc.yy:
Use 'any_db' instead of '' to mean any database. Using "" causes a 'wrong db name' error.
strings/ctype-big5.c:
Strip only end space, not other space characters.
strings/ctype-bin.c:
Removed some not needed functions.
Added function comments
Don't remove end space in comparisons
Change my_wildcmp_bin() to be 'identical' with other similar code
strings/ctype-czech.c:
Strip only end space, not other space characters.
strings/ctype-gbk.c:
Strip only end space, not other space characters.
strings/ctype-latin1.c:
Strip only end space, not other space characters.
strings/ctype-mb.c:
Strip only end space, not other space characters.
strings/ctype-simple.c:
Strip only end space, not other space characters.
strings/ctype-sjis.c:
Strip only end space, not other space characters.
strings/ctype-tis620.c:
Added usage of my_instr_simple. This needs to be cleaned up!
strings/ctype-utf8.c:
Strip only end space, not other space characters.
strings/ctype-win1250ch.c:
Strip only end space, not other space characters.
Fixed indentation
strings/strto.c:
Code cleanup
2004-02-16 10:03:25 +02:00
|
|
|
if ((db == any_db || !memcmp(table->table_cache_key, db, dblen)) &&
|
2003-02-04 21:52:14 +02:00
|
|
|
!my_strcasecmp(system_charset_info,
|
|
|
|
(is_alias ? table->table_name : table->real_name),
|
|
|
|
table_name))
|
2003-11-18 22:06:47 +01:00
|
|
|
{
|
|
|
|
if (table->version != refresh_version)
|
|
|
|
{
|
2004-06-24 15:06:56 +02:00
|
|
|
if (!dont_lock)
|
|
|
|
VOID(pthread_mutex_lock(&LOCK_open));
|
2004-07-15 04:19:07 +03:00
|
|
|
|
|
|
|
table->file->ha_index_or_rnd_end();
|
2004-06-24 15:06:56 +02:00
|
|
|
if (close_thread_table(thd, table_ptr))
|
2003-11-18 22:06:47 +01:00
|
|
|
{
|
|
|
|
/* Tell threads waiting for refresh that something has happened */
|
|
|
|
VOID(pthread_cond_broadcast(&COND_refresh));
|
|
|
|
}
|
2004-06-24 15:06:56 +02:00
|
|
|
if (!dont_lock)
|
|
|
|
VOID(pthread_mutex_unlock(&LOCK_open));
|
|
|
|
*was_flushed= TRUE;
|
2003-11-18 22:06:47 +01:00
|
|
|
continue;
|
|
|
|
}
|
2004-06-24 15:06:56 +02:00
|
|
|
*was_flushed= FALSE;
|
2001-04-15 22:56:20 +02:00
|
|
|
break;
|
2003-11-18 22:06:47 +01:00
|
|
|
}
|
2004-06-25 20:13:05 +03:00
|
|
|
table_ptr= &(table->next);
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|
2004-06-24 15:06:56 +02:00
|
|
|
return table_ptr;
|
2001-04-07 00:18:33 +02:00
|
|
|
}
|