~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/csv/ha_tina.cc

  • Committer: Mark Atwood
  • Date: 2011-08-11 03:05:03 UTC
  • mfrom: (2385.1.12 refactor4)
  • Revision ID: me@mark.atwood.name-20110811030503-rp9xjihc5x3y0x4q
mergeĀ lp:~olafvdspek/drizzle/refactor4

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