summaryrefslogtreecommitdiff
path: root/sql/sql_class.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r--sql/sql_class.cc593
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;
+}