sql_load.cc 32.6 KB
Newer Older
unknown's avatar
unknown committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown committed
3 4 5 6
   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.
unknown's avatar
unknown committed
7

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

unknown's avatar
unknown committed
13 14 15 16 17 18 19 20 21 22
   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>
23
#include "sql_repl.h"
unknown's avatar
unknown committed
24 25 26 27 28 29 30 31 32 33 34 35

class READ_INFO {
  File	file;
  byte	*buffer,			/* Buffer for read text */
	*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;
36
  bool  need_end_io_cache;
unknown's avatar
unknown committed
37 38 39 40 41 42 43
  IO_CACHE cache;
  NET *io_net;

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

unknown's avatar
unknown committed
46
  READ_INFO(File file,uint tot_length,CHARSET_INFO *cs,
unknown's avatar
unknown committed
47
	    String &field_term,String &line_start,String &line_term,
48
	    String &enclosed,int escape,bool get_it_from_net, bool is_fifo);
unknown's avatar
unknown committed
49 50 51 52 53 54 55
  ~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
56 57 58 59
  /*
    We need to force cache close before destructor is invoked to log
    the last read block
  */
60 61 62 63 64 65
  void end_io_cache()
  {
    ::end_io_cache(&cache);
    need_end_io_cache = 0;
  }

unknown's avatar
unknown committed
66 67 68 69 70
  /*
    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
  */
71
  void set_io_cache_arg(void* arg) { cache.arg = arg; }
unknown's avatar
unknown committed
72 73
};

74
static int read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
75 76
                             List<Item> &fields_vars, List<Item> &set_fields,
                             List<Item> &set_values, READ_INFO &read_info,
77 78 79
			     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
80 81
                          List<Item> &fields_vars, List<Item> &set_fields,
                          List<Item> &set_values, READ_INFO &read_info,
82 83
			  String &enclosed, ulong skip_lines,
			  bool ignore_check_option_errors);
unknown's avatar
unknown committed
84

unknown's avatar
unknown committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

/*
  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
*/

unknown's avatar
unknown committed
107
bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
unknown's avatar
unknown committed
108 109 110
	        List<Item> &fields_vars, List<Item> &set_fields,
                List<Item> &set_values,
                enum enum_duplicates handle_duplicates, bool ignore,
111
                bool read_file_from_client)
unknown's avatar
unknown committed
112 113 114 115 116
{
  char name[FN_REFLEN];
  File file;
  TABLE *table;
  int error;
117 118
  String *field_term=ex->field_term,*escaped=ex->escaped;
  String *enclosed=ex->enclosed;
unknown's avatar
unknown committed
119
  Item *unused_conds= 0;
120
  bool is_fifo=0;
121
#ifndef EMBEDDED_LIBRARY
122
  LOAD_FILE_INFO lf_info;
123
#endif
unknown's avatar
unknown committed
124
  char *db = table_list->db;			// This is never null
unknown's avatar
unknown committed
125 126 127 128 129
  /*
    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
130
  char *tdb= thd->db ? thd->db : db;		// Result is never null
131
  ulong skip_lines= ex->skip_lines;
unknown's avatar
unknown committed
132
  bool transactional_table;
unknown's avatar
unknown committed
133 134
  DBUG_ENTER("mysql_load");

unknown's avatar
unknown committed
135 136 137 138
#ifdef EMBEDDED_LIBRARY
  read_file_from_client  = 0; //server is always in the same process 
#endif

unknown's avatar
unknown committed
139 140 141 142
  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
143
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
144
  }
unknown's avatar
unknown committed
145 146
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
147
  if (setup_tables(thd, table_list, &unused_conds,
unknown's avatar
unknown committed
148
		   &thd->lex->select_lex.leaf_tables, FALSE, FALSE))
149
     DBUG_RETURN(-1);
unknown's avatar
unknown committed
150 151 152
  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
153
  {
154
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "LOAD");
unknown's avatar
unknown committed
155
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
156
  }
unknown's avatar
unknown committed
157 158 159 160 161 162 163 164 165 166 167 168 169 170
  /*
    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
    mark this table as as 'const table' (ie, one that has only one row).
  */
  if (unique_table(table_list, table_list->next_global))
  {
    my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
    DBUG_RETURN(TRUE);
  }

unknown's avatar
VIEW  
unknown committed
171
  table= table_list->table;
172 173
  transactional_table= table->file->has_transactions();

unknown's avatar
unknown committed
174
  if (!fields_vars.elements)
unknown's avatar
unknown committed
175 176 177
  {
    Field **field;
    for (field=table->field; *field ; field++)
unknown's avatar
unknown committed
178 179 180 181 182 183 184 185 186
      fields_vars.push_back(new Item_field(*field));
    table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
    /*
      Let us also prepare SET clause, altough it is probably empty
      in this case.
    */
    if (setup_fields(thd, 0, table_list, set_fields, 1, 0, 0) ||
        setup_fields(thd, 0, table_list, set_values, 1, 0, 0))
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
187 188 189
  }
  else
  {						// Part field list
unknown's avatar
VIEW  
unknown committed
190
    /* TODO: use this conds for 'WITH CHECK OPTIONS' */
unknown's avatar
unknown committed
191 192 193
    if (setup_fields(thd, 0, table_list, fields_vars, 1, 0, 0) ||
        setup_fields(thd, 0, table_list, set_fields, 1, 0, 0) ||
        check_that_all_fields_are_given_values(thd, table))
unknown's avatar
unknown committed
194
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
195 196 197 198 199 200 201 202 203 204 205 206 207
    /*
      Check whenever TIMESTAMP field with auto-set feature specified
      explicitly.
    */
    if (table->timestamp_field &&
        table->timestamp_field->query_id == thd->query_id)
      table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
    /*
      Fix the expressions in SET clause. This should be done after
      check_that_all_fields_are_given_values() and setting use_timestamp
      since it may update query_id for some fields.
    */
    if (setup_fields(thd, 0, table_list, set_values, 1, 0, 0))
unknown's avatar
unknown committed
208
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
209 210 211
  }

  uint tot_length=0;
unknown's avatar
unknown committed
212 213 214
  bool use_blobs= 0, use_vars= 0;
  List_iterator_fast<Item> it(fields_vars);
  Item *item;
unknown's avatar
unknown committed
215

unknown's avatar
unknown committed
216
  while ((item= it++))
unknown's avatar
unknown committed
217
  {
unknown's avatar
unknown committed
218
    if (item->type() == Item::FIELD_ITEM)
unknown's avatar
unknown committed
219
    {
unknown's avatar
unknown committed
220 221 222 223 224 225 226 227
      Field *field= ((Item_field*)item)->field;
      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
228 229
    }
    else
unknown's avatar
unknown committed
230
      use_vars= 1;
unknown's avatar
unknown committed
231 232 233 234 235
  }
  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
236
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
237
  }
unknown's avatar
unknown committed
238 239 240 241 242
  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
243 244 245

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

248
#ifndef EMBEDDED_LIBRARY
249
  if (read_file_from_client)
unknown's avatar
unknown committed
250
  {
unknown's avatar
unknown committed
251
    (void)net_request_file(&thd->net,ex->file_name);
unknown's avatar
unknown committed
252 253 254
    file = -1;
  }
  else
255
#endif
unknown's avatar
unknown committed
256 257 258 259
  {
#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
    ex->file_name+=dirname_length(ex->file_name);
#endif
260
    if (!dirname_length(ex->file_name))
unknown's avatar
unknown committed
261
    {
262
      strxnmov(name, FN_REFLEN, mysql_real_data_home, tdb, NullS);
unknown's avatar
unknown committed
263 264
      (void) fn_format(name, ex->file_name, name, "",
		       MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
unknown's avatar
unknown committed
265 266 267
    }
    else
    {
unknown's avatar
unknown committed
268 269
      (void) fn_format(name, ex->file_name, mysql_real_data_home, "",
		       MY_RELATIVE_PATH | MY_UNPACK_FILENAME);
270
#if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__)
unknown's avatar
unknown committed
271
      MY_STAT stat_info;
272
      if (!my_stat(name,&stat_info,MYF(MY_WME)))
unknown's avatar
unknown committed
273
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
274

275 276 277
      // 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
278
#ifndef __EMX__
279
	    (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink
280
#endif
281 282
	    ((stat_info.st_mode & S_IFREG) == S_IFREG ||
	     (stat_info.st_mode & S_IFIFO) == S_IFIFO)))
unknown's avatar
unknown committed
283
      {
284
	my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), name);
unknown's avatar
unknown committed
285
	DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
286
      }
287 288
      if ((stat_info.st_mode & S_IFIFO) == S_IFIFO)
	is_fifo = 1;
unknown's avatar
unknown committed
289 290 291
#endif
    }
    if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0)
unknown's avatar
unknown committed
292
      DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
293 294 295 296
  }

  COPY_INFO info;
  bzero((char*) &info,sizeof(info));
297
  info.ignore= ignore;
unknown's avatar
unknown committed
298 299 300
  info.handle_duplicates=handle_duplicates;
  info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX;

301
  READ_INFO read_info(file,tot_length,thd->variables.collation_database,
unknown's avatar
unknown committed
302
		      *field_term,*ex->line_start, *ex->line_term, *enclosed,
303
		      info.escape_char, read_file_from_client, is_fifo);
unknown's avatar
unknown committed
304 305 306 307
  if (read_info.error)
  {
    if	(file >= 0)
      my_close(file,MYF(0));			// no files in net reading
unknown's avatar
unknown committed
308
    DBUG_RETURN(TRUE);				// Can't allocate buffers
unknown's avatar
unknown committed
309 310
  }

311
#ifndef EMBEDDED_LIBRARY
312
  if (mysql_bin_log.is_open())
313 314 315 316
  {
    lf_info.thd = thd;
    lf_info.wrote_create_file = 0;
    lf_info.last_pos_in_file = HA_POS_ERROR;
unknown's avatar
unknown committed
317
    lf_info.log_delayed= transactional_table;
unknown's avatar
unknown committed
318
    read_info.set_io_cache_arg((void*) &lf_info);
319
  }
320 321
#endif /*!EMBEDDED_LIBRARY*/

322
  thd->count_cuted_fields= CHECK_FIELD_WARN;		/* calc cuted fields */
unknown's avatar
unknown committed
323
  thd->cuted_fields=0L;
324 325
  /* Skip lines if there is a line terminator */
  if (ex->line_term->length())
unknown's avatar
unknown committed
326
  {
327 328
    /* ex->skip_lines needs to be preserved for logging */
    while (skip_lines > 0)
unknown's avatar
unknown committed
329
    {
330
      skip_lines--;
unknown's avatar
unknown committed
331 332 333 334
      if (read_info.next_line())
	break;
    }
  }
335

unknown's avatar
unknown committed
336 337
  if (!(error=test(read_info.error)))
  {
unknown's avatar
unknown committed
338

unknown's avatar
unknown committed
339
    table->next_number_field=table->found_next_number_field;
340
    if (ignore ||
unknown's avatar
unknown committed
341 342
	handle_duplicates == DUP_REPLACE)
      table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
343
    ha_enable_transaction(thd, FALSE); 
344
    table->file->start_bulk_insert((ha_rows) 0);
unknown's avatar
unknown committed
345
    table->copy_blobs=1;
unknown's avatar
unknown committed
346 347

    thd->no_trans_update= 0;
unknown's avatar
unknown committed
348
    thd->abort_on_warning= (!ignore &&
unknown's avatar
unknown committed
349 350 351 352
                            (thd->variables.sql_mode &
                             (MODE_STRICT_TRANS_TABLES |
                              MODE_STRICT_ALL_TABLES)));

unknown's avatar
unknown committed
353
    if (!field_term->length() && !enclosed->length())
unknown's avatar
unknown committed
354 355
      error= read_fixed_length(thd, info, table_list, fields_vars,
                               set_fields, set_values, read_info,
356
			       skip_lines, ignore);
unknown's avatar
unknown committed
357
    else
unknown's avatar
unknown committed
358 359 360
      error= read_sep_field(thd, info, table_list, fields_vars,
                            set_fields, set_values, read_info,
			    *enclosed, skip_lines, ignore);
361
    if (table->file->end_bulk_insert())
362
      error=1;					/* purecov: inspected */
363
    ha_enable_transaction(thd, TRUE);
unknown's avatar
unknown committed
364
    table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
unknown's avatar
unknown committed
365 366
    table->next_number_field=0;
  }
367 368
  if (file >= 0)
    my_close(file,MYF(0));
unknown's avatar
unknown committed
369 370
  free_blobs(table);				/* if pack_blob was used */
  table->copy_blobs=0;
371
  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
372

373 374 375 376
  /*
    We must invalidate the table in query cache before binlog writing and
    ha_autocommit_...
  */
unknown's avatar
unknown committed
377 378
  query_cache_invalidate3(thd, table_list, 0);

unknown's avatar
unknown committed
379
  if (error)
380
  {
381
    if (transactional_table)
382
      ha_autocommit_or_rollback(thd,error);
unknown's avatar
unknown committed
383

384 385 386
    if (read_file_from_client)
      while (!read_info.next_line())
	;
unknown's avatar
unknown committed
387

388
#ifndef EMBEDDED_LIBRARY
389
    if (mysql_bin_log.is_open())
390
    {
unknown's avatar
unknown committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
      /*
        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 */
unknown's avatar
unknown committed
414 415
      if (lf_info.wrote_create_file)
      {
unknown's avatar
unknown committed
416
        Delete_file_log_event d(thd, db, transactional_table);
unknown's avatar
unknown committed
417 418
        mysql_bin_log.write(&d);
      }
419
    }
420
#endif /*!EMBEDDED_LIBRARY*/
421 422
    error= -1;				// Error on read
    goto err;
423
  }
424 425
  sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
	  (ulong) (info.records - info.copied), (ulong) thd->cuted_fields);
426
  send_ok(thd,info.copied+info.deleted,0L,name);
unknown's avatar
unknown committed
427

unknown's avatar
unknown committed
428
  if (!transactional_table)
429
    thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
430
#ifndef EMBEDDED_LIBRARY
431
  if (mysql_bin_log.is_open())
432
  {
433 434
    /*
      As already explained above, we need to call end_io_cache() or the last
unknown's avatar
unknown committed
435 436
      block will be logged only after Execute_load_query_log_event (which is
      wrong), when read_info is destroyed.
437
    */
unknown's avatar
unknown committed
438
    read_info.end_io_cache();
439
    if (lf_info.wrote_create_file)
440
    {
unknown's avatar
unknown committed
441 442 443 444 445 446 447
      Execute_load_query_log_event e(thd, thd->query, thd->query_length,
        (char*)thd->lex->fname_start - (char*)thd->query,
        (char*)thd->lex->fname_end - (char*)thd->query,
        (handle_duplicates == DUP_REPLACE) ?  LOAD_DUP_REPLACE :
                                              (ignore ? LOAD_DUP_IGNORE :
                                                        LOAD_DUP_ERROR),
        transactional_table, FALSE);
448
      mysql_bin_log.write(&e);
449
    }
450
  }
451
#endif /*!EMBEDDED_LIBRARY*/
452
  if (transactional_table)
unknown's avatar
unknown committed
453
    error=ha_autocommit_or_rollback(thd,error);
unknown's avatar
unknown committed
454

455 456 457 458 459 460
err:
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
unknown's avatar
unknown committed
461
  thd->abort_on_warning= 0;
462
  DBUG_RETURN(error);
unknown's avatar
unknown committed
463 464 465 466 467 468 469
}

/****************************************************************************
** Read of rows of fixed size + optional garage + optonal newline
****************************************************************************/

static int
470
read_fixed_length(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
471 472 473
                  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
474
{
unknown's avatar
unknown committed
475
  List_iterator_fast<Item> it(fields_vars);
unknown's avatar
unknown committed
476
  Item_field *sql_field;
477
  TABLE *table= table_list->table;
478
  ulonglong id;
unknown's avatar
unknown committed
479
  bool no_trans_update;
unknown's avatar
unknown committed
480 481
  DBUG_ENTER("read_fixed_length");

482
  id= 0;
unknown's avatar
unknown committed
483
 
unknown's avatar
unknown committed
484 485 486 487
  while (!read_info.read_fixed_length())
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
488
      thd->send_kill_message();
unknown's avatar
unknown committed
489 490
      DBUG_RETURN(1);
    }
491 492 493 494 495 496 497 498 499 500 501
    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
502 503 504 505 506
    it.rewind();
    byte *pos=read_info.row_start;
#ifdef HAVE_purify
    read_info.row_end[0]=0;
#endif
unknown's avatar
unknown committed
507
    no_trans_update= !table->file->has_transactions();
unknown's avatar
unknown committed
508 509 510 511 512 513

    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
514 515
    while ((sql_field= (Item_field*) it++))
    {
516
      Field *field= sql_field->field;                  
unknown's avatar
unknown committed
517 518 519 520 521 522 523
      /*
        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
524 525
      if (pos == read_info.row_end)
      {
526 527 528
        thd->cuted_fields++;			/* Not enough fields */
        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                            ER_WARN_TOO_FEW_RECORDS, 
529
                            ER(ER_WARN_TOO_FEW_RECORDS), thd->row_count);
unknown's avatar
unknown committed
530 531 532 533 534 535 536 537 538
      }
      else
      {
	uint length;
	byte save_chr;
	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
539
        field->store((char*) pos,length,read_info.read_charset);
unknown's avatar
unknown committed
540 541 542 543 544 545
	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)
546
    {
unknown's avatar
unknown committed
547
      thd->cuted_fields++;			/* To long row */
548 549
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_TOO_MANY_RECORDS, 
550
                          ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count); 
551
    }
552

unknown's avatar
unknown committed
553 554 555
    if (fill_record(thd, set_fields, set_values, ignore_check_option_errors))
      DBUG_RETURN(1);

556 557
    switch (table_list->view_check_option(thd,
                                          ignore_check_option_errors)) {
558 559 560 561 562 563 564
    case VIEW_CHECK_SKIP:
      read_info.next_line();
      goto continue_loop;
    case VIEW_CHECK_ERROR:
      DBUG_RETURN(-1);
    }

unknown's avatar
unknown committed
565
    if (thd->killed || write_record(thd,table,&info))
unknown's avatar
unknown committed
566
      DBUG_RETURN(1);
unknown's avatar
unknown committed
567 568
    thd->no_trans_update= no_trans_update;
   
569 570 571 572 573 574 575 576
    /*
      If auto_increment values are used, save the first one
       for LAST_INSERT_ID() and for the binary/update log.
       We can't use insert_id() as we don't want to touch the
       last_insert_id_used flag.
    */
    if (!id && thd->insert_id_used)
      id= thd->last_insert_id;
unknown's avatar
unknown committed
577 578
    if (table->next_number_field)
      table->next_number_field->reset();	// Clear for next record
unknown's avatar
unknown committed
579
    if (read_info.next_line())			// Skip to next line
unknown's avatar
unknown committed
580 581
      break;
    if (read_info.line_cuted)
582
    {
unknown's avatar
unknown committed
583
      thd->cuted_fields++;			/* To long row */
584 585
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
                          ER_WARN_TOO_MANY_RECORDS, 
586
                          ER(ER_WARN_TOO_MANY_RECORDS), thd->row_count); 
587
    }
588
    thd->row_count++;
589
continue_loop:;
unknown's avatar
unknown committed
590
  }
591 592
  if (id && !read_info.error)
    thd->insert_id(id);			// For binary/update log
unknown's avatar
unknown committed
593 594 595 596 597 598
  DBUG_RETURN(test(read_info.error));
}



static int
599
read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list,
unknown's avatar
unknown committed
600 601
               List<Item> &fields_vars, List<Item> &set_fields,
               List<Item> &set_values, READ_INFO &read_info,
602 603
	       String &enclosed, ulong skip_lines,
	       bool ignore_check_option_errors)
unknown's avatar
unknown committed
604
{
unknown's avatar
unknown committed
605 606
  List_iterator_fast<Item> it(fields_vars);
  Item *item;
607
  TABLE *table= table_list->table;
unknown's avatar
unknown committed
608
  uint enclosed_length;
609
  ulonglong id;
unknown's avatar
unknown committed
610
  bool no_trans_update;
unknown's avatar
unknown committed
611 612 613
  DBUG_ENTER("read_sep_field");

  enclosed_length=enclosed.length();
614
  id= 0;
unknown's avatar
unknown committed
615
  no_trans_update= !table->file->has_transactions();
616

unknown's avatar
unknown committed
617 618 619 620
  for (;;it.rewind())
  {
    if (thd->killed)
    {
unknown's avatar
SCRUM  
unknown committed
621
      thd->send_kill_message();
unknown's avatar
unknown committed
622 623
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
624 625 626 627

    restore_record(table, s->default_values);

    while ((item= it++))
unknown's avatar
unknown committed
628 629 630 631 632 633
    {
      uint length;
      byte *pos;

      if (read_info.read_field())
	break;
unknown's avatar
unknown committed
634 635 636 637 638

      /* 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
639 640 641 642 643 644 645
      pos=read_info.row_start;
      length=(uint) (read_info.row_end-pos);

      if (!read_info.enclosed &&
	  (enclosed_length && length == 4 && !memcmp(pos,"NULL",4)) ||
	  (length == 1 && read_info.found_null))
      {
unknown's avatar
unknown committed
646 647 648 649 650 651 652 653 654 655 656 657 658
        if (item->type() == Item::FIELD_ITEM)
        {
          Field *field= ((Item_field *)item)->field;
          field->reset();
          field->set_null();
          if (!field->maybe_null())
          {
            if (field->type() == FIELD_TYPE_TIMESTAMP)
              ((Field_timestamp*) field)->set_time();
            else if (field != table->next_number_field)
              field->set_warning((uint) MYSQL_ERROR::WARN_LEVEL_WARN,
                                 ER_WARN_NULL_TO_NOTNULL, 1);
          }
unknown's avatar
unknown committed
659
	}
unknown's avatar
unknown committed
660 661 662
        else
          ((Item_user_var_as_out_param *)item)->set_null_value(
                                                  read_info.read_charset);
unknown's avatar
unknown committed
663 664
	continue;
      }
unknown's avatar
unknown committed
665 666 667 668 669 670 671 672 673 674 675

      if (item->type() == Item::FIELD_ITEM)
      {
        Field *field= ((Item_field *)item)->field;
        field->set_notnull();
        read_info.row_end[0]=0;			// Safe to change end marker
        field->store((char*) pos, length, read_info.read_charset);
      }
      else
        ((Item_user_var_as_out_param *)item)->set_value((char*) pos, length,
                                                read_info.read_charset);
unknown's avatar
unknown committed
676 677 678
    }
    if (read_info.error)
      break;
679 680
    if (skip_lines)
    {
unknown's avatar
unknown committed
681
      skip_lines--;
682 683
      continue;
    }
unknown's avatar
unknown committed
684 685 686 687
    if (item)
    {
      /* Have not read any field, thus input file is simply ended */
      if (item == fields_vars.head())
unknown's avatar
unknown committed
688
	break;
unknown's avatar
unknown committed
689
      for (; item ; item= it++)
unknown's avatar
unknown committed
690
      {
unknown's avatar
unknown committed
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706
        if (item->type() == Item::FIELD_ITEM)
        {
          /*
            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);
        }
        else
          ((Item_user_var_as_out_param *)item)->set_null_value(
                                                  read_info.read_charset);
unknown's avatar
unknown committed
707 708
      }
    }
709

unknown's avatar
unknown committed
710 711 712
    if (fill_record(thd, set_fields, set_values, ignore_check_option_errors))
      DBUG_RETURN(1);

713 714
    switch (table_list->view_check_option(thd,
                                          ignore_check_option_errors)) {
715 716 717 718 719 720 721 722
    case VIEW_CHECK_SKIP:
      read_info.next_line();
      goto continue_loop;
    case VIEW_CHECK_ERROR:
      DBUG_RETURN(-1);
    }


unknown's avatar
unknown committed
723
    if (thd->killed || write_record(thd, table, &info))
unknown's avatar
unknown committed
724
      DBUG_RETURN(1);
725 726 727 728 729 730 731 732
    /*
      If auto_increment values are used, save the first one
       for LAST_INSERT_ID() and for the binary/update log.
       We can't use insert_id() as we don't want to touch the
       last_insert_id_used flag.
    */
    if (!id && thd->insert_id_used)
      id= thd->last_insert_id;
unknown's avatar
unknown committed
733 734
    if (table->next_number_field)
      table->next_number_field->reset();	// Clear for next record
unknown's avatar
unknown committed
735
    thd->no_trans_update= no_trans_update;
unknown's avatar
unknown committed
736
    if (read_info.next_line())			// Skip to next line
unknown's avatar
unknown committed
737 738
      break;
    if (read_info.line_cuted)
739
    {
unknown's avatar
unknown committed
740
      thd->cuted_fields++;			/* To long row */
741
      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
742 743
                          ER_WARN_TOO_MANY_RECORDS, ER(ER_WARN_TOO_MANY_RECORDS), 
                          thd->row_count);   
unknown's avatar
unknown committed
744 745
      if (thd->killed)
        DBUG_RETURN(1);
746
    }
747
    thd->row_count++;
748
continue_loop:;
unknown's avatar
unknown committed
749
  }
750 751
  if (id && !read_info.error)
    thd->insert_id(id);			// For binary/update log
unknown's avatar
unknown committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
  DBUG_RETURN(test(read_info.error));
}


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

char
READ_INFO::unescape(char chr)
{
  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;
  }
}


776 777 778 779
/*
  Read a line using buffering
  If last line is empty (in line mode) then it isn't outputed
*/
unknown's avatar
unknown committed
780 781


unknown's avatar
unknown committed
782 783
READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs,
		     String &field_term, String &line_start, String &line_term,
784 785
		     String &enclosed_par, int escape, bool get_it_from_net,
		     bool is_fifo)
unknown's avatar
unknown committed
786 787
  :file(file_par),escape_char(escape)
{
unknown's avatar
unknown committed
788
  read_charset= cs;
unknown's avatar
unknown committed
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
  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);

  if (!(buffer=(byte*) my_malloc(buff_length+1,MYF(0))))
    error=1; /* purecov: inspected */
  else
  {
    end_of_buff=buffer+buff_length;
    if (init_io_cache(&cache,(get_it_from_net) ? -1 : file, 0,
830 831
		      (get_it_from_net) ? READ_NET :
		      (is_fifo ? READ_FIFO : READ_CACHE),0L,1,
unknown's avatar
unknown committed
832 833 834 835 836
		      MYF(MY_WME)))
    {
      my_free((gptr) buffer,MYF(0)); /* purecov: inspected */
      error=1;
    }
unknown's avatar
unknown committed
837
    else
838
    {
839 840
      /*
	init_io_cache() will not initialize read_function member
unknown's avatar
unknown committed
841
	if the cache is READ_NET. So we work around the problem with a
842
	manual assignment
unknown's avatar
unknown committed
843
      */
844 845 846
      need_end_io_cache = 1;

#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
847 848
      if (get_it_from_net)
	cache.read_function = _my_b_net_read;
unknown's avatar
unknown committed
849

850
      if (mysql_bin_log.is_open())
851
	cache.pre_read = cache.pre_close =
unknown's avatar
unknown committed
852
	  (IO_CACHE_CALLBACK) log_loaded_block;
853
#endif
854
    }
unknown's avatar
unknown committed
855 856 857 858 859 860 861 862
  }
}


READ_INFO::~READ_INFO()
{
  if (!error)
  {
863 864
    if (need_end_io_cache)
      ::end_io_cache(&cache);
unknown's avatar
unknown committed
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
    my_free((gptr) buffer,MYF(0));
    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;
  byte *to,*new_buffer;

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

unknown's avatar
unknown committed
904
  /* Skip until we find 'line_start' */
unknown's avatar
unknown committed
905 906

  if (start_of_line)
unknown's avatar
unknown committed
907
  {						// Skip until line_start
unknown's avatar
unknown committed
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
    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;
    *to++=(byte) chr;				// If error
  }
  else
  {
    found_enclosed_char= INT_MAX;
    PUSH(chr);
  }

  for (;;)
  {
    while ( to < end_of_buff)
    {
      chr = GET;
#ifdef USE_MB
935
      if ((my_mbcharlen(read_charset, chr) > 1) &&
unknown's avatar
unknown committed
936
          to+my_mbcharlen(read_charset, chr) <= end_of_buff)
unknown's avatar
unknown committed
937 938 939
      {
	  uchar* p = (uchar*)to;
	  *to++ = chr;
unknown's avatar
unknown committed
940
	  int ml = my_mbcharlen(read_charset, chr);
unknown's avatar
unknown committed
941 942 943 944 945 946 947
	  int i;
	  for (i=1; i<ml; i++) {
	      chr = GET;
	      if (chr == my_b_EOF)
		  goto found_eof;
	      *to++ = chr;
	  }
unknown's avatar
unknown committed
948
	  if (my_ismbchar(read_charset,
unknown's avatar
unknown committed
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
                          (const char *)p,
                          (const char *)to))
	    continue;
	  for (i=0; i<ml; i++)
	    PUSH((uchar) *--to);
	  chr = GET;
      }
#endif
      if (chr == my_b_EOF)
	goto found_eof;
      if (chr == escape_char)
      {
	if ((chr=GET) == my_b_EOF)
	{
	  *to++= (byte) escape_char;
	  goto found_eof;
	}
	*to++ = (byte) unescape((char) chr);
	continue;
      }
#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
	  *to++ = (byte) chr;
	  continue;
	}
	// End of enclosed field if followed by field_term or line_term
	if (chr == my_b_EOF ||
	    chr == line_term_char && terminator(line_term_ptr,
						line_term_length))
	{					// 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;
	}
1010 1011 1012 1013
	/*
	  The string didn't terminate yet.
	  Store back next character for the loop
	*/
unknown's avatar
unknown committed
1014
	PUSH(chr);
1015 1016
	/* copy the found term character to 'to' */
	chr= found_enclosed_char;
unknown's avatar
unknown committed
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
      }
      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;
	}
      }
      *to++ = (byte) chr;
    }
    /*
    ** We come here if buffer is too small. Enlarge it and continue
    */
    if (!(new_buffer=(byte*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
					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;
}

/*
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
  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
1063
*/
unknown's avatar
unknown committed
1064

unknown's avatar
unknown committed
1065 1066 1067 1068 1069 1070 1071 1072
int READ_INFO::read_fixed_length()
{
  int chr;
  byte *to;
  if (found_end_of_line)
    return 1;					// One have to call next_line

  if (start_of_line)
unknown's avatar
unknown committed
1073
  {						// Skip until line_start
unknown's avatar
unknown committed
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 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
    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)
      {
	*to++= (byte) escape_char;
	goto found_eof;
      }
      *to++ =(byte) unescape((char) chr);
      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;
      }
    }
    *to++ = (byte) chr;
  }
  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
1132
   if (my_mbcharlen(read_charset, chr) > 1)
unknown's avatar
unknown committed
1133 1134
   {
       for (int i=1;
unknown's avatar
unknown committed
1135
            chr != my_b_EOF && i<my_mbcharlen(read_charset, chr);
unknown's avatar
unknown committed
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 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
            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)
1180
      {						// Restart with next char
unknown's avatar
unknown committed
1181 1182 1183 1184 1185 1186 1187
	PUSH((uchar) *ptr);
      }
      goto try_again;
    }
  }
  return 0;
}