~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/csv/ha_tina.cc

  • Committer: Jay Pipes
  • Date: 2008-09-11 16:03:22 UTC
  • mto: (383.5.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: jay@mysql.com-20080911160322-vrl0k1djo6q6ytv1
Removed SQL_MODE variances from comment_table.test and ensured correct error thrown when a comment that is too long was input.  After moving to proto buffer definition for table, the 2048 length will go away.

Show diffs side-by-side

added added

removed removed

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