~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/csv/ha_tina.cc

  • Committer: Olaf van der Spek
  • Date: 2011-03-23 10:31:37 UTC
  • mto: (2247.1.1 build)
  • mto: This revision was merged to the branch mainline in revision 2248.
  • Revision ID: olafvdspek@gmail.com-20110323103137-lwevis2tfchgu18u
Propogate return void

Show diffs side-by-side

added added

removed removed

Lines of Context:
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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  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
 
*/
43
 
#include "config.h"
44
 
#include <drizzled/field.h>
45
 
#include <drizzled/field/blob.h>
46
 
#include <drizzled/field/timestamp.h>
47
 
#include <drizzled/error.h>
48
 
#include <drizzled/table.h>
49
 
#include <drizzled/session.h>
50
 
#include "drizzled/internal/my_sys.h"
51
 
 
52
 
#include "ha_tina.h"
53
 
 
54
 
#include <fcntl.h>
55
 
 
56
 
#include <algorithm>
57
 
#include <vector>
58
 
#include <string>
59
 
#include <map>
60
 
 
61
 
using namespace std;
62
 
using namespace drizzled;
63
 
 
64
 
/*
65
 
  unsigned char + unsigned char + uint64_t + uint64_t + uint64_t + uint64_t + unsigned char
66
 
*/
67
 
#define META_BUFFER_SIZE sizeof(unsigned char) + sizeof(unsigned char) + sizeof(uint64_t) \
68
 
  + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(unsigned char)
69
 
#define TINA_CHECK_HEADER 254 // The number we use to determine corruption
70
 
#define BLOB_MEMROOT_ALLOC_SIZE 8192
71
 
 
72
 
/* The file extension */
73
 
#define CSV_EXT ".CSV"               // The data file
74
 
#define CSN_EXT ".CSN"               // Files used during repair and update
75
 
#define CSM_EXT ".CSM"               // Meta file
76
 
 
77
 
 
78
 
static int read_meta_file(int meta_file, ha_rows *rows);
79
 
static int write_meta_file(int meta_file, ha_rows rows, bool dirty);
80
 
 
81
 
/* Stuff for shares */
82
 
pthread_mutex_t tina_mutex;
83
 
 
84
 
/*****************************************************************************
85
 
 ** TINA tables
86
 
 *****************************************************************************/
87
 
 
88
 
/*
89
 
  If frm_error() is called in table.cc this is called to find out what file
90
 
  extensions exist for this Cursor.
91
 
*/
92
 
static const char *ha_tina_exts[] = {
93
 
  CSV_EXT,
94
 
  CSM_EXT,
95
 
  NULL
96
 
};
97
 
 
98
 
class Tina : public drizzled::plugin::StorageEngine
99
 
{
100
 
  typedef std::map<string, TinaShare*> TinaMap;
101
 
  TinaMap tina_open_tables;
102
 
public:
103
 
  Tina(const string& name_arg)
104
 
   : drizzled::plugin::StorageEngine(name_arg,
105
 
                                     HTON_TEMPORARY_ONLY |
106
 
                                     HTON_NO_AUTO_INCREMENT |
107
 
                                     HTON_SKIP_STORE_LOCK),
108
 
    tina_open_tables()
109
 
  {}
110
 
  virtual ~Tina()
111
 
  {
112
 
    pthread_mutex_destroy(&tina_mutex);
113
 
  }
114
 
 
115
 
  virtual Cursor *create(Table &table)
116
 
  {
117
 
    return new ha_tina(*this, table);
118
 
  }
119
 
 
120
 
  const char **bas_ext() const {
121
 
    return ha_tina_exts;
122
 
  }
123
 
 
124
 
  int doCreateTable(Session &,
125
 
                    Table &table_arg,
126
 
                    const drizzled::TableIdentifier &identifier,
127
 
                    drizzled::message::Table&);
128
 
 
129
 
  int doGetTableDefinition(Session& session,
130
 
                           const drizzled::TableIdentifier &identifier,
131
 
                           drizzled::message::Table &table_message);
132
 
 
133
 
  int doDropTable(Session&, const drizzled::TableIdentifier &identifier);
134
 
  TinaShare *findOpenTable(const string table_name);
135
 
  void addOpenTable(const string &table_name, TinaShare *);
136
 
  void deleteOpenTable(const string &table_name);
137
 
 
138
 
 
139
 
  uint32_t max_keys()          const { return 0; }
140
 
  uint32_t max_key_parts()     const { return 0; }
141
 
  uint32_t max_key_length()    const { return 0; }
142
 
  bool doDoesTableExist(Session& session, const drizzled::TableIdentifier &identifier);
143
 
  int doRenameTable(Session&, const drizzled::TableIdentifier &from, const drizzled::TableIdentifier &to);
144
 
 
145
 
  void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
146
 
                             const drizzled::SchemaIdentifier &schema_identifier,
147
 
                             drizzled::TableIdentifier::vector &set_of_identifiers);
148
 
};
149
 
 
150
 
void Tina::doGetTableIdentifiers(drizzled::CachedDirectory&,
151
 
                                 const drizzled::SchemaIdentifier&,
152
 
                                 drizzled::TableIdentifier::vector&)
153
 
{
154
 
}
155
 
 
156
 
int Tina::doRenameTable(Session &session,
157
 
                        const drizzled::TableIdentifier &from, const drizzled::TableIdentifier &to)
158
 
{
159
 
  int error= 0;
160
 
  for (const char **ext= bas_ext(); *ext ; ext++)
161
 
  {
162
 
    if (rename_file_ext(from.getPath().c_str(), to.getPath().c_str(), *ext))
163
 
    {
164
 
      if ((error=errno) != ENOENT)
165
 
        break;
166
 
      error= 0;
167
 
    }
168
 
  }
169
 
 
170
 
  session.getMessageCache().renameTableMessage(from, to);
171
 
 
172
 
  return error;
173
 
}
174
 
 
175
 
bool Tina::doDoesTableExist(Session &session, const drizzled::TableIdentifier &identifier)
176
 
{
177
 
  return session.getMessageCache().doesTableMessageExist(identifier);
178
 
}
179
 
 
180
 
 
181
 
int Tina::doDropTable(Session &session,
182
 
                      const drizzled::TableIdentifier &identifier)
183
 
{
184
 
  int error= 0;
185
 
  int enoent_or_zero= ENOENT;                   // Error if no file was deleted
186
 
 
187
 
  for (const char **ext= bas_ext(); *ext ; ext++)
188
 
  {
189
 
    std::string full_name= identifier.getPath();
190
 
    full_name.append(*ext);
191
 
 
192
 
    if (internal::my_delete_with_symlink(full_name.c_str(), MYF(0)))
193
 
    {
194
 
      if ((error= errno) != ENOENT)
195
 
        break;
196
 
    }
197
 
    else
198
 
    {
199
 
      enoent_or_zero= 0;                        // No error for ENOENT
200
 
    }
201
 
    error= enoent_or_zero;
202
 
  }
203
 
 
204
 
  session.getMessageCache().removeTableMessage(identifier);
205
 
 
206
 
  return error;
207
 
}
208
 
 
209
 
TinaShare *Tina::findOpenTable(const string table_name)
210
 
{
211
 
  TinaMap::iterator find_iter=
212
 
    tina_open_tables.find(table_name);
213
 
 
214
 
  if (find_iter != tina_open_tables.end())
215
 
    return (*find_iter).second;
216
 
  else
217
 
    return NULL;
218
 
}
219
 
 
220
 
void Tina::addOpenTable(const string &table_name, TinaShare *share)
221
 
{
222
 
  tina_open_tables[table_name]= share;
223
 
}
224
 
 
225
 
void Tina::deleteOpenTable(const string &table_name)
226
 
{
227
 
  tina_open_tables.erase(table_name);
228
 
}
229
 
 
230
 
 
231
 
int Tina::doGetTableDefinition(Session &session,
232
 
                               const drizzled::TableIdentifier &identifier,
233
 
                               drizzled::message::Table &table_message)
234
 
{
235
 
  if (session.getMessageCache().getTableMessage(identifier, table_message))
236
 
    return EEXIST;
237
 
 
238
 
  return ENOENT;
239
 
}
240
 
 
241
 
 
242
 
static Tina *tina_engine= NULL;
243
 
 
244
 
static int tina_init_func(drizzled::module::Context &context)
245
 
{
246
 
 
247
 
  tina_engine= new Tina("CSV");
248
 
  context.add(tina_engine);
249
 
 
250
 
  pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST);
251
 
  return 0;
252
 
}
253
 
 
254
 
 
255
 
 
256
 
TinaShare::TinaShare(const std::string &table_name_arg) : 
257
 
  table_name(table_name_arg),
258
 
  data_file_name(table_name_arg),
259
 
  use_count(0),
260
 
  saved_data_file_length(0),
261
 
  update_file_opened(false),
262
 
  tina_write_opened(false),
263
 
  crashed(false),
264
 
  rows_recorded(0),
265
 
  data_file_version(0)
266
 
{
267
 
  data_file_name.append(CSV_EXT);
268
 
}
269
 
 
270
 
TinaShare::~TinaShare()
271
 
{
272
 
  pthread_mutex_destroy(&mutex);
273
 
}
274
 
 
275
 
/*
276
 
  Simple lock controls.
277
 
*/
278
 
TinaShare *ha_tina::get_share(const std::string &table_name)
279
 
{
280
 
  pthread_mutex_lock(&tina_mutex);
281
 
 
282
 
  Tina *a_tina= static_cast<Tina *>(getEngine());
283
 
  share= a_tina->findOpenTable(table_name);
284
 
 
285
 
  std::string meta_file_name;
286
 
  struct stat file_stat;
287
 
 
288
 
  /*
289
 
    If share is not present in the hash, create a new share and
290
 
    initialize its members.
291
 
  */
292
 
  if (! share)
293
 
  {
294
 
    share= new TinaShare(table_name);
295
 
 
296
 
    if (share == NULL)
297
 
    {
298
 
      pthread_mutex_unlock(&tina_mutex);
299
 
      return NULL;
300
 
    }
301
 
 
302
 
    meta_file_name.assign(table_name);
303
 
    meta_file_name.append(CSM_EXT);
304
 
 
305
 
    if (stat(share->data_file_name.c_str(), &file_stat))
306
 
    {
307
 
      pthread_mutex_unlock(&tina_mutex);
308
 
      delete share;
309
 
      return NULL;
310
 
    }
311
 
  
312
 
    share->saved_data_file_length= file_stat.st_size;
313
 
 
314
 
    a_tina->addOpenTable(share->table_name, share);
315
 
 
316
 
    pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
317
 
 
318
 
    /*
319
 
      Open or create the meta file. In the latter case, we'll get
320
 
      an error during read_meta_file and mark the table as crashed.
321
 
      Usually this will result in auto-repair, and we will get a good
322
 
      meta-file in the end.
323
 
    */
324
 
    if ((share->meta_file= internal::my_open(meta_file_name.c_str(),
325
 
                                             O_RDWR|O_CREAT, MYF(0))) == -1)
326
 
      share->crashed= true;
327
 
 
328
 
    /*
329
 
      If the meta file will not open we assume it is crashed and
330
 
      mark it as such.
331
 
    */
332
 
    if (read_meta_file(share->meta_file, &share->rows_recorded))
333
 
      share->crashed= true;
334
 
  }
335
 
  share->use_count++;
336
 
  pthread_mutex_unlock(&tina_mutex);
337
 
 
338
 
  return share;
339
 
}
340
 
 
341
 
 
342
 
/*
343
 
  Read CSV meta-file
344
 
 
345
 
  SYNOPSIS
346
 
    read_meta_file()
347
 
    meta_file   The meta-file filedes
348
 
    ha_rows     Pointer to the var we use to store rows count.
349
 
                These are read from the meta-file.
350
 
 
351
 
  DESCRIPTION
352
 
 
353
 
    Read the meta-file info. For now we are only interested in
354
 
    rows counf, crashed bit and magic number.
355
 
 
356
 
  RETURN
357
 
    0 - OK
358
 
    non-zero - error occurred
359
 
*/
360
 
 
361
 
static int read_meta_file(int meta_file, ha_rows *rows)
362
 
{
363
 
  unsigned char meta_buffer[META_BUFFER_SIZE];
364
 
  unsigned char *ptr= meta_buffer;
365
 
 
366
 
  lseek(meta_file, 0, SEEK_SET);
367
 
  if (internal::my_read(meta_file, (unsigned char*)meta_buffer, META_BUFFER_SIZE, 0)
368
 
      != META_BUFFER_SIZE)
369
 
    return(HA_ERR_CRASHED_ON_USAGE);
370
 
 
371
 
  /*
372
 
    Parse out the meta data, we ignore version at the moment
373
 
  */
374
 
 
375
 
  ptr+= sizeof(unsigned char)*2; // Move past header
376
 
  *rows= (ha_rows)uint8korr(ptr);
377
 
  ptr+= sizeof(uint64_t); // Move past rows
378
 
  /*
379
 
    Move past check_point, auto_increment and forced_flushes fields.
380
 
    They are present in the format, but we do not use them yet.
381
 
  */
382
 
  ptr+= 3*sizeof(uint64_t);
383
 
 
384
 
  /* check crashed bit and magic number */
385
 
  if ((meta_buffer[0] != (unsigned char)TINA_CHECK_HEADER) ||
386
 
      ((bool)(*ptr)== true))
387
 
    return(HA_ERR_CRASHED_ON_USAGE);
388
 
 
389
 
  internal::my_sync(meta_file, MYF(MY_WME));
390
 
 
391
 
  return(0);
392
 
}
393
 
 
394
 
 
395
 
/*
396
 
  Write CSV meta-file
397
 
 
398
 
  SYNOPSIS
399
 
    write_meta_file()
400
 
    meta_file   The meta-file filedes
401
 
    ha_rows     The number of rows we have in the datafile.
402
 
    dirty       A flag, which marks whether we have a corrupt table
403
 
 
404
 
  DESCRIPTION
405
 
 
406
 
    Write meta-info the the file. Only rows count, crashed bit and
407
 
    magic number matter now.
408
 
 
409
 
  RETURN
410
 
    0 - OK
411
 
    non-zero - error occurred
412
 
*/
413
 
 
414
 
static int write_meta_file(int meta_file, ha_rows rows, bool dirty)
415
 
{
416
 
  unsigned char meta_buffer[META_BUFFER_SIZE];
417
 
  unsigned char *ptr= meta_buffer;
418
 
 
419
 
  *ptr= (unsigned char)TINA_CHECK_HEADER;
420
 
  ptr+= sizeof(unsigned char);
421
 
  *ptr= (unsigned char)TINA_VERSION;
422
 
  ptr+= sizeof(unsigned char);
423
 
  int8store(ptr, (uint64_t)rows);
424
 
  ptr+= sizeof(uint64_t);
425
 
  memset(ptr, 0, 3*sizeof(uint64_t));
426
 
  /*
427
 
     Skip over checkpoint, autoincrement and forced_flushes fields.
428
 
     We'll need them later.
429
 
  */
430
 
  ptr+= 3*sizeof(uint64_t);
431
 
  *ptr= (unsigned char)dirty;
432
 
 
433
 
  lseek(meta_file, 0, SEEK_SET);
434
 
  if (internal::my_write(meta_file, (unsigned char *)meta_buffer, META_BUFFER_SIZE, 0)
435
 
      != META_BUFFER_SIZE)
436
 
    return(-1);
437
 
 
438
 
  internal::my_sync(meta_file, MYF(MY_WME));
439
 
 
440
 
  return(0);
441
 
}
442
 
 
443
 
int ha_tina::init_tina_writer()
444
 
{
445
 
  /*
446
 
    Mark the file as crashed. We will set the flag back when we close
447
 
    the file. In the case of the crash it will remain marked crashed,
448
 
    which enforce recovery.
449
 
  */
450
 
  (void)write_meta_file(share->meta_file, share->rows_recorded, true);
451
 
 
452
 
  if ((share->tina_write_filedes=
453
 
        internal::my_open(share->data_file_name.c_str(), O_RDWR|O_APPEND, MYF(0))) == -1)
454
 
  {
455
 
    share->crashed= true;
456
 
    return(1);
457
 
  }
458
 
  share->tina_write_opened= true;
459
 
 
460
 
  return(0);
461
 
}
462
 
 
463
 
 
464
 
/*
465
 
  Free lock controls.
466
 
*/
467
 
int ha_tina::free_share()
468
 
{
469
 
  pthread_mutex_lock(&tina_mutex);
470
 
  int result_code= 0;
471
 
  if (!--share->use_count){
472
 
    /* Write the meta file. Mark it as crashed if needed. */
473
 
    (void)write_meta_file(share->meta_file, share->rows_recorded,
474
 
                          share->crashed ? true :false);
475
 
    if (internal::my_close(share->meta_file, MYF(0)))
476
 
      result_code= 1;
477
 
    if (share->tina_write_opened)
478
 
    {
479
 
      if (internal::my_close(share->tina_write_filedes, MYF(0)))
480
 
        result_code= 1;
481
 
      share->tina_write_opened= false;
482
 
    }
483
 
 
484
 
    Tina *a_tina= static_cast<Tina *>(getEngine());
485
 
    a_tina->deleteOpenTable(share->table_name);
486
 
    delete share;
487
 
  }
488
 
  pthread_mutex_unlock(&tina_mutex);
489
 
 
490
 
  return(result_code);
491
 
}
492
 
 
493
 
 
494
 
/*
495
 
  This function finds the end of a line and returns the length
496
 
  of the line ending.
497
 
 
498
 
  We support three kinds of line endings:
499
 
  '\r'     --  Old Mac OS line ending
500
 
  '\n'     --  Traditional Unix and Mac OS X line ending
501
 
  '\r''\n' --  DOS\Windows line ending
502
 
*/
503
 
 
504
 
static off_t find_eoln_buff(Transparent_file *data_buff, off_t begin,
505
 
                            off_t end, int *eoln_len)
506
 
{
507
 
  *eoln_len= 0;
508
 
 
509
 
  for (off_t x= begin; x < end; x++)
510
 
  {
511
 
    /* Unix (includes Mac OS X) */
512
 
    if (data_buff->get_value(x) == '\n')
513
 
      *eoln_len= 1;
514
 
    else
515
 
      if (data_buff->get_value(x) == '\r') // Mac or Dos
516
 
      {
517
 
        /* old Mac line ending */
518
 
        if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
519
 
          *eoln_len= 1;
520
 
        else // DOS style ending
521
 
          *eoln_len= 2;
522
 
      }
523
 
 
524
 
    if (*eoln_len)  // end of line was found
525
 
      return x;
526
 
  }
527
 
 
528
 
  return 0;
529
 
}
530
 
 
531
 
 
532
 
 
533
 
ha_tina::ha_tina(drizzled::plugin::StorageEngine &engine_arg, Table &table_arg)
534
 
  :Cursor(engine_arg, table_arg),
535
 
  /*
536
 
    These definitions are found in Cursor.h
537
 
    They are not probably completely right.
538
 
  */
539
 
  current_position(0), next_position(0), local_saved_data_file_length(0),
540
 
  file_buff(0), local_data_file_version(0), records_is_known(0)
541
 
{
542
 
  /* Set our original buffers from pre-allocated memory */
543
 
  buffer.set((char*)byte_buffer, IO_SIZE, &my_charset_bin);
544
 
  file_buff= new Transparent_file();
545
 
}
546
 
 
547
 
 
548
 
/*
549
 
  Encode a buffer into the quoted format.
550
 
*/
551
 
 
552
 
int ha_tina::encode_quote(unsigned char *)
553
 
{
554
 
  char attribute_buffer[1024];
555
 
  String attribute(attribute_buffer, sizeof(attribute_buffer),
556
 
                   &my_charset_bin);
557
 
 
558
 
  buffer.length(0);
559
 
 
560
 
  for (Field **field= getTable()->getFields() ; *field ; field++)
561
 
  {
562
 
    const char *ptr;
563
 
    const char *end_ptr;
564
 
    const bool was_null= (*field)->is_null();
565
 
 
566
 
    /*
567
 
      assistance for backwards compatibility in production builds.
568
 
      note: this will not work for ENUM columns.
569
 
    */
570
 
    if (was_null)
571
 
    {
572
 
      (*field)->set_default();
573
 
      (*field)->set_notnull();
574
 
    }
575
 
 
576
 
    /* 
577
 
      Since we are needing to "translate" the type into a string we 
578
 
      will need to do a val_str(). This would cause an assert() to
579
 
      normally occur since we are onlying "writing" values.
580
 
    */
581
 
    (*field)->setReadSet();
582
 
    (*field)->val_str(&attribute,&attribute);
583
 
 
584
 
    if (was_null)
585
 
      (*field)->set_null();
586
 
 
587
 
    if ((*field)->str_needs_quotes())
588
 
    {
589
 
      ptr= attribute.ptr();
590
 
      end_ptr= attribute.length() + ptr;
591
 
 
592
 
      buffer.append('"');
593
 
 
594
 
      while (ptr < end_ptr)
595
 
      {
596
 
        if (*ptr == '"')
597
 
        {
598
 
          buffer.append('\\');
599
 
          buffer.append('"');
600
 
          (void) *ptr++;
601
 
        }
602
 
        else if (*ptr == '\r')
603
 
        {
604
 
          buffer.append('\\');
605
 
          buffer.append('r');
606
 
          (void) *ptr++;
607
 
        }
608
 
        else if (*ptr == '\\')
609
 
        {
610
 
          buffer.append('\\');
611
 
          buffer.append('\\');
612
 
          (void) *ptr++;
613
 
        }
614
 
        else if (*ptr == '\n')
615
 
        {
616
 
          buffer.append('\\');
617
 
          buffer.append('n');
618
 
          (void) *ptr++;
619
 
        }
620
 
        else
621
 
          buffer.append(*ptr++);
622
 
      }
623
 
      buffer.append('"');
624
 
    }
625
 
    else
626
 
    {
627
 
      buffer.append(attribute);
628
 
    }
629
 
 
630
 
    buffer.append(',');
631
 
  }
632
 
  // Remove the comma, add a line feed
633
 
  buffer.length(buffer.length() - 1);
634
 
  buffer.append('\n');
635
 
 
636
 
  //buffer.replace(buffer.length(), 0, "\n", 1);
637
 
 
638
 
  return (buffer.length());
639
 
}
640
 
 
641
 
/*
642
 
  chain_append() adds delete positions to the chain that we use to keep
643
 
  track of space. Then the chain will be used to cleanup "holes", occurred
644
 
  due to deletes and updates.
645
 
*/
646
 
int ha_tina::chain_append()
647
 
{
648
 
  if (chain.size() > 0 && chain.back().second == current_position)
649
 
    chain.back().second= next_position;
650
 
  else
651
 
    chain.push_back(make_pair(current_position, next_position));
652
 
  return 0;
653
 
}
654
 
 
655
 
 
656
 
/*
657
 
  Scans for a row.
658
 
*/
659
 
int ha_tina::find_current_row(unsigned char *buf)
660
 
{
661
 
  off_t end_offset, curr_offset= current_position;
662
 
  int eoln_len;
663
 
  int error;
664
 
 
665
 
  blobroot.free_root(MYF(drizzled::memory::MARK_BLOCKS_FREE));
666
 
 
667
 
  /*
668
 
    We do not read further then local_saved_data_file_length in order
669
 
    not to conflict with undergoing concurrent insert.
670
 
  */
671
 
  if ((end_offset=
672
 
        find_eoln_buff(file_buff, current_position,
673
 
                       local_saved_data_file_length, &eoln_len)) == 0)
674
 
    return(HA_ERR_END_OF_FILE);
675
 
 
676
 
  error= HA_ERR_CRASHED_ON_USAGE;
677
 
 
678
 
  memset(buf, 0, getTable()->getShare()->null_bytes);
679
 
 
680
 
  for (Field **field= getTable()->getFields() ; *field ; field++)
681
 
  {
682
 
    char curr_char;
683
 
 
684
 
    buffer.length(0);
685
 
    if (curr_offset >= end_offset)
686
 
      goto err;
687
 
    curr_char= file_buff->get_value(curr_offset);
688
 
    if (curr_char == '"')
689
 
    {
690
 
      curr_offset++; // Incrementpast the first quote
691
 
 
692
 
      for(; curr_offset < end_offset; curr_offset++)
693
 
      {
694
 
        curr_char= file_buff->get_value(curr_offset);
695
 
        // Need to convert line feeds!
696
 
        if (curr_char == '"' &&
697
 
            (curr_offset == end_offset - 1 ||
698
 
             file_buff->get_value(curr_offset + 1) == ','))
699
 
        {
700
 
          curr_offset+= 2; // Move past the , and the "
701
 
          break;
702
 
        }
703
 
        if (curr_char == '\\' && curr_offset != (end_offset - 1))
704
 
        {
705
 
          curr_offset++;
706
 
          curr_char= file_buff->get_value(curr_offset);
707
 
          if (curr_char == 'r')
708
 
            buffer.append('\r');
709
 
          else if (curr_char == 'n' )
710
 
            buffer.append('\n');
711
 
          else if (curr_char == '\\' || curr_char == '"')
712
 
            buffer.append(curr_char);
713
 
          else  /* This could only happed with an externally created file */
714
 
          {
715
 
            buffer.append('\\');
716
 
            buffer.append(curr_char);
717
 
          }
718
 
        }
719
 
        else // ordinary symbol
720
 
        {
721
 
          /*
722
 
            We are at final symbol and no last quote was found =>
723
 
            we are working with a damaged file.
724
 
          */
725
 
          if (curr_offset == end_offset - 1)
726
 
            goto err;
727
 
          buffer.append(curr_char);
728
 
        }
729
 
      }
730
 
    }
731
 
    else
732
 
    {
733
 
      for(; curr_offset < end_offset; curr_offset++)
734
 
      {
735
 
        curr_char= file_buff->get_value(curr_offset);
736
 
        if (curr_char == ',')
737
 
        {
738
 
          curr_offset++;       // Skip the ,
739
 
          break;
740
 
        }
741
 
        buffer.append(curr_char);
742
 
      }
743
 
    }
744
 
 
745
 
    if ((*field)->isReadSet() || (*field)->isWriteSet())
746
 
    {
747
 
      /* This masks a bug in the logic for a SELECT * */
748
 
      (*field)->setWriteSet();
749
 
      if ((*field)->store_and_check(CHECK_FIELD_WARN, buffer.c_ptr(), buffer.length(), buffer.charset()))
750
 
      {
751
 
        goto err;
752
 
      }
753
 
 
754
 
      if ((*field)->flags & BLOB_FLAG)
755
 
      {
756
 
        Field_blob *blob= *(Field_blob**) field;
757
 
        unsigned char *src, *tgt;
758
 
        uint32_t length, packlength;
759
 
 
760
 
        packlength= blob->pack_length_no_ptr();
761
 
        length= blob->get_length(blob->ptr);
762
 
        memcpy(&src, blob->ptr + packlength, sizeof(char*));
763
 
        if (src)
764
 
        {
765
 
          tgt= (unsigned char*) blobroot.alloc_root(length);
766
 
          memmove(tgt, src, length);
767
 
          memcpy(blob->ptr + packlength, &tgt, sizeof(char*));
768
 
        }
769
 
      }
770
 
    }
771
 
  }
772
 
  next_position= end_offset + eoln_len;
773
 
  error= 0;
774
 
 
775
 
err:
776
 
 
777
 
  return(error);
778
 
}
779
 
 
780
 
/*
781
 
  Open a database file. Keep in mind that tables are caches, so
782
 
  this will not be called for every request. Any sort of positions
783
 
  that need to be reset should be kept in the ::extra() call.
784
 
*/
785
 
int ha_tina::doOpen(const TableIdentifier &identifier, int , uint32_t )
786
 
{
787
 
  if (not (share= get_share(identifier.getPath().c_str())))
788
 
    return(ENOENT);
789
 
 
790
 
  if (share->crashed)
791
 
  {
792
 
    free_share();
793
 
    return(HA_ERR_CRASHED_ON_USAGE);
794
 
  }
795
 
 
796
 
  local_data_file_version= share->data_file_version;
797
 
  if ((data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1)
798
 
    return(0);
799
 
 
800
 
  /*
801
 
    Init locking. Pass Cursor object to the locking routines,
802
 
    so that they could save/update local_saved_data_file_length value
803
 
    during locking. This is needed to enable concurrent inserts.
804
 
  */
805
 
  ref_length=sizeof(off_t);
806
 
 
807
 
  return(0);
808
 
}
809
 
 
810
 
/*
811
 
  Close a database file. We remove ourselves from the shared strucutre.
812
 
  If it is empty we destroy it.
813
 
*/
814
 
int ha_tina::close(void)
815
 
{
816
 
  int rc= 0;
817
 
  rc= internal::my_close(data_file, MYF(0));
818
 
  return(free_share() || rc);
819
 
}
820
 
 
821
 
/*
822
 
  This is an INSERT. At the moment this Cursor just seeks to the end
823
 
  of the file and appends the data. In an error case it really should
824
 
  just truncate to the original position (this is not done yet).
825
 
*/
826
 
int ha_tina::doInsertRecord(unsigned char * buf)
827
 
{
828
 
  int size;
829
 
 
830
 
  if (share->crashed)
831
 
      return(HA_ERR_CRASHED_ON_USAGE);
832
 
 
833
 
  size= encode_quote(buf);
834
 
 
835
 
  if (!share->tina_write_opened)
836
 
    if (init_tina_writer())
837
 
      return(-1);
838
 
 
839
 
   /* use pwrite, as concurrent reader could have changed the position */
840
 
  if (internal::my_write(share->tina_write_filedes, (unsigned char*)buffer.ptr(), size,
841
 
               MYF(MY_WME | MY_NABP)))
842
 
    return(-1);
843
 
 
844
 
  /* update local copy of the max position to see our own changes */
845
 
  local_saved_data_file_length+= size;
846
 
 
847
 
  /* update shared info */
848
 
  pthread_mutex_lock(&share->mutex);
849
 
  share->rows_recorded++;
850
 
  /* update status for the log tables */
851
 
  pthread_mutex_unlock(&share->mutex);
852
 
 
853
 
  stats.records++;
854
 
  return(0);
855
 
}
856
 
 
857
 
 
858
 
int ha_tina::open_update_temp_file_if_needed()
859
 
{
860
 
  char updated_fname[FN_REFLEN];
861
 
 
862
 
  if (!share->update_file_opened)
863
 
  {
864
 
    if ((update_temp_file=
865
 
           internal::my_create(internal::fn_format(updated_fname, share->table_name.c_str(),
866
 
                               "", CSN_EXT,
867
 
                               MY_REPLACE_EXT | MY_UNPACK_FILENAME),
868
 
                     0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
869
 
      return 1;
870
 
    share->update_file_opened= true;
871
 
    temp_file_length= 0;
872
 
  }
873
 
  return 0;
874
 
}
875
 
 
876
 
/*
877
 
  This is called for an update.
878
 
  Make sure you put in code to increment the auto increment, also
879
 
  update any timestamp data. Currently auto increment is not being
880
 
  fixed since autoincrements have yet to be added to this table Cursor.
881
 
  This will be called in a table scan right before the previous ::rnd_next()
882
 
  call.
883
 
*/
884
 
int ha_tina::doUpdateRecord(const unsigned char *, unsigned char * new_data)
885
 
{
886
 
  int size;
887
 
  int rc= -1;
888
 
 
889
 
  size= encode_quote(new_data);
890
 
 
891
 
  /*
892
 
    During update we mark each updating record as deleted
893
 
    (see the chain_append()) then write new one to the temporary data file.
894
 
    At the end of the sequence in the doEndTableScan() we append all non-marked
895
 
    records from the data file to the temporary data file then rename it.
896
 
    The temp_file_length is used to calculate new data file length.
897
 
  */
898
 
  if (chain_append())
899
 
    goto err;
900
 
 
901
 
  if (open_update_temp_file_if_needed())
902
 
    goto err;
903
 
 
904
 
  if (internal::my_write(update_temp_file, (unsigned char*)buffer.ptr(), size,
905
 
               MYF(MY_WME | MY_NABP)))
906
 
    goto err;
907
 
  temp_file_length+= size;
908
 
  rc= 0;
909
 
 
910
 
err:
911
 
  return(rc);
912
 
}
913
 
 
914
 
 
915
 
/*
916
 
  Deletes a row. First the database will find the row, and then call this
917
 
  method. In the case of a table scan, the previous call to this will be
918
 
  the ::rnd_next() that found this row.
919
 
  The exception to this is an ORDER BY. This will cause the table Cursor
920
 
  to walk the table noting the positions of all rows that match a query.
921
 
  The table will then be deleted/positioned based on the ORDER (so RANDOM,
922
 
  DESC, ASC).
923
 
*/
924
 
int ha_tina::doDeleteRecord(const unsigned char *)
925
 
{
926
 
 
927
 
  if (chain_append())
928
 
    return(-1);
929
 
 
930
 
  stats.records--;
931
 
  /* Update shared info */
932
 
  assert(share->rows_recorded);
933
 
  pthread_mutex_lock(&share->mutex);
934
 
  share->rows_recorded--;
935
 
  pthread_mutex_unlock(&share->mutex);
936
 
 
937
 
  return(0);
938
 
}
939
 
 
940
 
 
941
 
/**
942
 
  @brief Initialize the data file.
943
 
 
944
 
  @details Compare the local version of the data file with the shared one.
945
 
  If they differ, there are some changes behind and we have to reopen
946
 
  the data file to make the changes visible.
947
 
  Call @c file_buff->init_buff() at the end to read the beginning of the
948
 
  data file into buffer.
949
 
 
950
 
  @retval  0  OK.
951
 
  @retval  1  There was an error.
952
 
*/
953
 
 
954
 
int ha_tina::init_data_file()
955
 
{
956
 
  if (local_data_file_version != share->data_file_version)
957
 
  {
958
 
    local_data_file_version= share->data_file_version;
959
 
    if (internal::my_close(data_file, MYF(0)) ||
960
 
        (data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1)
961
 
      return 1;
962
 
  }
963
 
  file_buff->init_buff(data_file);
964
 
  return 0;
965
 
}
966
 
 
967
 
 
968
 
/*
969
 
  All table scans call this first.
970
 
  The order of a table scan is:
971
 
 
972
 
  ha_tina::info
973
 
  ha_tina::rnd_init
974
 
  ha_tina::extra
975
 
  ENUM HA_EXTRA_CACHE   Cash record in HA_rrnd()
976
 
  ha_tina::rnd_next
977
 
  ha_tina::rnd_next
978
 
  ha_tina::rnd_next
979
 
  ha_tina::rnd_next
980
 
  ha_tina::rnd_next
981
 
  ha_tina::rnd_next
982
 
  ha_tina::rnd_next
983
 
  ha_tina::rnd_next
984
 
  ha_tina::rnd_next
985
 
  ha_tina::extra
986
 
  ENUM HA_EXTRA_NO_CACHE   End cacheing of records (def)
987
 
  ha_tina::extra
988
 
  ENUM HA_EXTRA_RESET   Reset database to after open
989
 
 
990
 
  Each call to ::rnd_next() represents a row returned in the can. When no more
991
 
  rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
992
 
  The ::info() call is just for the optimizer.
993
 
 
994
 
*/
995
 
 
996
 
int ha_tina::doStartTableScan(bool)
997
 
{
998
 
  /* set buffer to the beginning of the file */
999
 
  if (share->crashed || init_data_file())
1000
 
    return(HA_ERR_CRASHED_ON_USAGE);
1001
 
 
1002
 
  current_position= next_position= 0;
1003
 
  stats.records= 0;
1004
 
  records_is_known= 0;
1005
 
  chain.clear();
1006
 
 
1007
 
  blobroot.init_alloc_root(BLOB_MEMROOT_ALLOC_SIZE);
1008
 
 
1009
 
  return(0);
1010
 
}
1011
 
 
1012
 
/*
1013
 
  ::rnd_next() does all the heavy lifting for a table scan. You will need to
1014
 
  populate *buf with the correct field data. You can walk the field to
1015
 
  determine at what position you should store the data (take a look at how
1016
 
  ::find_current_row() works). The structure is something like:
1017
 
  0Foo  Dog  Friend
1018
 
  The first offset is for the first attribute. All space before that is
1019
 
  reserved for null count.
1020
 
  Basically this works as a mask for which rows are nulled (compared to just
1021
 
  empty).
1022
 
  This table Cursor doesn't do nulls and does not know the difference between
1023
 
  NULL and "". This is ok since this table Cursor is for spreadsheets and
1024
 
  they don't know about them either :)
1025
 
*/
1026
 
int ha_tina::rnd_next(unsigned char *buf)
1027
 
{
1028
 
  int rc;
1029
 
 
1030
 
  if (share->crashed)
1031
 
      return(HA_ERR_CRASHED_ON_USAGE);
1032
 
 
1033
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
1034
 
 
1035
 
  current_position= next_position;
1036
 
 
1037
 
  /* don't scan an empty file */
1038
 
  if (!local_saved_data_file_length)
1039
 
    return(HA_ERR_END_OF_FILE);
1040
 
 
1041
 
  if ((rc= find_current_row(buf)))
1042
 
    return(rc);
1043
 
 
1044
 
  stats.records++;
1045
 
  return(0);
1046
 
}
1047
 
 
1048
 
/*
1049
 
  In the case of an order by rows will need to be sorted.
1050
 
  ::position() is called after each call to ::rnd_next(),
1051
 
  the data it stores is to a byte array. You can store this
1052
 
  data via my_store_ptr(). ref_length is a variable defined to the
1053
 
  class that is the sizeof() of position being stored. In our case
1054
 
  its just a position. Look at the bdb code if you want to see a case
1055
 
  where something other then a number is stored.
1056
 
*/
1057
 
void ha_tina::position(const unsigned char *)
1058
 
{
1059
 
  internal::my_store_ptr(ref, ref_length, current_position);
1060
 
  return;
1061
 
}
1062
 
 
1063
 
 
1064
 
/*
1065
 
  Used to fetch a row from a posiion stored with ::position().
1066
 
  internal::my_get_ptr() retrieves the data for you.
1067
 
*/
1068
 
 
1069
 
int ha_tina::rnd_pos(unsigned char * buf, unsigned char *pos)
1070
 
{
1071
 
  ha_statistic_increment(&system_status_var::ha_read_rnd_count);
1072
 
  current_position= (off_t)internal::my_get_ptr(pos,ref_length);
1073
 
  return(find_current_row(buf));
1074
 
}
1075
 
 
1076
 
/*
1077
 
  ::info() is used to return information to the optimizer.
1078
 
  Currently this table Cursor doesn't implement most of the fields
1079
 
  really needed. SHOW also makes use of this data
1080
 
*/
1081
 
int ha_tina::info(uint32_t)
1082
 
{
1083
 
  /* This is a lie, but you don't want the optimizer to see zero or 1 */
1084
 
  if (!records_is_known && stats.records < 2)
1085
 
    stats.records= 2;
1086
 
  return(0);
1087
 
}
1088
 
 
1089
 
/*
1090
 
  Set end_pos to the last valid byte of continuous area, closest
1091
 
  to the given "hole", stored in the buffer. "Valid" here means,
1092
 
  not listed in the chain of deleted records ("holes").
1093
 
*/
1094
 
bool ha_tina::get_write_pos(off_t *end_pos, vector< pair<off_t, off_t> >::iterator &closest_hole)
1095
 
{
1096
 
  if (closest_hole == chain.end()) /* no more chains */
1097
 
    *end_pos= file_buff->end();
1098
 
  else
1099
 
    *end_pos= std::min(file_buff->end(),
1100
 
                       closest_hole->first);
1101
 
  return (closest_hole != chain.end()) && (*end_pos == closest_hole->first);
1102
 
}
1103
 
 
1104
 
 
1105
 
/*
1106
 
  Called after each table scan. In particular after deletes,
1107
 
  and updates. In the last case we employ chain of deleted
1108
 
  slots to clean up all of the dead space we have collected while
1109
 
  performing deletes/updates.
1110
 
*/
1111
 
int ha_tina::doEndTableScan()
1112
 
{
1113
 
  off_t file_buffer_start= 0;
1114
 
 
1115
 
  blobroot.free_root(MYF(0));
1116
 
  records_is_known= 1;
1117
 
 
1118
 
  if (chain.size() > 0)
1119
 
  {
1120
 
    vector< pair<off_t, off_t> >::iterator ptr= chain.begin();
1121
 
 
1122
 
    /*
1123
 
      Re-read the beginning of a file (as the buffer should point to the
1124
 
      end of file after the scan).
1125
 
    */
1126
 
    file_buff->init_buff(data_file);
1127
 
 
1128
 
    /*
1129
 
      The sort is needed when there were updates/deletes with random orders.
1130
 
      It sorts so that we move the firts blocks to the beginning.
1131
 
    */
1132
 
    sort(chain.begin(), chain.end());
1133
 
 
1134
 
    off_t write_begin= 0, write_end;
1135
 
 
1136
 
    /* create the file to write updated table if it wasn't yet created */
1137
 
    if (open_update_temp_file_if_needed())
1138
 
      return(-1);
1139
 
 
1140
 
    /* write the file with updated info */
1141
 
    while ((file_buffer_start != -1))     // while not end of file
1142
 
    {
1143
 
      bool in_hole= get_write_pos(&write_end, ptr);
1144
 
      off_t write_length= write_end - write_begin;
1145
 
      if ((uint64_t)write_length > SIZE_MAX)
1146
 
      {
1147
 
        goto error;
1148
 
      }
1149
 
 
1150
 
      /* if there is something to write, write it */
1151
 
      if (write_length)
1152
 
      {
1153
 
        if (internal::my_write(update_temp_file,
1154
 
                     (unsigned char*) (file_buff->ptr() +
1155
 
                               (write_begin - file_buff->start())),
1156
 
                     (size_t)write_length, MYF_RW))
1157
 
          goto error;
1158
 
        temp_file_length+= write_length;
1159
 
      }
1160
 
      if (in_hole)
1161
 
      {
1162
 
        /* skip hole */
1163
 
        while (file_buff->end() <= ptr->second && file_buffer_start != -1)
1164
 
          file_buffer_start= file_buff->read_next();
1165
 
        write_begin= ptr->second;
1166
 
        ++ptr;
1167
 
      }
1168
 
      else
1169
 
        write_begin= write_end;
1170
 
 
1171
 
      if (write_end == file_buff->end())
1172
 
        file_buffer_start= file_buff->read_next(); /* shift the buffer */
1173
 
 
1174
 
    }
1175
 
 
1176
 
    if (internal::my_sync(update_temp_file, MYF(MY_WME)) ||
1177
 
        internal::my_close(update_temp_file, MYF(0)))
1178
 
      return(-1);
1179
 
 
1180
 
    share->update_file_opened= false;
1181
 
 
1182
 
    if (share->tina_write_opened)
1183
 
    {
1184
 
      if (internal::my_close(share->tina_write_filedes, MYF(0)))
1185
 
        return(-1);
1186
 
      /*
1187
 
        Mark that the writer fd is closed, so that init_tina_writer()
1188
 
        will reopen it later.
1189
 
      */
1190
 
      share->tina_write_opened= false;
1191
 
    }
1192
 
 
1193
 
    /*
1194
 
      Close opened fildes's. Then move updated file in place
1195
 
      of the old datafile.
1196
 
    */
1197
 
    std::string rename_file= share->table_name;
1198
 
    rename_file.append(CSN_EXT);
1199
 
    if (internal::my_close(data_file, MYF(0)) ||
1200
 
        internal::my_rename(rename_file.c_str(),
1201
 
                            share->data_file_name.c_str(), MYF(0)))
1202
 
      return(-1);
1203
 
 
1204
 
    /* Open the file again */
1205
 
    if (((data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1))
1206
 
      return(-1);
1207
 
    /*
1208
 
      As we reopened the data file, increase share->data_file_version
1209
 
      in order to force other threads waiting on a table lock and
1210
 
      have already opened the table to reopen the data file.
1211
 
      That makes the latest changes become visible to them.
1212
 
      Update local_data_file_version as no need to reopen it in the
1213
 
      current thread.
1214
 
    */
1215
 
    share->data_file_version++;
1216
 
    local_data_file_version= share->data_file_version;
1217
 
    /*
1218
 
      The datafile is consistent at this point and the write filedes is
1219
 
      closed, so nothing worrying will happen to it in case of a crash.
1220
 
      Here we record this fact to the meta-file.
1221
 
    */
1222
 
    (void)write_meta_file(share->meta_file, share->rows_recorded, false);
1223
 
    /*
1224
 
      Update local_saved_data_file_length with the real length of the
1225
 
      data file.
1226
 
    */
1227
 
    local_saved_data_file_length= temp_file_length;
1228
 
  }
1229
 
 
1230
 
  return(0);
1231
 
error:
1232
 
  internal::my_close(update_temp_file, MYF(0));
1233
 
  share->update_file_opened= false;
1234
 
  return(-1);
1235
 
}
1236
 
 
1237
 
 
1238
 
/*
1239
 
  DELETE without WHERE calls this
1240
 
*/
1241
 
 
1242
 
int ha_tina::delete_all_rows()
1243
 
{
1244
 
  int rc;
1245
 
 
1246
 
  if (!records_is_known)
1247
 
    return(errno=HA_ERR_WRONG_COMMAND);
1248
 
 
1249
 
  if (!share->tina_write_opened)
1250
 
    if (init_tina_writer())
1251
 
      return(-1);
1252
 
 
1253
 
  /* Truncate the file to zero size */
1254
 
  rc= ftruncate(share->tina_write_filedes, 0);
1255
 
 
1256
 
  stats.records=0;
1257
 
  /* Update shared info */
1258
 
  pthread_mutex_lock(&share->mutex);
1259
 
  share->rows_recorded= 0;
1260
 
  pthread_mutex_unlock(&share->mutex);
1261
 
  local_saved_data_file_length= 0;
1262
 
  return(rc);
1263
 
}
1264
 
 
1265
 
/*
1266
 
  Create a table. You do not want to leave the table open after a call to
1267
 
  this (the database will call ::open() if it needs to).
1268
 
*/
1269
 
 
1270
 
int Tina::doCreateTable(Session &session,
1271
 
                        Table& table_arg,
1272
 
                        const drizzled::TableIdentifier &identifier,
1273
 
                        drizzled::message::Table &create_proto)
1274
 
{
1275
 
  char name_buff[FN_REFLEN];
1276
 
  int create_file;
1277
 
 
1278
 
  /*
1279
 
    check columns
1280
 
  */
1281
 
  const drizzled::TableShare::Fields fields(table_arg.getShare()->getFields());
1282
 
  for (drizzled::TableShare::Fields::const_iterator iter= fields.begin();
1283
 
       iter != fields.end();
1284
 
       iter++)
1285
 
  {
1286
 
    if (not *iter) // Historical legacy for NULL array end.
1287
 
      continue;
1288
 
 
1289
 
    if ((*iter)->real_maybe_null())
1290
 
    {
1291
 
      my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1292
 
      return(HA_ERR_UNSUPPORTED);
1293
 
    }
1294
 
  }
1295
 
 
1296
 
 
1297
 
  if ((create_file= internal::my_create(internal::fn_format(name_buff, identifier.getPath().c_str(), "", CSM_EXT,
1298
 
                                                            MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0,
1299
 
                                        O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1300
 
    return(-1);
1301
 
 
1302
 
  write_meta_file(create_file, 0, false);
1303
 
  internal::my_close(create_file, MYF(0));
1304
 
 
1305
 
  if ((create_file= internal::my_create(internal::fn_format(name_buff, identifier.getPath().c_str(), "", CSV_EXT,
1306
 
                                                            MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
1307
 
                                        O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1308
 
    return(-1);
1309
 
 
1310
 
  internal::my_close(create_file, MYF(0));
1311
 
 
1312
 
  session.getMessageCache().storeTableMessage(identifier, create_proto);
1313
 
 
1314
 
  return 0;
1315
 
}
1316
 
 
1317
 
 
1318
 
DRIZZLE_DECLARE_PLUGIN
1319
 
{
1320
 
  DRIZZLE_VERSION_ID,
1321
 
  "CSV",
1322
 
  "1.0",
1323
 
  "Brian Aker, MySQL AB",
1324
 
  "CSV storage engine",
1325
 
  PLUGIN_LICENSE_GPL,
1326
 
  tina_init_func, /* Plugin Init */
1327
 
  NULL,                       /* system variables                */
1328
 
  NULL                        /* config options                  */
1329
 
}
1330
 
DRIZZLE_DECLARE_PLUGIN_END;
1331