sql_load.cc 42.9 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000-2006 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4
   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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

unknown's avatar
unknown committed
7 8 9 10
   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.
unknown's avatar
unknown committed
11

unknown's avatar
unknown committed
12 13 14 15 16 17 18 19 20
   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 */


/* Copy data from a textfile to table */
#include "mysql_priv.h"
#include <my_dir.h>
#include <m_ctype.h>
21
#include "rpl_mi.h"
22
#include "sql_repl.h"
unknown's avatar
unknown committed
23 24
#include "sp_head.h"
#include "sql_trigger.h"
unknown's avatar
unknown committed
25 26 27

class READ_INFO {
  File	file;
28
  uchar	*buffer,			/* Buffer for read text */
unknown's avatar
unknown committed
29 30 31 32 33 34 35 36
	*end_of_buff;			/* Data in bufferts ends here */
  uint	buff_length,			/* Length of buffert */
	max_length;			/* Max length of row */
  char	*field_term_ptr,*line_term_ptr,*line_start_ptr,*line_start_end;
  uint	field_term_length,line_term_length,enclosed_length;
  int	field_term_char,line_term_char,enclosed_char,escape_char;
  int	*stack,*stack_pos;
  bool	found_end_of_line,start_of_line,eof;
37
  bool  need_end_io_cache;
unknown's avatar
unknown committed
38 39 40 41 42
  IO_CACHE cache;
  NET *io_net;

public:
  bool error,line_cuted,found_null,enclosed;
43
  uchar	*row_start,			/* Found row starts here */
unknown's avatar
unknown committed
44
	*row_end;			/* Found row ends here */
unknown's avatar
unknown committed
45
  CHARSET_INFO *read_charset;
unknown's avatar
unknown committed
46

unknown's avatar
unknown committed
47
  READ_INFO(File file,uint tot_length,CHARSET_INFO *cs,
unknown's avatar
unknown committed
48
	    String &field_term,String &line_start,String &line_term,
49
	    String &enclosed,int escape,bool get_it_from_net, bool is_fifo);
unknown's avatar
unknown committed
50 51 52 53 54 55 56
  ~READ_INFO();
  int read_field();
  int read_fixed_length(void);
  int next_line(void);
  char unescape(char chr);
  int terminator(char *ptr,uint length);
  bool find_start_of_fields();
unknown's avatar
unknown committed
57 58 59 60
  /*
    We need to force cache close before destructor is invoked to log
    the last read block
  */
61 62 63 64 65 66
  void end_io_cache()
  {
    ::end_io_cache(&cache);
    need_end_io_cache = 0;
  }

unknown's avatar
unknown committed
67 68 69 70 71
  /*
    Either this method, or we need to make cache public
    Arg must be set from mysql_load() since constructor does not see
    either the table or THD value
  */
72
  void set_io_cache_arg(void* arg) { cache.arg = arg; }
unknown's avatar
unknown committed
73 74
};

75
static int read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
76 77
                             List<Item> &fields_vars, List<Item> &set_fields,
                             List<Item> &set_values, READ_INFO &read_info,
78 79 80
			     ulong skip_lines,
			     bool ignore_check_option_errors);
static int read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
81 82
                          List<Item> &fields_vars, List<Item> &set_fields,
                          List<Item> &set_values, READ_INFO &read_info,
83 84
			  String &enclosed, ulong skip_lines,
			  bool ignore_check_option_errors);
85
#ifndef EMBEDDED_LIBRARY
86
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
87
                                               const char* db_arg, /* table's database */
88 89 90 91 92
                                               const char* table_name_arg,
                                               enum enum_duplicates duplicates,
                                               bool ignore,
                                               bool transactional_table,
                                               int errocode);
93
#endif /* EMBEDDED_LIBRARY */
unknown's avatar
unknown committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

/*
  Execute LOAD DATA query

  SYNOPSYS
    mysql_load()
      thd - current thread
      ex  - sql_exchange object representing source file and its parsing rules
      table_list  - list of tables to which we are loading data
      fields_vars - list of fields and variables to which we read
                    data from file
      set_fields  - list of fields mentioned in set clause
      set_values  - expressions to assign to fields in previous list
      handle_duplicates - indicates whenever we should emit error or
                          replace row if we will meet duplicates.
      ignore -          - indicates whenever we should ignore duplicates
      read_file_from_client - is this LOAD DATA LOCAL ?

  RETURN VALUES
    TRUE - error / FALSE - success
*/

116
int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
unknown's avatar
unknown committed
117 118 119
	        List<Item> &fields_vars, List<Item> &set_fields,
                List<Item> &set_values,
                enum enum_duplicates handle_duplicates, bool ignore,
120
                bool read_file_from_client)
unknown's avatar
unknown committed
121 122 123
{
  char name[FN_REFLEN];
  File file;
124
  TABLE *table= NULL;
He Zhenxing's avatar
He Zhenxing committed
125
  int error= 0;
126 127
  String *field_term=ex->field_term,*escaped=ex->escaped;
  String *enclosed=ex->enclosed;
128
  bool is_fifo=0;
129
#ifndef EMBEDDED_LIBRARY
130
  LOAD_FILE_INFO lf_info;
131
  THD::killed_state killed_status;
132
#endif
unknown's avatar
unknown committed
133
  char *db = table_list->db;			// This is never null
unknown's avatar
unknown committed
134 135 136 137 138
  /*
    If path for file is not defined, we will use the current database.
    If this is not set, we will use the directory where the table to be
    loaded is located
  */
unknown's avatar
unknown committed
139
  char *tdb= thd->db ? thd->db : db;		// Result is never null
140
  ulong skip_lines= ex->skip_lines;
unknown's avatar
unknown committed
141
  bool transactional_table;
unknown's avatar
unknown committed
142 143
  DBUG_ENTER("mysql_load");

144 145 146 147 148 149 150 151
  /*
    Bug #34283
    mysqlbinlog leaves tmpfile after termination if binlog contains
    load data infile, so in mixed mode we go to row-based for
    avoiding the problem.
  */
  thd->set_current_stmt_binlog_row_based_if_mixed();

unknown's avatar
unknown committed
152 153 154 155
#ifdef EMBEDDED_LIBRARY
  read_file_from_client  = 0; //server is always in the same process 
#endif

unknown's avatar
unknown committed
156 157 158 159
  if (escaped->length() > 1 || enclosed->length() > 1)
  {
    my_message(ER_WRONG_FIELD_TERMINATORS,ER(ER_WRONG_FIELD_TERMINATORS),
	       MYF(0));
unknown's avatar
unknown committed
160
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
161
  }
162 163 164 165 166 167 168 169 170 171 172

  /* Report problems with non-ascii separators */
  if (!escaped->is_ascii() || !enclosed->is_ascii() ||
      !field_term->is_ascii() ||
      !ex->line_term->is_ascii() || !ex->line_start->is_ascii())
  {
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                 WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED,
                 ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
  } 

unknown's avatar
unknown committed
173 174
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
175 176
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
177
                                    table_list,
178
                                    &thd->lex->select_lex.leaf_tables, FALSE,
179
                                    INSERT_ACL | UPDATE_ACL,
180
                                    INSERT_ACL | UPDATE_ACL))
181
     DBUG_RETURN(-1);
unknown's avatar
unknown committed
182 183 184
  if (!table_list->table ||               // do not suport join view
      !table_list->updatable ||           // and derived tables
      check_key_in_view(thd, table_list))
unknown's avatar
VIEW  
unknown committed
185
  {
186
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD");
unknown's avatar
unknown committed
187
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
188
  }
189 190 191 192 193
  if (table_list->prepare_where(thd, 0, TRUE) ||
      table_list->prepare_check_option(thd))
  {
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
194 195 196 197 198 199
  /*
    Let us emit an error if we are loading data to table which is used
    in subselect in SET clause like we do it for INSERT.

    The main thing to fix to remove this restriction is to ensure that the
    table is marked to be 'used for insert' in which case we should never
200
    mark this table as 'const table' (ie, one that has only one row).
unknown's avatar
unknown committed
201
  */
202
  if (unique_table(thd, table_list, table_list->next_global, 0))
unknown's avatar
unknown committed
203 204 205 206 207
  {
    my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
    DBUG_RETURN(TRUE);
  }

unknown's avatar
VIEW  
unknown committed
208
  table= table_list->table;
209 210
  transactional_table= table->file->has_transactions();

unknown's avatar
unknown committed
211
  if (!fields_vars.elements)
unknown's avatar
unknown committed
212 213 214
  {
    Field **field;
    for (field=table->field; *field ; field++)
unknown's avatar
unknown committed
215
      fields_vars.push_back(new Item_field(*field));
216
    bitmap_set_all(table->write_set);
unknown's avatar
unknown committed
217 218 219 220 221
    table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
    /*
      Let us also prepare SET clause, altough it is probably empty
      in this case.
    */
222 223
    if (setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) ||
        setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
unknown's avatar
unknown committed
224
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
225 226 227
  }
  else
  {						// Part field list
unknown's avatar
VIEW  
unknown committed
228
    /* TODO: use this conds for 'WITH CHECK OPTIONS' */
229 230
    if (setup_fields(thd, 0, fields_vars, MARK_COLUMNS_WRITE, 0, 0) ||
        setup_fields(thd, 0, set_fields, MARK_COLUMNS_WRITE, 0, 0) ||
231
        check_that_all_fields_are_given_values(thd, table, table_list))
unknown's avatar
unknown committed
232
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
233 234 235 236
    /*
      Check whenever TIMESTAMP field with auto-set feature specified
      explicitly.
    */
237 238 239 240 241 242 243 244 245 246 247 248 249
    if (table->timestamp_field)
    {
      if (bitmap_is_set(table->write_set,
                        table->timestamp_field->field_index))
        table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
      else
      {
        bitmap_set_bit(table->write_set,
                       table->timestamp_field->field_index);
      }
    }
    /* Fix the expressions in SET clause */
    if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
unknown's avatar
unknown committed
250
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
251 252
  }

unknown's avatar
unknown committed
253
  prepare_triggers_for_insert_stmt(table);
254

unknown's avatar
unknown committed
255
  uint tot_length=0;
unknown's avatar
unknown committed
256 257 258
  bool use_blobs= 0, use_vars= 0;
  List_iterator_fast<Item> it(fields_vars);
  Item *item;
unknown's avatar
unknown committed
259

unknown's avatar
unknown committed
260
  while ((item= it++))
unknown's avatar
unknown committed
261
  {
262 263 264
    Item *real_item= item->real_item();

    if (real_item->type() == Item::FIELD_ITEM)
unknown's avatar
unknown committed
265
    {
266
      Field *field= ((Item_field*)real_item)->field;
unknown's avatar
unknown committed
267 268 269 270 271 272 273
      if (field->flags & BLOB_FLAG)
      {
        use_blobs= 1;
        tot_length+= 256;			// Will be extended if needed
      }
      else
        tot_length+= field->field_length;
unknown's avatar
unknown committed
274
    }
275
    else if (item->type() == Item::STRING_ITEM)
unknown's avatar
unknown committed
276
      use_vars= 1;
unknown's avatar
unknown committed
277 278 279 280 281
  }
  if (use_blobs && !ex->line_term->length() && !field_term->length())
  {
    my_message(ER_BLOBS_AND_NO_TERMINATED,ER(ER_BLOBS_AND_NO_TERMINATED),
	       MYF(0));
unknown's avatar
unknown committed
282
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
283
  }
unknown's avatar
unknown committed
284 285 286 287 288
  if (use_vars && !field_term->length() && !enclosed->length())
  {
    my_error(ER_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR, MYF(0));
    DBUG_RETURN(TRUE);
  }
unknown's avatar
unknown committed
289 290 291

  /* We can't give an error in the middle when using LOCAL files */
  if (read_file_from_client && handle_duplicates == DUP_ERROR)
292
    ignore= 1;
unknown's avatar
unknown committed
293

294
#ifndef EMBEDDED_LIBRARY
295
  if (read_file_from_client)
unknown's avatar
unknown committed
296
  {
unknown's avatar
unknown committed
297
    (void)net_request_file(&thd->net,ex->file_name);
unknown's avatar
unknown committed
298 299 300
    file = -1;
  }
  else
301
#endif
unknown's avatar
unknown committed
302 303 304 305
  {
#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
    ex->file_name+=dirname_length(ex->file_name);
#endif
306
    if (!dirname_length(ex->file_name))
unknown's avatar
unknown committed
307
    {
unknown's avatar
unknown committed
308
      strxnmov(name, FN_REFLEN-1, mysql_real_data_home, tdb, NullS);
unknown's avatar
unknown committed
309 310
      (void) fn_format(name, ex->file_name, name, "",
		       MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
unknown's avatar
unknown committed
311 312 313
    }
    else
    {
unknown's avatar
unknown committed
314
      (void) fn_format(name, ex->file_name, mysql_real_data_home, "",
315 316
                       MY_RELATIVE_PATH | MY_UNPACK_FILENAME |
                       MY_RETURN_REAL_PATH);
317
#if !defined(__WIN__) && ! defined(__NETWARE__)
unknown's avatar
unknown committed
318
      MY_STAT stat_info;
319
      if (!my_stat(name,&stat_info,MYF(MY_WME)))
unknown's avatar
unknown committed
320
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
321

322 323 324
      // if we are not in slave thread, the file must be:
      if (!thd->slave_thread &&
	  !((stat_info.st_mode & S_IROTH) == S_IROTH &&  // readable by others
325 326 327
	    (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink
	    ((stat_info.st_mode & S_IFREG) == S_IFREG ||
	     (stat_info.st_mode & S_IFIFO) == S_IFIFO)))
unknown's avatar
unknown committed
328
      {
329
	my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name);
unknown's avatar
unknown committed
330
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
331
      }
332 333
      if ((stat_info.st_mode & S_IFIFO) == S_IFIFO)
	is_fifo = 1;
unknown's avatar
unknown committed
334
#endif
335

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
      if (thd->slave_thread)
      {
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
        if (strncmp(active_mi->rli.slave_patternload_file, name, 
            active_mi->rli.slave_patternload_file_size))
        {
          /*
            LOAD DATA INFILE in the slave SQL Thread can only read from 
            --slave-load-tmpdir". This should never happen. Please, report a bug.
           */

          sql_print_error("LOAD DATA INFILE in the slave SQL Thread can only read from --slave-load-tmpdir. " \
                          "Please, report a bug.");
          my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--slave-load-tmpdir");
          DBUG_RETURN(TRUE);
        }
#else
        /*
          This is impossible and should never happen.
        */
        DBUG_ASSERT(FALSE); 
#endif
      }
359
      else if (!is_secure_file_path(name))
360
      {
361 362 363
        /* Read only allowed from within dir specified by secure_file_priv */
        my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--secure-file-priv");
        DBUG_RETURN(TRUE);
364 365
      }

unknown's avatar
unknown committed
366 367
    }
    if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0)
unknown's avatar
unknown committed
368
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
369 370 371 372
  }

  COPY_INFO info;
  bzero((char*) &info,sizeof(info));
373
  info.ignore= ignore;
unknown's avatar
unknown committed
374
  info.handle_duplicates=handle_duplicates;
375 376 377
  info.escape_char= (escaped->length() && (ex->escaped_given() ||
                    !(thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)))
                    ? (*escaped)[0] : INT_MAX;
unknown's avatar
unknown committed
378

379 380
  READ_INFO read_info(file,tot_length,
                      ex->cs ? ex->cs : thd->variables.collation_database,
unknown's avatar
unknown committed
381
		      *field_term,*ex->line_start, *ex->line_term, *enclosed,
382
		      info.escape_char, read_file_from_client, is_fifo);
unknown's avatar
unknown committed
383 384 385 386
  if (read_info.error)
  {
    if	(file >= 0)
      my_close(file,MYF(0));			// no files in net reading
unknown's avatar
unknown committed
387
    DBUG_RETURN(TRUE);				// Can't allocate buffers
unknown's avatar
unknown committed
388 389
  }

390
#ifndef EMBEDDED_LIBRARY
391
  if (mysql_bin_log.is_open())
392 393 394 395
  {
    lf_info.thd = thd;
    lf_info.wrote_create_file = 0;
    lf_info.last_pos_in_file = HA_POS_ERROR;
unknown's avatar
unknown committed
396
    lf_info.log_delayed= transactional_table;
unknown's avatar
unknown committed
397
    read_info.set_io_cache_arg((void*) &lf_info);
398
  }
399 400
#endif /*!EMBEDDED_LIBRARY*/

401
  thd->count_cuted_fields= CHECK_FIELD_WARN;		/* calc cuted fields */
unknown's avatar
unknown committed
402
  thd->cuted_fields=0L;
403 404
  /* Skip lines if there is a line terminator */
  if (ex->line_term->length())
unknown's avatar
unknown committed
405
  {
406 407
    /* ex->skip_lines needs to be preserved for logging */
    while (skip_lines > 0)
unknown's avatar
unknown committed
408
    {
409
      skip_lines--;
unknown's avatar
unknown committed
410 411 412 413
      if (read_info.next_line())
	break;
    }
  }
414

unknown's avatar
unknown committed
415 416
  if (!(error=test(read_info.error)))
  {
unknown's avatar
unknown committed
417

unknown's avatar
unknown committed
418
    table->next_number_field=table->found_next_number_field;
419
    if (ignore ||
unknown's avatar
unknown committed
420 421
	handle_duplicates == DUP_REPLACE)
      table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
422 423 424
    if (handle_duplicates == DUP_REPLACE &&
        (!table->triggers ||
         !table->triggers->has_delete_triggers()))
425
        table->file->extra(HA_EXTRA_WRITE_CAN_REPLACE);
426
    if (!thd->prelocked_mode)
427
      table->file->ha_start_bulk_insert((ha_rows) 0);
unknown's avatar
unknown committed
428
    table->copy_blobs=1;
unknown's avatar
unknown committed
429

unknown's avatar
unknown committed
430
    thd->abort_on_warning= (!ignore &&
unknown's avatar
unknown committed
431 432 433 434
                            (thd->variables.sql_mode &
                             (MODE_STRICT_TRANS_TABLES |
                              MODE_STRICT_ALL_TABLES)));

unknown's avatar
unknown committed
435
    if (!field_term->length() && !enclosed->length())
unknown's avatar
unknown committed
436 437
      error= read_fixed_length(thd, info, table_list, fields_vars,
                               set_fields, set_values, read_info,
438
			       skip_lines, ignore);
unknown's avatar
unknown committed
439
    else
unknown's avatar
unknown committed
440 441 442
      error= read_sep_field(thd, info, table_list, fields_vars,
                            set_fields, set_values, read_info,
			    *enclosed, skip_lines, ignore);
443
    if (!thd->prelocked_mode && table->file->ha_end_bulk_insert() && !error)
444 445 446 447
    {
      table->file->print_error(my_errno, MYF(0));
      error= 1;
    }
unknown's avatar
unknown committed
448
    table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
449
    table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
unknown's avatar
unknown committed
450 451
    table->next_number_field=0;
  }
452 453
  if (file >= 0)
    my_close(file,MYF(0));
unknown's avatar
unknown committed
454 455
  free_blobs(table);				/* if pack_blob was used */
  table->copy_blobs=0;
456
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
457 458 459 460 461 462 463 464 465
  /* 
     simulated killing in the middle of per-row loop
     must be effective for binlogging
  */
  DBUG_EXECUTE_IF("simulate_kill_bug27571",
                  {
                    error=1;
                    thd->killed= THD::KILL_QUERY;
                  };);
466 467 468 469 470

#ifndef EMBEDDED_LIBRARY
  killed_status= (error == 0) ? THD::NOT_KILLED : thd->killed;
#endif

471 472 473 474
  /*
    We must invalidate the table in query cache before binlog writing and
    ha_autocommit_...
  */
unknown's avatar
unknown committed
475
  query_cache_invalidate3(thd, table_list, 0);
unknown's avatar
unknown committed
476
  if (error)
477
  {
478 479 480
    if (read_file_from_client)
      while (!read_info.next_line())
	;
unknown's avatar
unknown committed
481

482
#ifndef EMBEDDED_LIBRARY
483
    if (mysql_bin_log.is_open())
484
    {
unknown's avatar
unknown committed
485
      {
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	/*
	  Make sure last block (the one which caused the error) gets
	  logged.  This is needed because otherwise after write of (to
	  the binlog, not to read_info (which is a cache))
	  Delete_file_log_event the bad block will remain in read_info
	  (because pre_read is not called at the end of the last
	  block; remember pre_read is called whenever a new block is
	  read from disk).  At the end of mysql_load(), the destructor
	  of read_info will call end_io_cache() which will flush
	  read_info, so we will finally have this in the binlog:

	  Append_block # The last successfull block
	  Delete_file
	  Append_block # The failing block
	  which is nonsense.
	  Or could also be (for a small file)
	  Create_file  # The failing block
	  which is nonsense (Delete_file is not written in this case, because:
	  Create_file has not been written, so Delete_file is not written, then
	  when read_info is destroyed end_io_cache() is called which writes
	  Create_file.
	*/
	read_info.end_io_cache();
	/* If the file was not empty, wrote_create_file is true */
	if (lf_info.wrote_create_file)
511
	{
512 513
          int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
          
He Zhenxing's avatar
He Zhenxing committed
514 515
          /* since there is already an error, the possible error of
             writing binary log will be ignored */
516
	  if (thd->transaction.stmt.modified_non_trans_table)
He Zhenxing's avatar
He Zhenxing committed
517 518 519 520 521 522
            (void) write_execute_load_query_log_event(thd, ex,
                                                      table_list->db, 
                                                      table_list->table_name,
                                                      handle_duplicates, ignore,
                                                      transactional_table,
                                                      errcode);
523 524 525
	  else
	  {
	    Delete_file_log_event d(thd, db, transactional_table);
He Zhenxing's avatar
He Zhenxing committed
526
	    (void) mysql_bin_log.write(&d);
527
	  }
528
	}
unknown's avatar
unknown committed
529
      }
530
    }
531
#endif /*!EMBEDDED_LIBRARY*/
532 533
    error= -1;				// Error on read
    goto err;
534
  }
535 536
  sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
	  (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
unknown's avatar
unknown committed
537

unknown's avatar
unknown committed
538 539
  if (thd->transaction.stmt.modified_non_trans_table)
    thd->transaction.all.modified_non_trans_table= TRUE;
540
#ifndef EMBEDDED_LIBRARY
541
  if (mysql_bin_log.is_open())
542
  {
543
    /*
544 545 546 547 548 549 550
      We need to do the job that is normally done inside
      binlog_query() here, which is to ensure that the pending event
      is written before tables are unlocked and before any other
      events are written.  We also need to update the table map
      version for the binary log to mark that table maps are invalid
      after this point.
     */
551
    if (thd->current_stmt_binlog_row_based)
He Zhenxing's avatar
He Zhenxing committed
552
      error= thd->binlog_flush_pending_rows_event(true);
553 554 555 556 557 558 559 560 561 562
    else
    {
      /*
        As already explained above, we need to call end_io_cache() or the last
        block will be logged only after Execute_load_query_log_event (which is
        wrong), when read_info is destroyed.
      */
      read_info.end_io_cache();
      if (lf_info.wrote_create_file)
      {
563
        int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
He Zhenxing's avatar
He Zhenxing committed
564 565 566 567 568
        error= write_execute_load_query_log_event(thd, ex,
                                                  table_list->db, table_list->table_name,
                                                  handle_duplicates, ignore,
                                                  transactional_table,
                                                  errcode);
569 570
      }
    }
He Zhenxing's avatar
He Zhenxing committed
571 572
    if (error)
      goto err;
573
  }
574
#endif /*!EMBEDDED_LIBRARY*/
unknown's avatar
unknown committed
575

576
  /* ok to client sent only after binlog write and engine commit */
577
  my_ok(thd, info.copied + info.deleted, 0L, name);
578
err:
unknown's avatar
unknown committed
579 580
  DBUG_ASSERT(transactional_table || !(info.copied || info.deleted) ||
              thd->transaction.stmt.modified_non_trans_table);
581
  table->file->ha_release_auto_increment();
582
  table->auto_increment_field_not_null= FALSE;
unknown's avatar
unknown committed
583
  thd->abort_on_warning= 0;
584
  DBUG_RETURN(error);
unknown's avatar
unknown committed
585 586
}

587

588 589
#ifndef EMBEDDED_LIBRARY

590
/* Not a very useful function; just to avoid duplication of code */
591
static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex,
592
                                               const char* db_arg,  /* table's database */
593 594 595 596
                                               const char* table_name_arg,
                                               enum enum_duplicates duplicates,
                                               bool ignore,
                                               bool transactional_table,
597
                                               int errcode)
598
{
599 600 601 602 603 604 605 606 607 608
  char                *load_data_query,
                      *end,
                      *fname_start,
                      *fname_end,
                      *p= NULL;
  size_t               pl= 0;
  List<Item>           fv;
  Item                *item, *val;
  String               pfield, pfields;
  int                  n;
609 610 611
  const char          *tbl= table_name_arg;
  const char          *tdb= (thd->db != NULL ? thd->db : db_arg);
  String              string_buf;
612

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
  if (!thd->db || strcmp(db_arg, thd->db)) 
  {
    /*
      If used database differs from table's database, 
      prefix table name with database name so that it 
      becomes a FQ name.
     */
    string_buf.set_charset(system_charset_info);
    string_buf.append(db_arg);
    string_buf.append("`");
    string_buf.append(".");
    string_buf.append("`");
    string_buf.append(table_name_arg);
    tbl= string_buf.c_ptr_safe();
  }

  Load_log_event       lle(thd, ex, tdb, tbl, fv, duplicates,
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
                           ignore, transactional_table);

  /*
    force in a LOCAL if there was one in the original.
  */
  if (thd->lex->local_file)
    lle.set_fname_outside_temp_buf(ex->file_name, strlen(ex->file_name));

  /*
    prepare fields-list and SET if needed; print_query won't do that for us.
  */
  if (!thd->lex->field_list.is_empty())
  {
    List_iterator<Item>  li(thd->lex->field_list);

    pfields.append(" (");
    n= 0;

    while ((item= li++))
    {
      if (n++)
        pfields.append(", ");
      if (item->name)
653 654
      {
        pfields.append("`");
655
        pfields.append(item->name);
656 657
        pfields.append("`");
      }
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
      else
        item->print(&pfields, QT_ORDINARY);
    }
    pfields.append(")");
  }

  if (!thd->lex->update_list.is_empty())
  {
    List_iterator<Item> lu(thd->lex->update_list);
    List_iterator<Item> lv(thd->lex->value_list);

    pfields.append(" SET ");
    n= 0;

    while ((item= lu++))
    {
      val= lv++;
      if (n++)
        pfields.append(", ");
677
      pfields.append("`");
678
      pfields.append(item->name);
679
      pfields.append("`");
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
      pfields.append("=");
      val->print(&pfields, QT_ORDINARY);
    }
  }

  p= pfields.c_ptr_safe();
  pl= strlen(p);

  if (!(load_data_query= (char *)thd->alloc(lle.get_query_buffer_length() + 1 + pl)))
    return TRUE;

  lle.print_query(FALSE, (const char *) ex->cs?ex->cs->csname:NULL,
                  load_data_query, &end,
                  (char **)&fname_start, (char **)&fname_end);

  strcpy(end, p);
  end += pl;

698
  Execute_load_query_log_event
699 700 701
    e(thd, load_data_query, end-load_data_query,
      (uint) ((char*) fname_start - load_data_query - 1),
      (uint) ((char*) fname_end - load_data_query),
702 703
      (duplicates == DUP_REPLACE) ? LOAD_DUP_REPLACE :
      (ignore ? LOAD_DUP_IGNORE : LOAD_DUP_ERROR),
704
      transactional_table, FALSE, errcode);
705 706 707
  return mysql_bin_log.write(&e);
}

708
#endif
709

unknown's avatar
unknown committed
710 711 712 713 714
/****************************************************************************
** Read of rows of fixed size + optional garage + optonal newline
****************************************************************************/

static int
715
read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
716 717 718
                  List<Item> &fields_vars, List<Item> &set_fields,
                  List<Item> &set_values, READ_INFO &read_info,
                  ulong skip_lines, bool ignore_check_option_errors)
unknown's avatar
unknown committed
719
{
unknown's avatar
unknown committed
720
  List_iterator_fast<Item> it(fields_vars);
unknown's avatar
unknown committed
721
  Item_field *sql_field;
722
  TABLE *table= table_list->table;
unknown's avatar
unknown committed
723
  bool err;
unknown's avatar
unknown committed
724 725 726 727 728 729
  DBUG_ENTER("read_fixed_length");

  while (!read_info.read_fixed_length())
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
730
      thd->send_kill_message();
unknown's avatar
unknown committed
731 732
      DBUG_RETURN(1);
    }
733 734 735 736 737 738 739 740 741 742 743
    if (skip_lines)
    {
      /*
	We could implement this with a simple seek if:
	- We are not using DATA INFILE LOCAL
	- escape character is  ""
	- line starting prefix is ""
      */
      skip_lines--;
      continue;
    }
unknown's avatar
unknown committed
744
    it.rewind();
745
    uchar *pos=read_info.row_start;
unknown's avatar
unknown committed
746 747 748
#ifdef HAVE_purify
    read_info.row_end[0]=0;
#endif
unknown's avatar
unknown committed
749 750 751 752 753 754

    restore_record(table, s->default_values);
    /*
      There is no variables in fields_vars list in this format so
      this conversion is safe.
    */
unknown's avatar
unknown committed
755 756
    while ((sql_field= (Item_field*) it++))
    {
757
      Field *field= sql_field->field;                  
758 759
      if (field == table->next_number_field)
        table->auto_increment_field_not_null= TRUE;
unknown's avatar
unknown committed
760 761 762 763 764 765 766
      /*
        No fields specified in fields_vars list can be null in this format.
        Mark field as not null, we should do this for each row because of
        restore_record...
      */
      field->set_notnull();

unknown's avatar
unknown committed
767 768
      if (pos == read_info.row_end)
      {
769 770 771
        thd->cuted_fields++;			/* Not enough fields */
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                            ER_WARN_TOO_FEW_RECORDS, 
772
                            ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
773 774
        if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
            ((Field_timestamp*) field)->set_time();
unknown's avatar
unknown committed
775 776 777 778
      }
      else
      {
	uint length;
779
	uchar save_chr;
unknown's avatar
unknown committed
780 781 782 783
	if ((length=(uint) (read_info.row_end-pos)) >
	    field->field_length)
	  length=field->field_length;
	save_chr=pos[length]; pos[length]='\0'; // Safeguard aganst malloc
unknown's avatar
unknown committed
784
        field->store((char*) pos,length,read_info.read_charset);
unknown's avatar
unknown committed
785 786 787 788 789 790
	pos[length]=save_chr;
	if ((pos+=length) > read_info.row_end)
	  pos= read_info.row_end;	/* Fills rest with space */
      }
    }
    if (pos != read_info.row_end)
791
    {
unknown's avatar
unknown committed
792
      thd->cuted_fields++;			/* To long row */
793 794
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_TOO_MANY_RECORDS, 
795
                          ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count); 
796
    }
797

unknown's avatar
unknown committed
798 799 800 801 802
    if (thd->killed ||
        fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
                                             ignore_check_option_errors,
                                             table->triggers,
                                             TRG_EVENT_INSERT))
unknown's avatar
unknown committed
803 804
      DBUG_RETURN(1);

805 806
    switch (table_list->view_check_option(thd,
                                          ignore_check_option_errors)) {
807 808 809 810 811 812 813
    case VIEW_CHECK_SKIP:
      read_info.next_line();
      goto continue_loop;
    case VIEW_CHECK_ERROR:
      DBUG_RETURN(-1);
    }

814 815 816
    err= write_record(thd, table, &info);
    table->auto_increment_field_not_null= FALSE;
    if (err)
unknown's avatar
unknown committed
817
      DBUG_RETURN(1);
unknown's avatar
unknown committed
818
   
unknown's avatar
unknown committed
819 820 821 822
    /*
      We don't need to reset auto-increment field since we are restoring
      its default value at the beginning of each loop iteration.
    */
unknown's avatar
unknown committed
823
    if (read_info.next_line())			// Skip to next line
unknown's avatar
unknown committed
824 825
      break;
    if (read_info.line_cuted)
826
    {
unknown's avatar
unknown committed
827
      thd->cuted_fields++;			/* To long row */
828 829
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_TOO_MANY_RECORDS, 
830
                          ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count); 
831
    }
832
    thd->row_count++;
833
continue_loop:;
unknown's avatar
unknown committed
834 835 836 837 838 839 840
  }
  DBUG_RETURN(test(read_info.error));
}



static int
841
read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
842 843
               List<Item> &fields_vars, List<Item> &set_fields,
               List<Item> &set_values, READ_INFO &read_info,
844 845
	       String &enclosed, ulong skip_lines,
	       bool ignore_check_option_errors)
unknown's avatar
unknown committed
846
{
unknown's avatar
unknown committed
847 848
  List_iterator_fast<Item> it(fields_vars);
  Item *item;
849
  TABLE *table= table_list->table;
unknown's avatar
unknown committed
850
  uint enclosed_length;
unknown's avatar
unknown committed
851
  bool err;
unknown's avatar
unknown committed
852 853 854
  DBUG_ENTER("read_sep_field");

  enclosed_length=enclosed.length();
855

unknown's avatar
unknown committed
856 857 858 859
  for (;;it.rewind())
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
860
      thd->send_kill_message();
unknown's avatar
unknown committed
861 862
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
863 864 865 866

    restore_record(table, s->default_values);

    while ((item= it++))
unknown's avatar
unknown committed
867 868
    {
      uint length;
869
      uchar *pos;
870
      Item *real_item;
unknown's avatar
unknown committed
871 872 873

      if (read_info.read_field())
	break;
unknown's avatar
unknown committed
874 875 876 877 878

      /* If this line is to be skipped we don't want to fill field or var */
      if (skip_lines)
        continue;

unknown's avatar
unknown committed
879 880 881
      pos=read_info.row_start;
      length=(uint) (read_info.row_end-pos);

882 883
      real_item= item->real_item();

884
      if ((!read_info.enclosed &&
885
	  (enclosed_length && length == 4 &&
886
           !memcmp(pos, STRING_WITH_LEN("NULL")))) ||
unknown's avatar
unknown committed
887 888
	  (length == 1 && read_info.found_null))
      {
889 890

        if (real_item->type() == Item::FIELD_ITEM)
unknown's avatar
unknown committed
891
        {
892
          Field *field= ((Item_field *)real_item)->field;
893 894 895 896 897 898
          if (field->reset())
          {
            my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name,
                     thd->row_count);
            DBUG_RETURN(1);
          }
unknown's avatar
unknown committed
899 900 901
          field->set_null();
          if (!field->maybe_null())
          {
902
            if (field->type() == MYSQL_TYPE_TIMESTAMP)
unknown's avatar
unknown committed
903 904
              ((Field_timestamp*) field)->set_time();
            else if (field != table->next_number_field)
905
              field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
unknown's avatar
unknown committed
906 907
                                 ER_WARN_NULL_TO_NOTNULL, 1);
          }
unknown's avatar
unknown committed
908
	}
909 910
        else if (item->type() == Item::STRING_ITEM)
        {
unknown's avatar
unknown committed
911 912
          ((Item_user_var_as_out_param *)item)->set_null_value(
                                                  read_info.read_charset);
913 914 915 916 917 918 919
        }
        else
        {
          my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
          DBUG_RETURN(1);
        }

unknown's avatar
unknown committed
920 921
	continue;
      }
unknown's avatar
unknown committed
922

923
      if (real_item->type() == Item::FIELD_ITEM)
unknown's avatar
unknown committed
924
      {
925
        Field *field= ((Item_field *)real_item)->field;
unknown's avatar
unknown committed
926 927
        field->set_notnull();
        read_info.row_end[0]=0;			// Safe to change end marker
unknown's avatar
unknown committed
928 929
        if (field == table->next_number_field)
          table->auto_increment_field_not_null= TRUE;
unknown's avatar
unknown committed
930 931
        field->store((char*) pos, length, read_info.read_charset);
      }
932 933
      else if (item->type() == Item::STRING_ITEM)
      {
unknown's avatar
unknown committed
934
        ((Item_user_var_as_out_param *)item)->set_value((char*) pos, length,
935 936 937 938 939 940 941
                                                        read_info.read_charset);
      }
      else
      {
        my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
        DBUG_RETURN(1);
      }
unknown's avatar
unknown committed
942
    }
943 944 945 946

    if (thd->is_error())
      read_info.error= 1;

unknown's avatar
unknown committed
947 948
    if (read_info.error)
      break;
949 950
    if (skip_lines)
    {
unknown's avatar
unknown committed
951
      skip_lines--;
952 953
      continue;
    }
unknown's avatar
unknown committed
954 955 956 957
    if (item)
    {
      /* Have not read any field, thus input file is simply ended */
      if (item == fields_vars.head())
unknown's avatar
unknown committed
958
	break;
unknown's avatar
unknown committed
959
      for (; item ; item= it++)
unknown's avatar
unknown committed
960
      {
961 962
        Item *real_item= item->real_item();
        if (real_item->type() == Item::FIELD_ITEM)
unknown's avatar
unknown committed
963
        {
964
          Field *field= ((Item_field *)real_item)->field;
965 966
          if (field->reset())
          {
unknown's avatar
unknown committed
967
            my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name,
968 969 970
                     thd->row_count);
            DBUG_RETURN(1);
          }
971 972
          if (!field->maybe_null() && field->type() == FIELD_TYPE_TIMESTAMP)
              ((Field_timestamp*) field)->set_time();
unknown's avatar
unknown committed
973 974 975 976 977 978 979 980 981 982 983
          /*
            QQ: We probably should not throw warning for each field.
            But how about intention to always have the same number
            of warnings in THD::cuted_fields (and get rid of cuted_fields
            in the end ?)
          */
          thd->cuted_fields++;
          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                              ER_WARN_TOO_FEW_RECORDS,
                              ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
        }
984 985
        else if (item->type() == Item::STRING_ITEM)
        {
unknown's avatar
unknown committed
986 987
          ((Item_user_var_as_out_param *)item)->set_null_value(
                                                  read_info.read_charset);
988 989 990 991 992 993
        }
        else
        {
          my_error(ER_LOAD_DATA_INVALID_COLUMN, MYF(0), item->full_name());
          DBUG_RETURN(1);
        }
unknown's avatar
unknown committed
994 995
      }
    }
996

unknown's avatar
unknown committed
997 998 999 1000 1001
    if (thd->killed ||
        fill_record_n_invoke_before_triggers(thd, set_fields, set_values,
                                             ignore_check_option_errors,
                                             table->triggers,
                                             TRG_EVENT_INSERT))
unknown's avatar
unknown committed
1002 1003
      DBUG_RETURN(1);

1004 1005
    switch (table_list->view_check_option(thd,
                                          ignore_check_option_errors)) {
1006 1007 1008 1009 1010 1011 1012
    case VIEW_CHECK_SKIP:
      read_info.next_line();
      goto continue_loop;
    case VIEW_CHECK_ERROR:
      DBUG_RETURN(-1);
    }

1013 1014 1015
    err= write_record(thd, table, &info);
    table->auto_increment_field_not_null= FALSE;
    if (err)
unknown's avatar
unknown committed
1016
      DBUG_RETURN(1);
unknown's avatar
unknown committed
1017 1018 1019 1020
    /*
      We don't need to reset auto-increment field since we are restoring
      its default value at the beginning of each loop iteration.
    */
unknown's avatar
unknown committed
1021
    if (read_info.next_line())			// Skip to next line
unknown's avatar
unknown committed
1022 1023
      break;
    if (read_info.line_cuted)
1024
    {
unknown's avatar
unknown committed
1025
      thd->cuted_fields++;			/* To long row */
1026
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
1027 1028
                          ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS), 
                          thd->row_count);   
unknown's avatar
unknown committed
1029 1030
      if (thd->killed)
        DBUG_RETURN(1);
1031
    }
1032
    thd->row_count++;
1033
continue_loop:;
unknown's avatar
unknown committed
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
  }
  DBUG_RETURN(test(read_info.error));
}


/* Unescape all escape characters, mark \N as null */

char
READ_INFO::unescape(char chr)
{
unknown's avatar
unknown committed
1044
  /* keep this switch synchornous with the ESCAPE_CHARS macro */
unknown's avatar
unknown committed
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
  switch(chr) {
  case 'n': return '\n';
  case 't': return '\t';
  case 'r': return '\r';
  case 'b': return '\b';
  case '0': return 0;				// Ascii null
  case 'Z': return '\032';			// Win32 end of file
  case 'N': found_null=1;

    /* fall through */
  default:  return chr;
  }
}


1060 1061 1062 1063
/*
  Read a line using buffering
  If last line is empty (in line mode) then it isn't outputed
*/
unknown's avatar
unknown committed
1064 1065


unknown's avatar
unknown committed
1066 1067
READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
		     String &field_term, String &line_start, String &line_term,
1068 1069
		     String &enclosed_par, int escape, bool get_it_from_net,
		     bool is_fifo)
unknown's avatar
unknown committed
1070 1071
  :file(file_par),escape_char(escape)
{
unknown's avatar
unknown committed
1072
  read_charset= cs;
unknown's avatar
unknown committed
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
  field_term_ptr=(char*) field_term.ptr();
  field_term_length= field_term.length();
  line_term_ptr=(char*) line_term.ptr();
  line_term_length= line_term.length();
  if (line_start.length() == 0)
  {
    line_start_ptr=0;
    start_of_line= 0;
  }
  else
  {
    line_start_ptr=(char*) line_start.ptr();
    line_start_end=line_start_ptr+line_start.length();
    start_of_line= 1;
  }
  /* If field_terminator == line_terminator, don't use line_terminator */
  if (field_term_length == line_term_length &&
      !memcmp(field_term_ptr,line_term_ptr,field_term_length))
  {
    line_term_length=0;
    line_term_ptr=(char*) "";
  }
  enclosed_char= (enclosed_length=enclosed_par.length()) ?
    (uchar) enclosed_par[0] : INT_MAX;
  field_term_char= field_term_length ? (uchar) field_term_ptr[0] : INT_MAX;
  line_term_char= line_term_length ? (uchar) line_term_ptr[0] : INT_MAX;
  error=eof=found_end_of_line=found_null=line_cuted=0;
  buff_length=tot_length;


  /* Set of a stack for unget if long terminators */
  uint length=max(field_term_length,line_term_length)+1;
  set_if_bigger(length,line_start.length());
  stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);

1108
  if (!(buffer=(uchar*) my_malloc(buff_length+1,MYF(0))))
unknown's avatar
unknown committed
1109 1110 1111 1112 1113
    error=1; /* purecov: inspected */
  else
  {
    end_of_buff=buffer+buff_length;
    if (init_io_cache(&cache,(get_it_from_net) ? -1 : file, 0,
1114 1115
		      (get_it_from_net) ? READ_NET :
		      (is_fifo ? READ_FIFO : READ_CACHE),0L,1,
unknown's avatar
unknown committed
1116 1117
		      MYF(MY_WME)))
    {
1118
      my_free((uchar*) buffer,MYF(0)); /* purecov: inspected */
unknown's avatar
unknown committed
1119 1120
      error=1;
    }
unknown's avatar
unknown committed
1121
    else
1122
    {
1123 1124
      /*
	init_io_cache() will not initialize read_function member
unknown's avatar
unknown committed
1125
	if the cache is READ_NET. So we work around the problem with a
1126
	manual assignment
unknown's avatar
unknown committed
1127
      */
1128 1129 1130
      need_end_io_cache = 1;

#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1131 1132
      if (get_it_from_net)
	cache.read_function = _my_b_net_read;
unknown's avatar
unknown committed
1133

1134
      if (mysql_bin_log.is_open())
1135
	cache.pre_read = cache.pre_close =
unknown's avatar
unknown committed
1136
	  (IO_CACHE_CALLBACK) log_loaded_block;
1137
#endif
1138
    }
unknown's avatar
unknown committed
1139 1140 1141 1142 1143 1144 1145 1146
  }
}


READ_INFO::~READ_INFO()
{
  if (!error)
  {
1147 1148
    if (need_end_io_cache)
      ::end_io_cache(&cache);
1149
    my_free((uchar*) buffer,MYF(0));
unknown's avatar
unknown committed
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181
    error=1;
  }
}


#define GET (stack_pos != stack ? *--stack_pos : my_b_get(&cache))
#define PUSH(A) *(stack_pos++)=(A)


inline int READ_INFO::terminator(char *ptr,uint length)
{
  int chr=0;					// Keep gcc happy
  uint i;
  for (i=1 ; i < length ; i++)
  {
    if ((chr=GET) != *++ptr)
    {
      break;
    }
  }
  if (i == length)
    return 1;
  PUSH(chr);
  while (i-- > 1)
    PUSH((uchar) *--ptr);
  return 0;
}


int READ_INFO::read_field()
{
  int chr,found_enclosed_char;
1182
  uchar *to,*new_buffer;
unknown's avatar
unknown committed
1183 1184 1185 1186 1187

  found_null=0;
  if (found_end_of_line)
    return 1;					// One have to call next_line

unknown's avatar
unknown committed
1188
  /* Skip until we find 'line_start' */
unknown's avatar
unknown committed
1189 1190

  if (start_of_line)
unknown's avatar
unknown committed
1191
  {						// Skip until line_start
unknown's avatar
unknown committed
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
    start_of_line=0;
    if (find_start_of_fields())
      return 1;
  }
  if ((chr=GET) == my_b_EOF)
  {
    found_end_of_line=eof=1;
    return 1;
  }
  to=buffer;
  if (chr == enclosed_char)
  {
    found_enclosed_char=enclosed_char;
1205
    *to++=(uchar) chr;				// If error
unknown's avatar
unknown committed
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
  }
  else
  {
    found_enclosed_char= INT_MAX;
    PUSH(chr);
  }

  for (;;)
  {
    while ( to < end_of_buff)
    {
      chr = GET;
      if (chr == my_b_EOF)
	goto found_eof;
      if (chr == escape_char)
      {
	if ((chr=GET) == my_b_EOF)
	{
1224
	  *to++= (uchar) escape_char;
unknown's avatar
unknown committed
1225 1226
	  goto found_eof;
	}
1227 1228 1229 1230 1231 1232 1233 1234 1235
        /*
          When escape_char == enclosed_char, we treat it like we do for
          handling quotes in SQL parsing -- you can double-up the
          escape_char to include it literally, but it doesn't do escapes
          like \n. This allows: LOAD DATA ... ENCLOSED BY '"' ESCAPED BY '"'
          with data like: "fie""ld1", "field2"
         */
        if (escape_char != enclosed_char || chr == escape_char)
        {
1236
          *to++ = (uchar) unescape((char) chr);
1237 1238
          continue;
        }
1239 1240
        PUSH(chr);
        chr= escape_char;
unknown's avatar
unknown committed
1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
      }
#ifdef ALLOW_LINESEPARATOR_IN_STRINGS
      if (chr == line_term_char)
#else
      if (chr == line_term_char && found_enclosed_char == INT_MAX)
#endif
      {
	if (terminator(line_term_ptr,line_term_length))
	{					// Maybe unexpected linefeed
	  enclosed=0;
	  found_end_of_line=1;
	  row_start=buffer;
	  row_end=  to;
	  return 0;
	}
      }
      if (chr == found_enclosed_char)
      {
	if ((chr=GET) == found_enclosed_char)
	{					// Remove dupplicated
1261
	  *to++ = (uchar) chr;
unknown's avatar
unknown committed
1262 1263 1264 1265
	  continue;
	}
	// End of enclosed field if followed by field_term or line_term
	if (chr == my_b_EOF ||
1266 1267
	    (chr == line_term_char && terminator(line_term_ptr,
						line_term_length)))
unknown's avatar
unknown committed
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
	{					// Maybe unexpected linefeed
	  enclosed=1;
	  found_end_of_line=1;
	  row_start=buffer+1;
	  row_end=  to;
	  return 0;
	}
	if (chr == field_term_char &&
	    terminator(field_term_ptr,field_term_length))
	{
	  enclosed=1;
	  row_start=buffer+1;
	  row_end=  to;
	  return 0;
	}
1283 1284 1285 1286
	/*
	  The string didn't terminate yet.
	  Store back next character for the loop
	*/
unknown's avatar
unknown committed
1287
	PUSH(chr);
1288 1289
	/* copy the found term character to 'to' */
	chr= found_enclosed_char;
unknown's avatar
unknown committed
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
      }
      else if (chr == field_term_char && found_enclosed_char == INT_MAX)
      {
	if (terminator(field_term_ptr,field_term_length))
	{
	  enclosed=0;
	  row_start=buffer;
	  row_end=  to;
	  return 0;
	}
      }
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
#ifdef USE_MB
      if (my_mbcharlen(read_charset, chr) > 1 &&
          to + my_mbcharlen(read_charset, chr) <= end_of_buff)
      {
        uchar* p= (uchar*) to;
        int ml, i;
        *to++ = chr;

        ml= my_mbcharlen(read_charset, chr);

        for (i= 1; i < ml; i++) 
        {
          chr= GET;
          if (chr == my_b_EOF)
          {
            /*
             Need to back up the bytes already ready from illformed
             multi-byte char 
            */
            to-= i;
            goto found_eof;
          }
          *to++ = chr;
        }
        if (my_ismbchar(read_charset,
                        (const char *)p,
                        (const char *)to))
          continue;
        for (i= 0; i < ml; i++)
          PUSH((uchar) *--to);
        chr= GET;
      }
#endif
1334
      *to++ = (uchar) chr;
unknown's avatar
unknown committed
1335 1336 1337 1338
    }
    /*
    ** We come here if buffer is too small. Enlarge it and continue
    */
1339
    if (!(new_buffer=(uchar*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
unknown's avatar
unknown committed
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356
					MYF(MY_WME))))
      return (error=1);
    to=new_buffer + (to-buffer);
    buffer=new_buffer;
    buff_length+=IO_SIZE;
    end_of_buff=buffer+buff_length;
  }

found_eof:
  enclosed=0;
  found_end_of_line=eof=1;
  row_start=buffer;
  row_end=to;
  return 0;
}

/*
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368
  Read a row with fixed length.

  NOTES
    The row may not be fixed size on disk if there are escape
    characters in the file.

  IMPLEMENTATION NOTE
    One can't use fixed length with multi-byte charset **

  RETURN
    0  ok
    1  error
unknown's avatar
unknown committed
1369
*/
unknown's avatar
unknown committed
1370

unknown's avatar
unknown committed
1371 1372 1373
int READ_INFO::read_fixed_length()
{
  int chr;
1374
  uchar *to;
unknown's avatar
unknown committed
1375 1376 1377 1378
  if (found_end_of_line)
    return 1;					// One have to call next_line

  if (start_of_line)
unknown's avatar
unknown committed
1379
  {						// Skip until line_start
unknown's avatar
unknown committed
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393
    start_of_line=0;
    if (find_start_of_fields())
      return 1;
  }

  to=row_start=buffer;
  while (to < end_of_buff)
  {
    if ((chr=GET) == my_b_EOF)
      goto found_eof;
    if (chr == escape_char)
    {
      if ((chr=GET) == my_b_EOF)
      {
1394
	*to++= (uchar) escape_char;
unknown's avatar
unknown committed
1395 1396
	goto found_eof;
      }
1397
      *to++ =(uchar) unescape((char) chr);
unknown's avatar
unknown committed
1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
      continue;
    }
    if (chr == line_term_char)
    {
      if (terminator(line_term_ptr,line_term_length))
      {						// Maybe unexpected linefeed
	found_end_of_line=1;
	row_end=  to;
	return 0;
      }
    }
1409
    *to++ = (uchar) chr;
unknown's avatar
unknown committed
1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437
  }
  row_end=to;					// Found full line
  return 0;

found_eof:
  found_end_of_line=eof=1;
  row_start=buffer;
  row_end=to;
  return to == buffer ? 1 : 0;
}


int READ_INFO::next_line()
{
  line_cuted=0;
  start_of_line= line_start_ptr != 0;
  if (found_end_of_line || eof)
  {
    found_end_of_line=0;
    return eof;
  }
  found_end_of_line=0;
  if (!line_term_length)
    return 0;					// No lines
  for (;;)
  {
    int chr = GET;
#ifdef USE_MB
1438
   if (my_mbcharlen(read_charset, chr) > 1)
unknown's avatar
unknown committed
1439
   {
1440
       for (uint i=1;
unknown's avatar
unknown committed
1441
            chr != my_b_EOF && i<my_mbcharlen(read_charset, chr);
unknown's avatar
unknown committed
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485
            i++)
	   chr = GET;
       if (chr == escape_char)
	   continue;
   }
#endif
   if (chr == my_b_EOF)
   {
      eof=1;
      return 1;
    }
    if (chr == escape_char)
    {
      line_cuted=1;
      if (GET == my_b_EOF)
	return 1;
      continue;
    }
    if (chr == line_term_char && terminator(line_term_ptr,line_term_length))
      return 0;
    line_cuted=1;
  }
}


bool READ_INFO::find_start_of_fields()
{
  int chr;
 try_again:
  do
  {
    if ((chr=GET) == my_b_EOF)
    {
      found_end_of_line=eof=1;
      return 1;
    }
  } while ((char) chr != line_start_ptr[0]);
  for (char *ptr=line_start_ptr+1 ; ptr != line_start_end ; ptr++)
  {
    chr=GET;					// Eof will be checked later
    if ((char) chr != *ptr)
    {						// Can't be line_start
      PUSH(chr);
      while (--ptr != line_start_ptr)
1486
      {						// Restart with next char
unknown's avatar
unknown committed
1487 1488 1489 1490 1491 1492 1493
	PUSH((uchar) *ptr);
      }
      goto try_again;
    }
  }
  return 0;
}