diff options
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r-- | sql/sql_class.cc | 593 |
1 files changed, 593 insertions, 0 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc new file mode 100644 index 00000000000..19b9ecdaae0 --- /dev/null +++ b/sql/sql_class.cc @@ -0,0 +1,593 @@ +/* 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 */ + + +/***************************************************************************** +** +** This file implements classes defined in sql_class.h +** Especially the classes to handle a result from a select +** +*****************************************************************************/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "sql_acl.h" +#include <m_ctype.h> +#include <sys/stat.h> +#ifdef __WIN__ +#include <io.h> +#endif + +/***************************************************************************** +** Instansiate templates +*****************************************************************************/ + +#ifdef __GNUC__ +/* Used templates */ +template class List<Key>; +template class List_iterator<Key>; +template class List<key_part_spec>; +template class List_iterator<key_part_spec>; +template class List<Alter_drop>; +template class List_iterator<Alter_drop>; +template class List<Alter_column>; +template class List_iterator<Alter_column>; +#endif + +/**************************************************************************** +** User variables +****************************************************************************/ + +static byte* get_var_key(user_var_entry *entry, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=(uint) entry->name.length; + return (byte*) entry->name.str; +} + +static void free_var(user_var_entry *entry) +{ + char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry)); + if (entry->value && entry->value != pos) + my_free(entry->value, MYF(0)); + my_free((char*) entry,MYF(0)); +} + + +/**************************************************************************** +** Thread specific functions +****************************************************************************/ + +THD::THD() +{ + host=user=db=query=ip=0; + proc_info="login"; + locked=killed=count_cuted_fields=some_tables_deleted=no_errors=password= + fatal_error=query_start_used=last_insert_id_used=insert_id_used= + user_time=bootstrap=in_lock_tables=global_read_lock=0; + query_length=col_access=0; + query_error=0; + server_status=SERVER_STATUS_AUTOCOMMIT; + next_insert_id=last_insert_id=0; + open_tables=temporary_tables=0; + tmp_table=0; + lock=locked_tables=0; + cuted_fields=0L; + options=thd_startup_options; + update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; + start_time=(time_t) 0; + last_nx_table = last_nx_db = 0; + inactive_timeout=net_wait_timeout; + cond_count=0; + command=COM_CONNECT; + set_query_id=1; + default_select_limit= HA_POS_ERROR; + max_join_size= ((::max_join_size != ~ (ulong) 0L) ? ::max_join_size : + HA_POS_ERROR); + convert_set=0; + mysys_var=0; + db_access=NO_ACCESS; + hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, + (hash_get_key) get_var_key, + (void (*)(void*)) free_var,0); + net.vio=0; + ull=0; + system_thread=0; + bzero((char*) &alloc,sizeof(alloc)); +#ifdef __WIN__ + real_id = 0 ; +#endif + transaction.bdb_lock_count=0; + transaction.bdb_tid=0; +} + +THD::~THD() +{ + DBUG_ENTER("~THD()"); + /* Close connection */ + if (net.vio) + { + vio_delete(net.vio); + net_end(&net); + } + ha_rollback(this); + if (locked_tables) + { + lock=locked_tables; locked_tables=0; + close_thread_tables(this); + } + close_temporary_tables(this); + if (global_read_lock) + { + pthread_mutex_lock(&LOCK_open); + ::global_read_lock--; + pthread_cond_broadcast(&COND_refresh); + pthread_mutex_unlock(&LOCK_open); + } + if (ull) + { + pthread_mutex_lock(&LOCK_user_locks); + item_user_lock_release(ull); + pthread_mutex_unlock(&LOCK_user_locks); + } + hash_free(&user_vars); + + DBUG_PRINT("info", ("freeing host")); + + safeFree(host); + safeFree(user); + safeFree(db); + safeFree(ip); + free_root(&alloc); + mysys_var=0; // Safety (shouldn't be needed) + DBUG_VOID_RETURN; +} + +// remember the location of thread info, the structure needed for +// sql_alloc() and the structure for the net buffer + +bool THD::store_globals() +{ + return (my_pthread_setspecific_ptr(THR_THD, this) || + my_pthread_setspecific_ptr(THR_MALLOC, &alloc) || + my_pthread_setspecific_ptr(THR_NET, &net)); +} + + +/***************************************************************************** +** Functions to provide a interface to select results +*****************************************************************************/ + +select_result::select_result() +{ + thd=current_thd; +} + +static String default_line_term("\n"),default_escaped("\\"), + default_field_term("\t"); + +sql_exchange::sql_exchange(char *name,bool flag) + :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0) +{ + field_term= &default_field_term; + enclosed= line_start= &empty_string; + line_term= &default_line_term; + escaped= &default_escaped; +} + +bool select_send::send_fields(List<Item> &list,uint flag) +{ + return ::send_fields(thd,list,flag); +} + + +/* Send data to client. Returns 0 if ok */ + +bool select_send::send_data(List<Item> &items) +{ + List_iterator<Item> li(items); + String *packet= &thd->packet; + DBUG_ENTER("send_data"); + + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + DBUG_RETURN(0); + } + packet->length(0); // Reset packet + Item *item; + while ((item=li++)) + { + if (item->send(packet)) + { + packet->free(); // Free used + my_error(ER_OUT_OF_RESOURCES,MYF(0)); + DBUG_RETURN(1); + } + } + bool error=my_net_write(&thd->net,(char*) packet->ptr(),packet->length()); + DBUG_RETURN(error); +} + + +void select_send::send_error(uint errcode,const char *err) +{ + ::send_error(&thd->net,errcode,err); +} + +bool select_send::send_eof() +{ + /* Unlock tables before sending packet to gain some speed */ + if (thd->lock) + { + mysql_unlock_tables(thd, thd->lock); thd->lock=0; + } + ::send_eof(&thd->net); + return 0; +} + + +/*************************************************************************** +** Export of select to textfile +***************************************************************************/ + + +select_export::~select_export() +{ + if (file >= 0) + { // This only happens in case of error + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + file= -1; + } +} + +int +select_export::prepare(List<Item> &list) +{ + char path[FN_REFLEN]; + uint option=4; + bool blob_flag=0; +#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS + option|=1; // Force use of db directory +#endif + if (strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN) + strmake(path,exchange->file_name,FN_REFLEN-1); + (void) fn_format(path,exchange->file_name, thd->db ? thd->db : "", "", + option); + if (!access(path,F_OK)) + { + my_error(ER_FILE_EXISTS_ERROR,MYF(0),exchange->file_name); + return 1; + } + /* Create the file world readable */ + if ((file=my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0) + return 1; +#ifdef HAVE_FCHMOD + (void) fchmod(file,0666); // Because of umask() +#else + (void) chmod(path,0666); +#endif + if (init_io_cache(&cache,file,0L,WRITE_CACHE,0L,1,MYF(MY_WME))) + { + my_close(file,MYF(0)); + file= -1; + return 1; + } + /* Check if there is any blobs in data */ + { + List_iterator<Item> li(list); + Item *item; + while ((item=li++)) + { + if (item->max_length >= MAX_BLOB_WIDTH) + { + blob_flag=1; + break; + } + } + } + field_term_length=exchange->field_term->length(); + if (!exchange->line_term->length()) + exchange->line_term=exchange->field_term; // Use this if it exists + field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] : + field_term_length ? (*exchange->field_term)[0] : INT_MAX); + escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1); + line_sep_char= (exchange->line_term->length() ? + (*exchange->line_term)[0] : INT_MAX); + if (!field_term_length) + exchange->opt_enclosed=0; + if (!exchange->enclosed->length()) + exchange->opt_enclosed=1; // A little quicker loop + fixed_row_size= (!field_term_length && !exchange->enclosed->length() && + !blob_flag); + return 0; +} + + +bool select_export::send_data(List<Item> &items) +{ + + DBUG_ENTER("send_data"); + char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH]; + bool space_inited=0; + String tmp(buff,sizeof(buff)),*res; + tmp.length(0); + + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + DBUG_RETURN(0); + } + row_count++; + Item *item; + char *buff_ptr=buff; + uint used_length=0,items_left=items.elements; + List_iterator<Item> li(items); + + if (my_b_write(&cache,(byte*) exchange->line_start->ptr(), + exchange->line_start->length())) + goto err; + while ((item=li++)) + { + Item_result result_type=item->result_type(); + res=item->str_result(&tmp); + if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT)) + { + if (my_b_write(&cache,(byte*) exchange->enclosed->ptr(), + exchange->enclosed->length())) + goto err; + } + if (!res) + { // NULL + if (!fixed_row_size) + { + if (escape_char != -1) // Use \N syntax + { + null_buff[0]=escape_char; + null_buff[1]='N'; + if (my_b_write(&cache,(byte*) null_buff,2)) + goto err; + } + else if (my_b_write(&cache,(byte*) "NULL",4)) + goto err; + } + else + { + used_length=0; // Fill with space + } + } + else + { + if (fixed_row_size) + used_length=min(res->length(),item->max_length); + else + used_length=res->length(); + if (result_type == STRING_RESULT && escape_char != -1) + { + char *pos,*start,*end; + + for (start=pos=(char*) res->ptr(),end=pos+used_length ; + pos != end ; + pos++) + { +#ifdef USE_MB + if (use_mb(default_charset_info)) + { + int l; + if ((l=my_ismbchar(default_charset_info, pos, end))) + { + pos += l-1; + continue; + } + } +#endif + if ((int) *pos == escape_char || (int) *pos == field_sep_char || + (int) *pos == line_sep_char || !*pos) + { + char tmp_buff[2]; + tmp_buff[0]= escape_char; + tmp_buff[1]= *pos ? *pos : '0'; + if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) || + my_b_write(&cache,(byte*) tmp_buff,2)) + goto err; + start=pos+1; + } + } + if (my_b_write(&cache,(byte*) start,(uint) (pos-start))) + goto err; + } + else if (my_b_write(&cache,(byte*) res->ptr(),used_length)) + goto err; + } + if (fixed_row_size) + { // Fill with space + if (item->max_length > used_length) + { + /* QQ: Fix by adding a my_b_fill() function */ + if (!space_inited) + { + space_inited=1; + bfill(space,sizeof(space),' '); + } + uint length=item->max_length-used_length; + for ( ; length > sizeof(space) ; length-=sizeof(space)) + { + if (my_b_write(&cache,(byte*) space,sizeof(space))) + goto err; + } + if (my_b_write(&cache,(byte*) space,length)) + goto err; + } + } + buff_ptr=buff; // Place separators here + if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT)) + { + memcpy(buff_ptr,exchange->enclosed->ptr(),exchange->enclosed->length()); + buff_ptr+=exchange->enclosed->length(); + } + if (--items_left) + { + memcpy(buff_ptr,exchange->field_term->ptr(),field_term_length); + buff_ptr+=field_term_length; + } + if (my_b_write(&cache,(byte*) buff,(uint) (buff_ptr-buff))) + goto err; + } + if (my_b_write(&cache,(byte*) exchange->line_term->ptr(), + exchange->line_term->length())) + goto err; + DBUG_RETURN(0); +err: + DBUG_RETURN(1); +} + + +void select_export::send_error(uint errcode,const char *err) +{ + ::send_error(&thd->net,errcode,err); + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + file= -1; +} + + +bool select_export::send_eof() +{ + int error=test(end_io_cache(&cache)); + if (my_close(file,MYF(MY_WME))) + error=1; + if (error) + ::send_error(&thd->net); + else + ::send_ok(&thd->net,row_count); + file= -1; + return error; +} + + +/*************************************************************************** +** Dump of select to a binary file +***************************************************************************/ + + +select_dump::~select_dump() +{ + if (file >= 0) + { // This only happens in case of error + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + file= -1; + } +} + +int +select_dump::prepare(List<Item> &list __attribute__((unused))) +{ + uint option=4; +#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS + option|=1; // Force use of db directory +#endif + (void) fn_format(path,exchange->file_name, thd->db ? thd->db : "", "", + option); + if (!access(path,F_OK)) + { + my_error(ER_FILE_EXISTS_ERROR,MYF(0),exchange->file_name); + return 1; + } + /* Create the file world readable */ + if ((file=my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0) + return 1; +#ifdef HAVE_FCHMOD + (void) fchmod(file,0666); // Because of umask() +#else + (void) chmod(path,0666); +#endif + if (init_io_cache(&cache,file,0L,WRITE_CACHE,0L,1,MYF(MY_WME))) + { + my_close(file,MYF(0)); + my_delete(path,MYF(0)); + file= -1; + return 1; + } + return 0; +} + + +bool select_dump::send_data(List<Item> &items) +{ + List_iterator<Item> li(items); + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff)),*res; + tmp.length(0); + Item *item; + DBUG_ENTER("send_data"); + + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + DBUG_RETURN(0); + } + if (row_count++ > 1) + { + my_error(ER_TOO_MANY_ROWS,MYF(0)); + goto err; + } + while ((item=li++)) + { + Item_result result_type=item->result_type(); + res=item->str_result(&tmp); + if (!res) + { + if (my_b_write(&cache,(byte*) "",1)) goto err; // NULL + } + else if (my_b_write(&cache,(byte*) res->ptr(),res->length())) + { + my_error(ER_ERROR_ON_WRITE,MYF(0), path, my_errno); + goto err; + } + } + DBUG_RETURN(0); +err: + DBUG_RETURN(1); +} + + +void select_dump::send_error(uint errcode,const char *err) +{ + ::send_error(&thd->net,errcode,err); + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + (void) my_delete(path,MYF(0)); // Delete file on error + file= -1; +} + + +bool select_dump::send_eof() +{ + int error=test(end_io_cache(&cache)); + if (my_close(file,MYF(MY_WME))) + error=1; + if (error) + ::send_error(&thd->net); + else + ::send_ok(&thd->net,row_count); + file= -1; + return error; +} |