~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/* Copyright (C) 2003 MySQL AB
2
3
  This program is free software; you can redistribute it and/or modify
4
  it under the terms of the GNU General Public License as published by
5
  the Free Software Foundation; version 2 of the License.
6
7
  This program is distributed in the hope that it will be useful,
8
  but WITHOUT ANY WARRANTY; without even the implied warranty of
9
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
  GNU General Public License for more details.
11
12
  You should have received a copy of the GNU General Public License
13
  along with this program; if not, write to the Free Software
14
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
15
16
/*
17
  Make sure to look at ha_tina.h for more details.
18
19
  First off, this is a play thing for me, there are a number of things
20
  wrong with it:
21
    *) It was designed for csv and therefore its performance is highly
22
       questionable.
23
    *) Indexes have not been implemented. This is because the files can
24
       be traded in and out of the table directory without having to worry
25
       about rebuilding anything.
26
    *) NULLs and "" are treated equally (like a spreadsheet).
27
    *) There was in the beginning no point to anyone seeing this other
28
       then me, so there is a good chance that I haven't quite documented
29
       it well.
30
    *) Less design, more "make it work"
31
32
  Now there are a few cool things with it:
33
    *) Errors can result in corrupted data files.
34
    *) Data files can be read by spreadsheets directly.
35
36
TODO:
37
 *) Move to a block system for larger files
38
 *) Error recovery, its all there, just need to finish it
39
 *) Document how the chains work.
40
41
 -Brian
42
*/
243.1.14 by Jay Pipes
* Ensured all drizzled/field/x.cc files to include mysql_priv.h
43
#include <drizzled/common_includes.h>
1 by brian
clean slate
44
#include "ha_tina.h"
45
46
47
/*
481 by Brian Aker
Remove all of uchar.
48
  unsigned char + unsigned char + uint64_t + uint64_t + uint64_t + uint64_t + unsigned char
1 by brian
clean slate
49
*/
481 by Brian Aker
Remove all of uchar.
50
#define META_BUFFER_SIZE sizeof(unsigned char) + sizeof(unsigned char) + sizeof(uint64_t) \
51
  + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(unsigned char)
1 by brian
clean slate
52
#define TINA_CHECK_HEADER 254 // The number we use to determine corruption
53
#define BLOB_MEMROOT_ALLOC_SIZE 8192
54
55
/* The file extension */
56
#define CSV_EXT ".CSV"               // The data file
57
#define CSN_EXT ".CSN"               // Files used during repair and update
58
#define CSM_EXT ".CSM"               // Meta file
59
60
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
61
static TINA_SHARE *get_share(const char *table_name, Table *table);
1 by brian
clean slate
62
static int free_share(TINA_SHARE *share);
63
static int read_meta_file(File meta_file, ha_rows *rows);
64
static int write_meta_file(File meta_file, ha_rows rows, bool dirty);
65
66
extern "C" void tina_get_status(void* param, int concurrent_insert);
67
extern "C" void tina_update_status(void* param);
146 by Brian Aker
my_bool cleanup.
68
extern "C" bool tina_check_status(void* param);
1 by brian
clean slate
69
70
/* Stuff for shares */
71
pthread_mutex_t tina_mutex;
72
static HASH tina_open_tables;
73
static handler *tina_create_handler(handlerton *hton,
74
                                    TABLE_SHARE *table, 
75
                                    MEM_ROOT *mem_root);
76
77
78
/*****************************************************************************
79
 ** TINA tables
80
 *****************************************************************************/
81
82
/*
83
  Used for sorting chains with qsort().
84
*/
85
int sort_set (tina_set *a, tina_set *b)
86
{
87
  /*
88
    We assume that intervals do not intersect. So, it is enought to compare
89
    any two points. Here we take start of intervals for comparison.
90
  */
91
  return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) );
92
}
93
481 by Brian Aker
Remove all of uchar.
94
static unsigned char* tina_get_key(TINA_SHARE *share, size_t *length,
146 by Brian Aker
my_bool cleanup.
95
                          bool not_used __attribute__((unused)))
1 by brian
clean slate
96
{
97
  *length=share->table_name_length;
481 by Brian Aker
Remove all of uchar.
98
  return (unsigned char*) share->table_name;
1 by brian
clean slate
99
}
100
101
static int tina_init_func(void *p)
102
{
103
  handlerton *tina_hton;
104
105
  tina_hton= (handlerton *)p;
398.1.10 by Monty Taylor
Actually removed VOID() this time.
106
  pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST);
1 by brian
clean slate
107
  (void) hash_init(&tina_open_tables,system_charset_info,32,0,0,
108
                   (hash_get_key) tina_get_key,0,0);
109
  tina_hton->state= SHOW_OPTION_YES;
110
  tina_hton->create= tina_create_handler;
111
  tina_hton->flags= (HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES | 
112
                     HTON_NO_PARTITION);
113
  return 0;
114
}
115
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
116
static int tina_done_func(void *p __attribute__((unused)))
1 by brian
clean slate
117
{
118
  hash_free(&tina_open_tables);
119
  pthread_mutex_destroy(&tina_mutex);
120
121
  return 0;
122
}
123
124
125
/*
126
  Simple lock controls.
127
*/
77.1.6 by Monty Taylor
CSV is clean.
128
static TINA_SHARE *get_share(const char *table_name,
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
129
                             Table *table __attribute__((unused)))
1 by brian
clean slate
130
{
131
  TINA_SHARE *share;
132
  char meta_file_name[FN_REFLEN];
15 by brian
Fix for stat, NETWARE removal
133
  struct stat file_stat;
1 by brian
clean slate
134
  char *tmp_name;
482 by Brian Aker
Remove uint.
135
  uint32_t length;
1 by brian
clean slate
136
137
  pthread_mutex_lock(&tina_mutex);
138
  length=(uint) strlen(table_name);
139
140
  /*
141
    If share is not present in the hash, create a new share and
142
    initialize its members.
143
  */
144
  if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
481 by Brian Aker
Remove all of uchar.
145
                                        (unsigned char*) table_name,
1 by brian
clean slate
146
                                       length)))
147
  {
148
    if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
149
                         &share, sizeof(*share),
150
                         &tmp_name, length+1,
461 by Monty Taylor
Removed NullS. bu-bye.
151
                         NULL))
1 by brian
clean slate
152
    {
153
      pthread_mutex_unlock(&tina_mutex);
154
      return NULL;
155
    }
156
157
    share->use_count= 0;
158
    share->table_name_length= length;
159
    share->table_name= tmp_name;
163 by Brian Aker
Merge Monty's code.
160
    share->crashed= false;
1 by brian
clean slate
161
    share->rows_recorded= 0;
163 by Brian Aker
Merge Monty's code.
162
    share->update_file_opened= false;
163
    share->tina_write_opened= false;
1 by brian
clean slate
164
    share->data_file_version= 0;
411.1.1 by Brian Aker
Work on removing GNU specific calls.
165
    my_stpcpy(share->table_name, table_name);
1 by brian
clean slate
166
    fn_format(share->data_file_name, table_name, "", CSV_EXT,
167
              MY_REPLACE_EXT|MY_UNPACK_FILENAME);
168
    fn_format(meta_file_name, table_name, "", CSM_EXT,
169
              MY_REPLACE_EXT|MY_UNPACK_FILENAME);
170
15 by brian
Fix for stat, NETWARE removal
171
    if (stat(share->data_file_name, &file_stat))
1 by brian
clean slate
172
      goto error;
173
    share->saved_data_file_length= file_stat.st_size;
174
481 by Brian Aker
Remove all of uchar.
175
    if (my_hash_insert(&tina_open_tables, (unsigned char*) share))
1 by brian
clean slate
176
      goto error;
177
    thr_lock_init(&share->lock);
178
    pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
179
180
    /*
181
      Open or create the meta file. In the latter case, we'll get
182
      an error during read_meta_file and mark the table as crashed.
183
      Usually this will result in auto-repair, and we will get a good
184
      meta-file in the end.
185
    */
186
    if ((share->meta_file= my_open(meta_file_name,
187
                                   O_RDWR|O_CREAT, MYF(0))) == -1)
163 by Brian Aker
Merge Monty's code.
188
      share->crashed= true;
1 by brian
clean slate
189
190
    /*
191
      If the meta file will not open we assume it is crashed and
192
      mark it as such.
193
    */
194
    if (read_meta_file(share->meta_file, &share->rows_recorded))
163 by Brian Aker
Merge Monty's code.
195
      share->crashed= true;
1 by brian
clean slate
196
  }
197
  share->use_count++;
198
  pthread_mutex_unlock(&tina_mutex);
199
200
  return share;
201
202
error:
203
  pthread_mutex_unlock(&tina_mutex);
481 by Brian Aker
Remove all of uchar.
204
  free((unsigned char*) share);
1 by brian
clean slate
205
206
  return NULL;
207
}
208
209
210
/*
211
  Read CSV meta-file
212
213
  SYNOPSIS
214
    read_meta_file()
215
    meta_file   The meta-file filedes
216
    ha_rows     Pointer to the var we use to store rows count.
217
                These are read from the meta-file.
218
219
  DESCRIPTION
220
221
    Read the meta-file info. For now we are only interested in
222
    rows counf, crashed bit and magic number.
223
224
  RETURN
225
    0 - OK
226
    non-zero - error occurred
227
*/
228
229
static int read_meta_file(File meta_file, ha_rows *rows)
230
{
481 by Brian Aker
Remove all of uchar.
231
  unsigned char meta_buffer[META_BUFFER_SIZE];
232
  unsigned char *ptr= meta_buffer;
1 by brian
clean slate
233
398.1.10 by Monty Taylor
Actually removed VOID() this time.
234
  my_seek(meta_file, 0, MY_SEEK_SET, MYF(0));
481 by Brian Aker
Remove all of uchar.
235
  if (my_read(meta_file, (unsigned char*)meta_buffer, META_BUFFER_SIZE, 0)
1 by brian
clean slate
236
      != META_BUFFER_SIZE)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
237
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
238
239
  /*
240
    Parse out the meta data, we ignore version at the moment
241
  */
242
481 by Brian Aker
Remove all of uchar.
243
  ptr+= sizeof(unsigned char)*2; // Move past header
1 by brian
clean slate
244
  *rows= (ha_rows)uint8korr(ptr);
245
  ptr+= sizeof(uint64_t); // Move past rows
246
  /*
247
    Move past check_point, auto_increment and forced_flushes fields.
248
    They are present in the format, but we do not use them yet.
249
  */
250
  ptr+= 3*sizeof(uint64_t);
251
252
  /* check crashed bit and magic number */
481 by Brian Aker
Remove all of uchar.
253
  if ((meta_buffer[0] != (unsigned char)TINA_CHECK_HEADER) ||
163 by Brian Aker
Merge Monty's code.
254
      ((bool)(*ptr)== true))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
255
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
256
257
  my_sync(meta_file, MYF(MY_WME));
258
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
259
  return(0);
1 by brian
clean slate
260
}
261
262
263
/*
264
  Write CSV meta-file
265
266
  SYNOPSIS
267
    write_meta_file()
268
    meta_file   The meta-file filedes
269
    ha_rows     The number of rows we have in the datafile.
270
    dirty       A flag, which marks whether we have a corrupt table
271
272
  DESCRIPTION
273
274
    Write meta-info the the file. Only rows count, crashed bit and
275
    magic number matter now.
276
277
  RETURN
278
    0 - OK
279
    non-zero - error occurred
280
*/
281
282
static int write_meta_file(File meta_file, ha_rows rows, bool dirty)
283
{
481 by Brian Aker
Remove all of uchar.
284
  unsigned char meta_buffer[META_BUFFER_SIZE];
285
  unsigned char *ptr= meta_buffer;
1 by brian
clean slate
286
481 by Brian Aker
Remove all of uchar.
287
  *ptr= (unsigned char)TINA_CHECK_HEADER;
288
  ptr+= sizeof(unsigned char);
289
  *ptr= (unsigned char)TINA_VERSION;
290
  ptr+= sizeof(unsigned char);
1 by brian
clean slate
291
  int8store(ptr, (uint64_t)rows);
292
  ptr+= sizeof(uint64_t);
293
  memset(ptr, 0, 3*sizeof(uint64_t));
294
  /*
295
     Skip over checkpoint, autoincrement and forced_flushes fields.
296
     We'll need them later.
297
  */
298
  ptr+= 3*sizeof(uint64_t);
481 by Brian Aker
Remove all of uchar.
299
  *ptr= (unsigned char)dirty;
1 by brian
clean slate
300
398.1.10 by Monty Taylor
Actually removed VOID() this time.
301
  my_seek(meta_file, 0, MY_SEEK_SET, MYF(0));
481 by Brian Aker
Remove all of uchar.
302
  if (my_write(meta_file, (unsigned char *)meta_buffer, META_BUFFER_SIZE, 0)
1 by brian
clean slate
303
      != META_BUFFER_SIZE)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
304
    return(-1);
1 by brian
clean slate
305
306
  my_sync(meta_file, MYF(MY_WME));
307
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
308
  return(0);
1 by brian
clean slate
309
}
310
311
bool ha_tina::check_and_repair(THD *thd)
312
{
313
  HA_CHECK_OPT check_opt;
314
315
  check_opt.init();
316
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
317
  return(repair(thd, &check_opt));
1 by brian
clean slate
318
}
319
320
321
int ha_tina::init_tina_writer()
322
{
323
  /*
324
    Mark the file as crashed. We will set the flag back when we close
325
    the file. In the case of the crash it will remain marked crashed,
326
    which enforce recovery.
327
  */
163 by Brian Aker
Merge Monty's code.
328
  (void)write_meta_file(share->meta_file, share->rows_recorded, true);
1 by brian
clean slate
329
330
  if ((share->tina_write_filedes=
331
        my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(0))) == -1)
332
  {
163 by Brian Aker
Merge Monty's code.
333
    share->crashed= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
334
    return(1);
1 by brian
clean slate
335
  }
163 by Brian Aker
Merge Monty's code.
336
  share->tina_write_opened= true;
1 by brian
clean slate
337
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
338
  return(0);
1 by brian
clean slate
339
}
340
341
342
bool ha_tina::is_crashed() const
343
{
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
344
  return(share->crashed);
1 by brian
clean slate
345
}
346
347
/*
348
  Free lock controls.
349
*/
350
static int free_share(TINA_SHARE *share)
351
{
352
  pthread_mutex_lock(&tina_mutex);
353
  int result_code= 0;
354
  if (!--share->use_count){
355
    /* Write the meta file. Mark it as crashed if needed. */
356
    (void)write_meta_file(share->meta_file, share->rows_recorded,
163 by Brian Aker
Merge Monty's code.
357
                          share->crashed ? true :false);
1 by brian
clean slate
358
    if (my_close(share->meta_file, MYF(0)))
359
      result_code= 1;
360
    if (share->tina_write_opened)
361
    {
362
      if (my_close(share->tina_write_filedes, MYF(0)))
363
        result_code= 1;
163 by Brian Aker
Merge Monty's code.
364
      share->tina_write_opened= false;
1 by brian
clean slate
365
    }
366
481 by Brian Aker
Remove all of uchar.
367
    hash_delete(&tina_open_tables, (unsigned char*) share);
1 by brian
clean slate
368
    thr_lock_delete(&share->lock);
369
    pthread_mutex_destroy(&share->mutex);
481 by Brian Aker
Remove all of uchar.
370
    free((unsigned char*) share);
1 by brian
clean slate
371
  }
372
  pthread_mutex_unlock(&tina_mutex);
373
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
374
  return(result_code);
1 by brian
clean slate
375
}
376
377
378
/*
379
  This function finds the end of a line and returns the length
380
  of the line ending.
381
382
  We support three kinds of line endings:
383
  '\r'     --  Old Mac OS line ending
384
  '\n'     --  Traditional Unix and Mac OS X line ending
385
  '\r''\n' --  DOS\Windows line ending
386
*/
387
388
off_t find_eoln_buff(Transparent_file *data_buff, off_t begin,
389
                     off_t end, int *eoln_len)
390
{
391
  *eoln_len= 0;
392
393
  for (off_t x= begin; x < end; x++)
394
  {
395
    /* Unix (includes Mac OS X) */
396
    if (data_buff->get_value(x) == '\n')
397
      *eoln_len= 1;
398
    else
399
      if (data_buff->get_value(x) == '\r') // Mac or Dos
400
      {
401
        /* old Mac line ending */
402
        if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
403
          *eoln_len= 1;
404
        else // DOS style ending
405
          *eoln_len= 2;
406
      }
407
408
    if (*eoln_len)  // end of line was found
409
      return x;
410
  }
411
412
  return 0;
413
}
414
415
416
static handler *tina_create_handler(handlerton *hton,
417
                                    TABLE_SHARE *table, 
418
                                    MEM_ROOT *mem_root)
419
{
420
  return new (mem_root) ha_tina(hton, table);
421
}
422
423
424
ha_tina::ha_tina(handlerton *hton, TABLE_SHARE *table_arg)
425
  :handler(hton, table_arg),
426
  /*
427
    These definitions are found in handler.h
428
    They are not probably completely right.
429
  */
430
  current_position(0), next_position(0), local_saved_data_file_length(0),
431
  file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH),
432
  local_data_file_version(0), records_is_known(0)
433
{
434
  /* Set our original buffers from pre-allocated memory */
435
  buffer.set((char*)byte_buffer, IO_SIZE, &my_charset_bin);
436
  chain= chain_buffer;
437
  file_buff= new Transparent_file();
438
}
439
440
441
/*
442
  Encode a buffer into the quoted format.
443
*/
444
481 by Brian Aker
Remove all of uchar.
445
int ha_tina::encode_quote(unsigned char *buf __attribute__((unused)))
1 by brian
clean slate
446
{
447
  char attribute_buffer[1024];
448
  String attribute(attribute_buffer, sizeof(attribute_buffer),
449
                   &my_charset_bin);
450
451
  buffer.length(0);
452
453
  for (Field **field=table->field ; *field ; field++)
454
  {
455
    const char *ptr;
456
    const char *end_ptr;
457
    const bool was_null= (*field)->is_null();
458
    
459
    /*
460
      assistance for backwards compatibility in production builds.
461
      note: this will not work for ENUM columns.
462
    */
463
    if (was_null)
464
    {
465
      (*field)->set_default();
466
      (*field)->set_notnull();
467
    }
468
469
    (*field)->val_str(&attribute,&attribute);
470
    
471
    if (was_null)
472
      (*field)->set_null();
473
474
    if ((*field)->str_needs_quotes())
475
    {
476
      ptr= attribute.ptr();
477
      end_ptr= attribute.length() + ptr;
478
479
      buffer.append('"');
480
481
      while (ptr < end_ptr) 
482
      {
483
        if (*ptr == '"')
484
        {
485
          buffer.append('\\');
486
          buffer.append('"');
487
          *ptr++;
488
        }
489
        else if (*ptr == '\r')
490
        {
491
          buffer.append('\\');
492
          buffer.append('r');
493
          *ptr++;
494
        }
495
        else if (*ptr == '\\')
496
        {
497
          buffer.append('\\');
498
          buffer.append('\\');
499
          *ptr++;
500
        }
501
        else if (*ptr == '\n')
502
        {
503
          buffer.append('\\');
504
          buffer.append('n');
505
          *ptr++;
506
        }
507
        else
508
          buffer.append(*ptr++);
509
      }
510
      buffer.append('"');
511
    }
512
    else
513
    {
514
      buffer.append(attribute);
515
    }
516
517
    buffer.append(',');
518
  }
519
  // Remove the comma, add a line feed
520
  buffer.length(buffer.length() - 1);
521
  buffer.append('\n');
522
523
  //buffer.replace(buffer.length(), 0, "\n", 1);
524
525
  return (buffer.length());
526
}
527
528
/*
529
  chain_append() adds delete positions to the chain that we use to keep
530
  track of space. Then the chain will be used to cleanup "holes", occurred
531
  due to deletes and updates.
532
*/
533
int ha_tina::chain_append()
534
{
535
  if ( chain_ptr != chain && (chain_ptr -1)->end == current_position)
536
    (chain_ptr -1)->end= next_position;
537
  else
538
  {
539
    /* We set up for the next position */
540
    if ((off_t)(chain_ptr - chain) == (chain_size -1))
541
    {
542
      off_t location= chain_ptr - chain;
543
      chain_size += DEFAULT_CHAIN_LENGTH;
544
      if (chain_alloced)
545
      {
546
        /* Must cast since my_malloc unlike malloc doesn't have a void ptr */
481 by Brian Aker
Remove all of uchar.
547
        if ((chain= (tina_set *) my_realloc((unsigned char*)chain,
1 by brian
clean slate
548
                                            chain_size, MYF(MY_WME))) == NULL)
549
          return -1;
550
      }
551
      else
552
      {
553
        tina_set *ptr= (tina_set *) my_malloc(chain_size * sizeof(tina_set),
554
                                              MYF(MY_WME));
555
        memcpy(ptr, chain, DEFAULT_CHAIN_LENGTH * sizeof(tina_set));
556
        chain= ptr;
557
        chain_alloced++;
558
      }
559
      chain_ptr= chain + location;
560
    }
561
    chain_ptr->begin= current_position;
562
    chain_ptr->end= next_position;
563
    chain_ptr++;
564
  }
565
566
  return 0;
567
}
568
569
570
/*
571
  Scans for a row.
572
*/
481 by Brian Aker
Remove all of uchar.
573
int ha_tina::find_current_row(unsigned char *buf)
1 by brian
clean slate
574
{
575
  off_t end_offset, curr_offset= current_position;
576
  int eoln_len;
577
  int error;
578
  bool read_all;
579
580
  free_root(&blobroot, MYF(MY_MARK_BLOCKS_FREE));
581
582
  /*
583
    We do not read further then local_saved_data_file_length in order
584
    not to conflict with undergoing concurrent insert.
585
  */
586
  if ((end_offset=
587
        find_eoln_buff(file_buff, current_position,
588
                       local_saved_data_file_length, &eoln_len)) == 0)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
589
    return(HA_ERR_END_OF_FILE);
1 by brian
clean slate
590
591
  /* We must read all columns in case a table is opened for update */
592
  read_all= !bitmap_is_clear_all(table->write_set);
593
  error= HA_ERR_CRASHED_ON_USAGE;
594
595
  memset(buf, 0, table->s->null_bytes);
596
597
  for (Field **field=table->field ; *field ; field++)
598
  {
599
    char curr_char;
600
    
601
    buffer.length(0);
602
    if (curr_offset >= end_offset)
603
      goto err;
604
    curr_char= file_buff->get_value(curr_offset);
605
    if (curr_char == '"')
606
    {
607
      curr_offset++; // Incrementpast the first quote
608
609
      for(; curr_offset < end_offset; curr_offset++)
610
      {
611
        curr_char= file_buff->get_value(curr_offset);
612
        // Need to convert line feeds!
613
        if (curr_char == '"' &&
614
            (curr_offset == end_offset - 1 ||
615
             file_buff->get_value(curr_offset + 1) == ','))
616
        {
617
          curr_offset+= 2; // Move past the , and the "
618
          break;
619
        }
620
        if (curr_char == '\\' && curr_offset != (end_offset - 1))
621
        {
622
          curr_offset++;
623
          curr_char= file_buff->get_value(curr_offset);
624
          if (curr_char == 'r')
625
            buffer.append('\r');
626
          else if (curr_char == 'n' )
627
            buffer.append('\n');
628
          else if (curr_char == '\\' || curr_char == '"')
629
            buffer.append(curr_char);
630
          else  /* This could only happed with an externally created file */
631
          {
632
            buffer.append('\\');
633
            buffer.append(curr_char);
634
          }
635
        }
636
        else // ordinary symbol
637
        {
638
          /*
639
            We are at final symbol and no last quote was found =>
640
            we are working with a damaged file.
641
          */
642
          if (curr_offset == end_offset - 1)
643
            goto err;
644
          buffer.append(curr_char);
645
        }
646
      }
647
    }
648
    else 
649
    {
650
      for(; curr_offset < end_offset; curr_offset++)
651
      {
652
        curr_char= file_buff->get_value(curr_offset);
653
        if (curr_char == ',')
654
        {
655
          curr_offset++;       // Skip the ,
656
          break;
657
        }
658
        buffer.append(curr_char);
659
      }
660
    }
661
662
    if (read_all || bitmap_is_set(table->read_set, (*field)->field_index))
663
    {
664
      if ((*field)->store(buffer.ptr(), buffer.length(), buffer.charset(),
665
                          CHECK_FIELD_WARN))
666
        goto err;
667
      if ((*field)->flags & BLOB_FLAG)
668
      {
669
        Field_blob *blob= *(Field_blob**) field;
481 by Brian Aker
Remove all of uchar.
670
        unsigned char *src, *tgt;
482 by Brian Aker
Remove uint.
671
        uint32_t length, packlength;
1 by brian
clean slate
672
        
673
        packlength= blob->pack_length_no_ptr();
674
        length= blob->get_length(blob->ptr);
212.6.3 by Mats Kindahl
Removing deprecated functions from code and replacing them with C99 equivalents:
675
        memcpy(&src, blob->ptr + packlength, sizeof(char*));
1 by brian
clean slate
676
        if (src)
677
        {
481 by Brian Aker
Remove all of uchar.
678
          tgt= (unsigned char*) alloc_root(&blobroot, length);
212.6.3 by Mats Kindahl
Removing deprecated functions from code and replacing them with C99 equivalents:
679
          memcpy(tgt, src, length);
680
          memcpy(blob->ptr + packlength, &tgt, sizeof(char*));
1 by brian
clean slate
681
        }
682
      }
683
    }
684
  }
685
  next_position= end_offset + eoln_len;
686
  error= 0;
687
688
err:
689
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
690
  return(error);
1 by brian
clean slate
691
}
692
693
/*
694
  If frm_error() is called in table.cc this is called to find out what file
695
  extensions exist for this handler.
696
*/
697
static const char *ha_tina_exts[] = {
698
  CSV_EXT,
699
  CSM_EXT,
461 by Monty Taylor
Removed NullS. bu-bye.
700
  NULL
1 by brian
clean slate
701
};
702
703
const char **ha_tina::bas_ext() const
704
{
705
  return ha_tina_exts;
706
}
707
708
/*
709
  Three functions below are needed to enable concurrent insert functionality
710
  for CSV engine. For more details see mysys/thr_lock.c
711
*/
712
77.1.6 by Monty Taylor
CSV is clean.
713
void tina_get_status(void* param,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
714
                     int concurrent_insert __attribute__((unused)))
1 by brian
clean slate
715
{
716
  ha_tina *tina= (ha_tina*) param;
717
  tina->get_status();
718
}
719
720
void tina_update_status(void* param)
721
{
722
  ha_tina *tina= (ha_tina*) param;
723
  tina->update_status();
724
}
725
726
/* this should exist and return 0 for concurrent insert to work */
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
727
bool tina_check_status(void* param __attribute__((unused)))
1 by brian
clean slate
728
{
729
  return 0;
730
}
731
732
/*
733
  Save the state of the table
734
735
  SYNOPSIS
736
    get_status()
737
738
  DESCRIPTION
739
    This function is used to retrieve the file length. During the lock
740
    phase of concurrent insert. For more details see comment to
741
    ha_tina::update_status below.
742
*/
743
744
void ha_tina::get_status()
745
{
746
  local_saved_data_file_length= share->saved_data_file_length;
747
}
748
749
750
/*
751
  Correct the state of the table. Called by unlock routines
752
  before the write lock is released.
753
754
  SYNOPSIS
755
    update_status()
756
757
  DESCRIPTION
758
    When we employ concurrent insert lock, we save current length of the file
759
    during the lock phase. We do not read further saved value, as we don't
760
    want to interfere with undergoing concurrent insert. Writers update file
761
    length info during unlock with update_status().
762
763
  NOTE
764
    For log tables concurrent insert works different. The reason is that
765
    log tables are always opened and locked. And as they do not unlock
766
    tables, the file length after writes should be updated in a different
283 by Brian Aker
Removed log_table variable efforts in CSV.
767
    way. 
1 by brian
clean slate
768
*/
769
770
void ha_tina::update_status()
771
{
772
  /* correct local_saved_data_file_length for writers */
773
  share->saved_data_file_length= local_saved_data_file_length;
774
}
775
776
777
/*
778
  Open a database file. Keep in mind that tables are caches, so
779
  this will not be called for every request. Any sort of positions
780
  that need to be reset should be kept in the ::extra() call.
781
*/
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
782
int ha_tina::open(const char *name, int mode __attribute__((unused)),
482 by Brian Aker
Remove uint.
783
                  uint32_t open_options)
1 by brian
clean slate
784
{
785
  if (!(share= get_share(name, table)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
786
    return(HA_ERR_OUT_OF_MEM);
1 by brian
clean slate
787
788
  if (share->crashed && !(open_options & HA_OPEN_FOR_REPAIR))
789
  {
790
    free_share(share);
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
791
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
792
  }
793
794
  local_data_file_version= share->data_file_version;
795
  if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
796
    return(0);
1 by brian
clean slate
797
798
  /*
799
    Init locking. Pass handler object to the locking routines,
800
    so that they could save/update local_saved_data_file_length value
801
    during locking. This is needed to enable concurrent inserts.
802
  */
803
  thr_lock_data_init(&share->lock, &lock, (void*) this);
804
  ref_length=sizeof(off_t);
805
806
  share->lock.get_status= tina_get_status;
807
  share->lock.update_status= tina_update_status;
808
  share->lock.check_status= tina_check_status;
809
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
810
  return(0);
1 by brian
clean slate
811
}
812
813
814
/*
815
  Close a database file. We remove ourselves from the shared strucutre.
816
  If it is empty we destroy it.
817
*/
818
int ha_tina::close(void)
819
{
820
  int rc= 0;
821
  rc= my_close(data_file, MYF(0));
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
822
  return(free_share(share) || rc);
1 by brian
clean slate
823
}
824
825
/*
826
  This is an INSERT. At the moment this handler just seeks to the end
827
  of the file and appends the data. In an error case it really should
828
  just truncate to the original position (this is not done yet).
829
*/
481 by Brian Aker
Remove all of uchar.
830
int ha_tina::write_row(unsigned char * buf)
1 by brian
clean slate
831
{
832
  int size;
833
834
  if (share->crashed)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
835
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
836
837
  ha_statistic_increment(&SSV::ha_write_count);
838
839
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
840
    table->timestamp_field->set_time();
841
842
  size= encode_quote(buf);
843
844
  if (!share->tina_write_opened)
845
    if (init_tina_writer())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
846
      return(-1);
1 by brian
clean slate
847
848
   /* use pwrite, as concurrent reader could have changed the position */
481 by Brian Aker
Remove all of uchar.
849
  if (my_write(share->tina_write_filedes, (unsigned char*)buffer.ptr(), size,
1 by brian
clean slate
850
               MYF(MY_WME | MY_NABP)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
851
    return(-1);
1 by brian
clean slate
852
853
  /* update local copy of the max position to see our own changes */
854
  local_saved_data_file_length+= size;
855
856
  /* update shared info */
857
  pthread_mutex_lock(&share->mutex);
858
  share->rows_recorded++;
859
  /* update status for the log tables */
860
  pthread_mutex_unlock(&share->mutex);
861
862
  stats.records++;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
863
  return(0);
1 by brian
clean slate
864
}
865
866
867
int ha_tina::open_update_temp_file_if_needed()
868
{
869
  char updated_fname[FN_REFLEN];
870
871
  if (!share->update_file_opened)
872
  {
873
    if ((update_temp_file=
874
           my_create(fn_format(updated_fname, share->table_name,
875
                               "", CSN_EXT,
876
                               MY_REPLACE_EXT | MY_UNPACK_FILENAME),
877
                     0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
878
      return 1;
163 by Brian Aker
Merge Monty's code.
879
    share->update_file_opened= true;
1 by brian
clean slate
880
    temp_file_length= 0;
881
  }
882
  return 0;
883
}
884
885
/*
886
  This is called for an update.
887
  Make sure you put in code to increment the auto increment, also
888
  update any timestamp data. Currently auto increment is not being
889
  fixed since autoincrements have yet to be added to this table handler.
890
  This will be called in a table scan right before the previous ::rnd_next()
891
  call.
892
*/
481 by Brian Aker
Remove all of uchar.
893
int ha_tina::update_row(const unsigned char * old_data __attribute__((unused)),
894
                        unsigned char * new_data)
1 by brian
clean slate
895
{
896
  int size;
897
  int rc= -1;
898
899
  ha_statistic_increment(&SSV::ha_update_count);
900
901
  if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
902
    table->timestamp_field->set_time();
903
904
  size= encode_quote(new_data);
905
906
  /*
907
    During update we mark each updating record as deleted 
908
    (see the chain_append()) then write new one to the temporary data file. 
909
    At the end of the sequence in the rnd_end() we append all non-marked
910
    records from the data file to the temporary data file then rename it.
911
    The temp_file_length is used to calculate new data file length.
912
  */
913
  if (chain_append())
914
    goto err;
915
916
  if (open_update_temp_file_if_needed())
917
    goto err;
918
481 by Brian Aker
Remove all of uchar.
919
  if (my_write(update_temp_file, (unsigned char*)buffer.ptr(), size,
1 by brian
clean slate
920
               MYF(MY_WME | MY_NABP)))
921
    goto err;
922
  temp_file_length+= size;
923
  rc= 0;
924
925
err:
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
926
  return(rc);
1 by brian
clean slate
927
}
928
929
930
/*
931
  Deletes a row. First the database will find the row, and then call this
932
  method. In the case of a table scan, the previous call to this will be
933
  the ::rnd_next() that found this row.
934
  The exception to this is an ORDER BY. This will cause the table handler
935
  to walk the table noting the positions of all rows that match a query.
936
  The table will then be deleted/positioned based on the ORDER (so RANDOM,
937
  DESC, ASC).
938
*/
481 by Brian Aker
Remove all of uchar.
939
int ha_tina::delete_row(const unsigned char * buf __attribute__((unused)))
1 by brian
clean slate
940
{
941
  ha_statistic_increment(&SSV::ha_delete_count);
942
943
  if (chain_append())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
944
    return(-1);
1 by brian
clean slate
945
946
  stats.records--;
947
  /* Update shared info */
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
948
  assert(share->rows_recorded);
1 by brian
clean slate
949
  pthread_mutex_lock(&share->mutex);
950
  share->rows_recorded--;
951
  pthread_mutex_unlock(&share->mutex);
952
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
953
  return(0);
1 by brian
clean slate
954
}
955
956
957
/**
958
  @brief Initialize the data file.
959
  
960
  @details Compare the local version of the data file with the shared one.
961
  If they differ, there are some changes behind and we have to reopen
962
  the data file to make the changes visible.
963
  Call @c file_buff->init_buff() at the end to read the beginning of the 
964
  data file into buffer.
965
  
966
  @retval  0  OK.
967
  @retval  1  There was an error.
968
*/
969
970
int ha_tina::init_data_file()
971
{
972
  if (local_data_file_version != share->data_file_version)
973
  {
974
    local_data_file_version= share->data_file_version;
975
    if (my_close(data_file, MYF(0)) ||
976
        (data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)
977
      return 1;
978
  }
979
  file_buff->init_buff(data_file);
980
  return 0;
981
}
982
983
984
/*
985
  All table scans call this first.
986
  The order of a table scan is:
987
988
  ha_tina::store_lock
989
  ha_tina::external_lock
990
  ha_tina::info
991
  ha_tina::rnd_init
992
  ha_tina::extra
993
  ENUM HA_EXTRA_CACHE   Cash record in HA_rrnd()
994
  ha_tina::rnd_next
995
  ha_tina::rnd_next
996
  ha_tina::rnd_next
997
  ha_tina::rnd_next
998
  ha_tina::rnd_next
999
  ha_tina::rnd_next
1000
  ha_tina::rnd_next
1001
  ha_tina::rnd_next
1002
  ha_tina::rnd_next
1003
  ha_tina::extra
1004
  ENUM HA_EXTRA_NO_CACHE   End cacheing of records (def)
1005
  ha_tina::external_lock
1006
  ha_tina::extra
1007
  ENUM HA_EXTRA_RESET   Reset database to after open
1008
1009
  Each call to ::rnd_next() represents a row returned in the can. When no more
1010
  rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
1011
  The ::info() call is just for the optimizer.
1012
1013
*/
1014
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1015
int ha_tina::rnd_init(bool scan __attribute__((unused)))
1 by brian
clean slate
1016
{
1017
  /* set buffer to the beginning of the file */
1018
  if (share->crashed || init_data_file())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1019
    return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
1020
1021
  current_position= next_position= 0;
1022
  stats.records= 0;
1023
  records_is_known= 0;
1024
  chain_ptr= chain;
1025
1026
  init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1027
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1028
  return(0);
1 by brian
clean slate
1029
}
1030
1031
/*
1032
  ::rnd_next() does all the heavy lifting for a table scan. You will need to
1033
  populate *buf with the correct field data. You can walk the field to
1034
  determine at what position you should store the data (take a look at how
1035
  ::find_current_row() works). The structure is something like:
1036
  0Foo  Dog  Friend
1037
  The first offset is for the first attribute. All space before that is
1038
  reserved for null count.
1039
  Basically this works as a mask for which rows are nulled (compared to just
1040
  empty).
1041
  This table handler doesn't do nulls and does not know the difference between
1042
  NULL and "". This is ok since this table handler is for spreadsheets and
1043
  they don't know about them either :)
1044
*/
481 by Brian Aker
Remove all of uchar.
1045
int ha_tina::rnd_next(unsigned char *buf)
1 by brian
clean slate
1046
{
1047
  int rc;
1048
1049
  if (share->crashed)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1050
      return(HA_ERR_CRASHED_ON_USAGE);
1 by brian
clean slate
1051
1052
  ha_statistic_increment(&SSV::ha_read_rnd_next_count);
1053
1054
  current_position= next_position;
1055
1056
  /* don't scan an empty file */
1057
  if (!local_saved_data_file_length)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1058
    return(HA_ERR_END_OF_FILE);
1 by brian
clean slate
1059
1060
  if ((rc= find_current_row(buf)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1061
    return(rc);
1 by brian
clean slate
1062
1063
  stats.records++;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1064
  return(0);
1 by brian
clean slate
1065
}
1066
1067
/*
1068
  In the case of an order by rows will need to be sorted.
1069
  ::position() is called after each call to ::rnd_next(),
1070
  the data it stores is to a byte array. You can store this
1071
  data via my_store_ptr(). ref_length is a variable defined to the
1072
  class that is the sizeof() of position being stored. In our case
1073
  its just a position. Look at the bdb code if you want to see a case
1074
  where something other then a number is stored.
1075
*/
481 by Brian Aker
Remove all of uchar.
1076
void ha_tina::position(const unsigned char *record __attribute__((unused)))
1 by brian
clean slate
1077
{
1078
  my_store_ptr(ref, ref_length, current_position);
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1079
  return;
1 by brian
clean slate
1080
}
1081
1082
1083
/*
1084
  Used to fetch a row from a posiion stored with ::position().
1085
  my_get_ptr() retrieves the data for you.
1086
*/
1087
481 by Brian Aker
Remove all of uchar.
1088
int ha_tina::rnd_pos(unsigned char * buf, unsigned char *pos)
1 by brian
clean slate
1089
{
1090
  ha_statistic_increment(&SSV::ha_read_rnd_count);
1091
  current_position= (off_t)my_get_ptr(pos,ref_length);
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1092
  return(find_current_row(buf));
1 by brian
clean slate
1093
}
1094
1095
/*
1096
  ::info() is used to return information to the optimizer.
1097
  Currently this table handler doesn't implement most of the fields
1098
  really needed. SHOW also makes use of this data
1099
*/
482 by Brian Aker
Remove uint.
1100
int ha_tina::info(uint32_t flag __attribute__((unused)))
1 by brian
clean slate
1101
{
1102
  /* This is a lie, but you don't want the optimizer to see zero or 1 */
1103
  if (!records_is_known && stats.records < 2) 
1104
    stats.records= 2;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1105
  return(0);
1 by brian
clean slate
1106
}
1107
1108
/*
1109
  Set end_pos to the last valid byte of continuous area, closest
1110
  to the given "hole", stored in the buffer. "Valid" here means,
1111
  not listed in the chain of deleted records ("holes").
1112
*/
1113
bool ha_tina::get_write_pos(off_t *end_pos, tina_set *closest_hole)
1114
{
1115
  if (closest_hole == chain_ptr) /* no more chains */
1116
    *end_pos= file_buff->end();
1117
  else
398.1.5 by Monty Taylor
Removed C++ includes and std namespace from global.h.
1118
    *end_pos= std::min(file_buff->end(),
1119
                       closest_hole->begin);
1 by brian
clean slate
1120
  return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin);
1121
}
1122
1123
1124
/*
1125
  Called after each table scan. In particular after deletes,
1126
  and updates. In the last case we employ chain of deleted
1127
  slots to clean up all of the dead space we have collected while
1128
  performing deletes/updates.
1129
*/
1130
int ha_tina::rnd_end()
1131
{
1132
  char updated_fname[FN_REFLEN];
1133
  off_t file_buffer_start= 0;
1134
1135
  free_root(&blobroot, MYF(0));
1136
  records_is_known= 1;
1137
1138
  if ((chain_ptr - chain)  > 0)
1139
  {
1140
    tina_set *ptr= chain;
1141
1142
    /*
1143
      Re-read the beginning of a file (as the buffer should point to the
1144
      end of file after the scan).
1145
    */
1146
    file_buff->init_buff(data_file);
1147
1148
    /*
1149
      The sort is needed when there were updates/deletes with random orders.
1150
      It sorts so that we move the firts blocks to the beginning.
1151
    */
1152
    my_qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set),
1153
             (qsort_cmp)sort_set);
1154
1155
    off_t write_begin= 0, write_end;
1156
1157
    /* create the file to write updated table if it wasn't yet created */
1158
    if (open_update_temp_file_if_needed())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1159
      return(-1);
1 by brian
clean slate
1160
1161
    /* write the file with updated info */
1162
    while ((file_buffer_start != -1))     // while not end of file
1163
    {
1164
      bool in_hole= get_write_pos(&write_end, ptr);
1165
      off_t write_length= write_end - write_begin;
1166
1167
      /* if there is something to write, write it */
1168
      if (write_length)
1169
      {
1170
        if (my_write(update_temp_file, 
481 by Brian Aker
Remove all of uchar.
1171
                     (unsigned char*) (file_buff->ptr() +
1 by brian
clean slate
1172
                               (write_begin - file_buff->start())),
1173
                     write_length, MYF_RW))
1174
          goto error;
1175
        temp_file_length+= write_length;
1176
      }
1177
      if (in_hole)
1178
      {
1179
        /* skip hole */
1180
        while (file_buff->end() <= ptr->end && file_buffer_start != -1)
1181
          file_buffer_start= file_buff->read_next();
1182
        write_begin= ptr->end;
1183
        ptr++;
1184
      }
1185
      else
1186
        write_begin= write_end;
1187
1188
      if (write_end == file_buff->end())
1189
        file_buffer_start= file_buff->read_next(); /* shift the buffer */
1190
1191
    }
1192
1193
    if (my_sync(update_temp_file, MYF(MY_WME)) ||
1194
        my_close(update_temp_file, MYF(0)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1195
      return(-1);
1 by brian
clean slate
1196
163 by Brian Aker
Merge Monty's code.
1197
    share->update_file_opened= false;
1 by brian
clean slate
1198
1199
    if (share->tina_write_opened)
1200
    {
1201
      if (my_close(share->tina_write_filedes, MYF(0)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1202
        return(-1);
1 by brian
clean slate
1203
      /*
1204
        Mark that the writer fd is closed, so that init_tina_writer()
1205
        will reopen it later.
1206
      */
163 by Brian Aker
Merge Monty's code.
1207
      share->tina_write_opened= false;
1 by brian
clean slate
1208
    }
1209
1210
    /*
1211
      Close opened fildes's. Then move updated file in place
1212
      of the old datafile.
1213
    */
1214
    if (my_close(data_file, MYF(0)) ||
1215
        my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT,
1216
                            MY_REPLACE_EXT | MY_UNPACK_FILENAME),
1217
                  share->data_file_name, MYF(0)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1218
      return(-1);
1 by brian
clean slate
1219
1220
    /* Open the file again */
1221
    if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1222
      return(-1);
1 by brian
clean slate
1223
    /*
1224
      As we reopened the data file, increase share->data_file_version 
1225
      in order to force other threads waiting on a table lock and  
1226
      have already opened the table to reopen the data file.
1227
      That makes the latest changes become visible to them.
1228
      Update local_data_file_version as no need to reopen it in the 
1229
      current thread.
1230
    */
1231
    share->data_file_version++;
1232
    local_data_file_version= share->data_file_version;
1233
    /*
1234
      The datafile is consistent at this point and the write filedes is
1235
      closed, so nothing worrying will happen to it in case of a crash.
1236
      Here we record this fact to the meta-file.
1237
    */
163 by Brian Aker
Merge Monty's code.
1238
    (void)write_meta_file(share->meta_file, share->rows_recorded, false);
1 by brian
clean slate
1239
    /* 
1240
      Update local_saved_data_file_length with the real length of the 
1241
      data file.
1242
    */
1243
    local_saved_data_file_length= temp_file_length;
1244
  }
1245
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1246
  return(0);
1 by brian
clean slate
1247
error:
1248
  my_close(update_temp_file, MYF(0));
163 by Brian Aker
Merge Monty's code.
1249
  share->update_file_opened= false;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1250
  return(-1);
1 by brian
clean slate
1251
}
1252
1253
1254
/*
1255
  Repair CSV table in the case, it is crashed.
1256
1257
  SYNOPSIS
1258
    repair()
1259
    thd         The thread, performing repair
1260
    check_opt   The options for repair. We do not use it currently.
1261
1262
  DESCRIPTION
1263
    If the file is empty, change # of rows in the file and complete recovery.
1264
    Otherwise, scan the table looking for bad rows. If none were found,
1265
    we mark file as a good one and return. If a bad row was encountered,
1266
    we truncate the datafile up to the last good row.
1267
1268
   TODO: Make repair more clever - it should try to recover subsequent
1269
         rows (after the first bad one) as well.
1270
*/
1271
77.1.6 by Monty Taylor
CSV is clean.
1272
int ha_tina::repair(THD* thd,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1273
                    HA_CHECK_OPT* check_opt __attribute__((unused)))
1 by brian
clean slate
1274
{
1275
  char repaired_fname[FN_REFLEN];
481 by Brian Aker
Remove all of uchar.
1276
  unsigned char *buf;
1 by brian
clean slate
1277
  File repair_file;
1278
  int rc;
1279
  ha_rows rows_repaired= 0;
1280
  off_t write_begin= 0, write_end;
1281
1282
  /* empty file */
1283
  if (!share->saved_data_file_length)
1284
  {
1285
    share->rows_recorded= 0;
1286
    goto end;
1287
  }
1288
1289
  /* Don't assert in field::val() functions */
1290
  table->use_all_columns();
481 by Brian Aker
Remove all of uchar.
1291
  if (!(buf= (unsigned char*) my_malloc(table->s->reclength, MYF(MY_WME))))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1292
    return(HA_ERR_OUT_OF_MEM);
1 by brian
clean slate
1293
1294
  /* position buffer to the start of the file */
1295
  if (init_data_file())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1296
    return(HA_ERR_CRASHED_ON_REPAIR);
1 by brian
clean slate
1297
1298
  /*
1299
    Local_saved_data_file_length is initialized during the lock phase.
1300
    Sometimes this is not getting executed before ::repair (e.g. for
1301
    the log tables). We set it manually here.
1302
  */
1303
  local_saved_data_file_length= share->saved_data_file_length;
1304
  /* set current position to the beginning of the file */
1305
  current_position= next_position= 0;
1306
1307
  init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1308
1309
  /* Read the file row-by-row. If everything is ok, repair is not needed. */
1310
  while (!(rc= find_current_row(buf)))
1311
  {
1312
    thd_inc_row_count(thd);
1313
    rows_repaired++;
1314
    current_position= next_position;
1315
  }
1316
1317
  free_root(&blobroot, MYF(0));
1318
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
1319
  free((char*)buf);
1 by brian
clean slate
1320
1321
  if (rc == HA_ERR_END_OF_FILE)
1322
  {
1323
    /*
1324
      All rows were read ok until end of file, the file does not need repair.
1325
      If rows_recorded != rows_repaired, we should update rows_recorded value
1326
      to the current amount of rows.
1327
    */
1328
    share->rows_recorded= rows_repaired;
1329
    goto end;
1330
  }
1331
1332
  /*
1333
    Otherwise we've encountered a bad row => repair is needed.
1334
    Let us create a temporary file.
1335
  */
1336
  if ((repair_file= my_create(fn_format(repaired_fname, share->table_name,
1337
                                        "", CSN_EXT,
1338
                                        MY_REPLACE_EXT|MY_UNPACK_FILENAME),
1339
                           0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1340
    return(HA_ERR_CRASHED_ON_REPAIR);
1 by brian
clean slate
1341
1342
  file_buff->init_buff(data_file);
1343
1344
1345
  /* we just truncated the file up to the first bad row. update rows count. */
1346
  share->rows_recorded= rows_repaired;
1347
1348
  /* write repaired file */
1349
  while (1)
1350
  {
398.1.5 by Monty Taylor
Removed C++ includes and std namespace from global.h.
1351
    write_end= std::min(file_buff->end(), current_position);
1 by brian
clean slate
1352
    if ((write_end - write_begin) &&
481 by Brian Aker
Remove all of uchar.
1353
        (my_write(repair_file, (unsigned char*)file_buff->ptr(),
1 by brian
clean slate
1354
                  write_end - write_begin, MYF_RW)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1355
      return(-1);
1 by brian
clean slate
1356
1357
    write_begin= write_end;
1358
    if (write_end== current_position)
1359
      break;
1360
    else
1361
      file_buff->read_next(); /* shift the buffer */
1362
  }
1363
1364
  /*
1365
    Close the files and rename repaired file to the datafile.
1366
    We have to close the files, as on Windows one cannot rename
1367
    a file, which descriptor is still open. EACCES will be returned
1368
    when trying to delete the "to"-file in my_rename().
1369
  */
1370
  if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) ||
1371
      my_rename(repaired_fname, share->data_file_name, MYF(0)))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1372
    return(-1);
1 by brian
clean slate
1373
1374
  /* Open the file again, it should now be repaired */
1375
  if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
1376
                          MYF(0))) == -1)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1377
     return(-1);
1 by brian
clean slate
1378
1379
  /* Set new file size. The file size will be updated by ::update_status() */
1380
  local_saved_data_file_length= (size_t) current_position;
1381
1382
end:
163 by Brian Aker
Merge Monty's code.
1383
  share->crashed= false;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1384
  return(HA_ADMIN_OK);
1 by brian
clean slate
1385
}
1386
1387
/*
1388
  DELETE without WHERE calls this
1389
*/
1390
1391
int ha_tina::delete_all_rows()
1392
{
1393
  int rc;
1394
1395
  if (!records_is_known)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1396
    return(my_errno=HA_ERR_WRONG_COMMAND);
1 by brian
clean slate
1397
1398
  if (!share->tina_write_opened)
1399
    if (init_tina_writer())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1400
      return(-1);
1 by brian
clean slate
1401
1402
  /* Truncate the file to zero size */
30 by Brian Aker
Large file and ftruncate() support
1403
  rc= ftruncate(share->tina_write_filedes, 0);
1 by brian
clean slate
1404
1405
  stats.records=0;
1406
  /* Update shared info */
1407
  pthread_mutex_lock(&share->mutex);
1408
  share->rows_recorded= 0;
1409
  pthread_mutex_unlock(&share->mutex);
1410
  local_saved_data_file_length= 0;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1411
  return(rc);
1 by brian
clean slate
1412
}
1413
1414
/*
1415
  Called by the database to lock the table. Keep in mind that this
1416
  is an internal lock.
1417
*/
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1418
THR_LOCK_DATA **ha_tina::store_lock(THD *thd __attribute__((unused)),
1 by brian
clean slate
1419
                                    THR_LOCK_DATA **to,
1420
                                    enum thr_lock_type lock_type)
1421
{
1422
  if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1423
    lock.type=lock_type;
1424
  *to++= &lock;
1425
  return to;
1426
}
1427
1428
/* 
1429
  Create a table. You do not want to leave the table open after a call to
1430
  this (the database will call ::open() if it needs to).
1431
*/
1432
327.1.5 by Brian Aker
Refactor around classes. TABLE_LIST has been factored out of table.h
1433
int ha_tina::create(const char *name, Table *table_arg,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1434
                    HA_CREATE_INFO *create_info __attribute__((unused)))
1 by brian
clean slate
1435
{
1436
  char name_buff[FN_REFLEN];
1437
  File create_file;
1438
1439
  /*
1440
    check columns
1441
  */
1442
  for (Field **field= table_arg->s->field; *field; field++)
1443
  {
1444
    if ((*field)->real_maybe_null())
1445
    {
1446
      my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1447
      return(HA_ERR_UNSUPPORTED);
1 by brian
clean slate
1448
    }
1449
  }
1450
  
1451
1452
  if ((create_file= my_create(fn_format(name_buff, name, "", CSM_EXT,
1453
                                        MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0,
1454
                              O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1455
    return(-1);
1 by brian
clean slate
1456
163 by Brian Aker
Merge Monty's code.
1457
  write_meta_file(create_file, 0, false);
1 by brian
clean slate
1458
  my_close(create_file, MYF(0));
1459
1460
  if ((create_file= my_create(fn_format(name_buff, name, "", CSV_EXT,
1461
                                        MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
1462
                              O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1463
    return(-1);
1 by brian
clean slate
1464
1465
  my_close(create_file, MYF(0));
1466
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1467
  return(0);
1 by brian
clean slate
1468
}
1469
77.1.6 by Monty Taylor
CSV is clean.
1470
int ha_tina::check(THD* thd,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1471
                   HA_CHECK_OPT* check_opt __attribute__((unused)))
1 by brian
clean slate
1472
{
1473
  int rc= 0;
481 by Brian Aker
Remove all of uchar.
1474
  unsigned char *buf;
1 by brian
clean slate
1475
  const char *old_proc_info;
1476
  ha_rows count= share->rows_recorded;
1477
1478
  old_proc_info= thd_proc_info(thd, "Checking table");
481 by Brian Aker
Remove all of uchar.
1479
  if (!(buf= (unsigned char*) my_malloc(table->s->reclength, MYF(MY_WME))))
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1480
    return(HA_ERR_OUT_OF_MEM);
1 by brian
clean slate
1481
1482
  /* position buffer to the start of the file */
1483
   if (init_data_file())
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1484
     return(HA_ERR_CRASHED);
1 by brian
clean slate
1485
1486
  /*
1487
    Local_saved_data_file_length is initialized during the lock phase.
1488
    Check does not use store_lock in certain cases. So, we set it
1489
    manually here.
1490
  */
1491
  local_saved_data_file_length= share->saved_data_file_length;
1492
  /* set current position to the beginning of the file */
1493
  current_position= next_position= 0;
1494
1495
  init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1496
1497
  /* Read the file row-by-row. If everything is ok, repair is not needed. */
1498
  while (!(rc= find_current_row(buf)))
1499
  {
1500
    thd_inc_row_count(thd);
1501
    count--;
1502
    current_position= next_position;
1503
  }
1504
  
1505
  free_root(&blobroot, MYF(0));
1506
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
1507
  free((char*)buf);
1 by brian
clean slate
1508
  thd_proc_info(thd, old_proc_info);
1509
1510
  if ((rc != HA_ERR_END_OF_FILE) || count)
1511
  {
163 by Brian Aker
Merge Monty's code.
1512
    share->crashed= true;
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1513
    return(HA_ADMIN_CORRUPT);
1 by brian
clean slate
1514
  }
1515
  else
51.3.8 by Jay Pipes
Removed DBUG from CSV and Blackhole storage engines
1516
    return(HA_ADMIN_OK);
1 by brian
clean slate
1517
}
1518
1519
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
1520
bool ha_tina::check_if_incompatible_data(HA_CREATE_INFO *info __attribute__((unused)),
482 by Brian Aker
Remove uint.
1521
                                         uint32_t table_changes __attribute__((unused)))
1 by brian
clean slate
1522
{
1523
  return COMPATIBLE_DATA_YES;
1524
}
1525
1526
mysql_declare_plugin(csv)
1527
{
319.1.1 by Grant Limberg
renamed all instances of MYSQL_ to DRIZZLE_
1528
  DRIZZLE_STORAGE_ENGINE_PLUGIN,
1 by brian
clean slate
1529
  "CSV",
177.4.3 by mark
ripped out more plugin ABI and API version checking, and plugin versions are now strings
1530
  "1.0",
1 by brian
clean slate
1531
  "Brian Aker, MySQL AB",
1532
  "CSV storage engine",
1533
  PLUGIN_LICENSE_GPL,
1534
  tina_init_func, /* Plugin Init */
1535
  tina_done_func, /* Plugin Deinit */
1536
  NULL,                       /* status variables                */
1537
  NULL,                       /* system variables                */
1538
  NULL                        /* config options                  */
1539
}
1540
mysql_declare_plugin_end;
1541