1
/* Copyright (C) 2003 MySQL AB
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.
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.
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 */
17
Make sure to look at ha_tina.h for more details.
19
First off, this is a play thing for me, there are a number of things
21
*) It was designed for csv and therefore its performance is highly
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
30
*) Less design, more "make it work"
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.
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.
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"
62
using namespace drizzled;
65
unsigned char + unsigned char + uint64_t + uint64_t + uint64_t + uint64_t + unsigned char
67
#define META_BUFFER_SIZE sizeof(unsigned char) + sizeof(unsigned char) + sizeof(uint64_t) \
68
+ sizeof(uint64_t) + sizeof(uint64_t) + sizeof(uint64_t) + sizeof(unsigned char)
69
#define TINA_CHECK_HEADER 254 // The number we use to determine corruption
70
#define BLOB_MEMROOT_ALLOC_SIZE 8192
72
/* The file extension */
73
#define CSV_EXT ".CSV" // The data file
74
#define CSN_EXT ".CSN" // Files used during repair and update
75
#define CSM_EXT ".CSM" // Meta file
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);
81
/* Stuff for shares */
82
pthread_mutex_t tina_mutex;
84
/*****************************************************************************
86
*****************************************************************************/
89
If frm_error() is called in table.cc this is called to find out what file
90
extensions exist for this Cursor.
92
static const char *ha_tina_exts[] = {
98
class Tina : public drizzled::plugin::StorageEngine
100
typedef std::map<string, TinaShare*> TinaMap;
101
TinaMap tina_open_tables;
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),
112
pthread_mutex_destroy(&tina_mutex);
115
virtual Cursor *create(Table &table)
117
return new ha_tina(*this, table);
120
const char **bas_ext() const {
124
int doCreateTable(Session &,
126
const drizzled::TableIdentifier &identifier,
127
drizzled::message::Table&);
129
int doGetTableDefinition(Session& session,
130
const drizzled::TableIdentifier &identifier,
131
drizzled::message::Table &table_message);
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);
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);
145
void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
146
const drizzled::SchemaIdentifier &schema_identifier,
147
drizzled::TableIdentifier::vector &set_of_identifiers);
150
void Tina::doGetTableIdentifiers(drizzled::CachedDirectory&,
151
const drizzled::SchemaIdentifier&,
152
drizzled::TableIdentifier::vector&)
156
int Tina::doRenameTable(Session &session,
157
const drizzled::TableIdentifier &from, const drizzled::TableIdentifier &to)
160
for (const char **ext= bas_ext(); *ext ; ext++)
162
if (rename_file_ext(from.getPath().c_str(), to.getPath().c_str(), *ext))
164
if ((error=errno) != ENOENT)
170
session.getMessageCache().renameTableMessage(from, to);
175
bool Tina::doDoesTableExist(Session &session, const drizzled::TableIdentifier &identifier)
177
return session.getMessageCache().doesTableMessageExist(identifier);
181
int Tina::doDropTable(Session &session,
182
const drizzled::TableIdentifier &identifier)
185
int enoent_or_zero= ENOENT; // Error if no file was deleted
187
for (const char **ext= bas_ext(); *ext ; ext++)
189
std::string full_name= identifier.getPath();
190
full_name.append(*ext);
192
if (internal::my_delete_with_symlink(full_name.c_str(), MYF(0)))
194
if ((error= errno) != ENOENT)
199
enoent_or_zero= 0; // No error for ENOENT
201
error= enoent_or_zero;
204
session.getMessageCache().removeTableMessage(identifier);
209
TinaShare *Tina::findOpenTable(const string table_name)
211
TinaMap::iterator find_iter=
212
tina_open_tables.find(table_name);
214
if (find_iter != tina_open_tables.end())
215
return (*find_iter).second;
220
void Tina::addOpenTable(const string &table_name, TinaShare *share)
222
tina_open_tables[table_name]= share;
225
void Tina::deleteOpenTable(const string &table_name)
227
tina_open_tables.erase(table_name);
231
int Tina::doGetTableDefinition(Session &session,
232
const drizzled::TableIdentifier &identifier,
233
drizzled::message::Table &table_message)
235
if (session.getMessageCache().getTableMessage(identifier, table_message))
242
static Tina *tina_engine= NULL;
244
static int tina_init_func(drizzled::module::Context &context)
247
tina_engine= new Tina("CSV");
248
context.add(tina_engine);
250
pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST);
256
TinaShare::TinaShare(const std::string &table_name_arg) :
257
table_name(table_name_arg),
258
data_file_name(table_name_arg),
260
saved_data_file_length(0),
261
update_file_opened(false),
262
tina_write_opened(false),
267
data_file_name.append(CSV_EXT);
270
TinaShare::~TinaShare()
272
pthread_mutex_destroy(&mutex);
276
Simple lock controls.
278
TinaShare *ha_tina::get_share(const std::string &table_name)
280
pthread_mutex_lock(&tina_mutex);
282
Tina *a_tina= static_cast<Tina *>(getEngine());
283
share= a_tina->findOpenTable(table_name);
285
std::string meta_file_name;
286
struct stat file_stat;
289
If share is not present in the hash, create a new share and
290
initialize its members.
294
share= new TinaShare(table_name);
298
pthread_mutex_unlock(&tina_mutex);
302
meta_file_name.assign(table_name);
303
meta_file_name.append(CSM_EXT);
305
if (stat(share->data_file_name.c_str(), &file_stat))
307
pthread_mutex_unlock(&tina_mutex);
312
share->saved_data_file_length= file_stat.st_size;
314
a_tina->addOpenTable(share->table_name, share);
316
pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
319
Open or create the meta file. In the latter case, we'll get
320
an error during read_meta_file and mark the table as crashed.
321
Usually this will result in auto-repair, and we will get a good
322
meta-file in the end.
324
if ((share->meta_file= internal::my_open(meta_file_name.c_str(),
325
O_RDWR|O_CREAT, MYF(0))) == -1)
326
share->crashed= true;
329
If the meta file will not open we assume it is crashed and
332
if (read_meta_file(share->meta_file, &share->rows_recorded))
333
share->crashed= true;
336
pthread_mutex_unlock(&tina_mutex);
347
meta_file The meta-file filedes
348
ha_rows Pointer to the var we use to store rows count.
349
These are read from the meta-file.
353
Read the meta-file info. For now we are only interested in
354
rows counf, crashed bit and magic number.
358
non-zero - error occurred
361
static int read_meta_file(int meta_file, ha_rows *rows)
363
unsigned char meta_buffer[META_BUFFER_SIZE];
364
unsigned char *ptr= meta_buffer;
366
lseek(meta_file, 0, SEEK_SET);
367
if (internal::my_read(meta_file, (unsigned char*)meta_buffer, META_BUFFER_SIZE, 0)
369
return(HA_ERR_CRASHED_ON_USAGE);
372
Parse out the meta data, we ignore version at the moment
375
ptr+= sizeof(unsigned char)*2; // Move past header
376
*rows= (ha_rows)uint8korr(ptr);
377
ptr+= sizeof(uint64_t); // Move past rows
379
Move past check_point, auto_increment and forced_flushes fields.
380
They are present in the format, but we do not use them yet.
382
ptr+= 3*sizeof(uint64_t);
384
/* check crashed bit and magic number */
385
if ((meta_buffer[0] != (unsigned char)TINA_CHECK_HEADER) ||
386
((bool)(*ptr)== true))
387
return(HA_ERR_CRASHED_ON_USAGE);
389
internal::my_sync(meta_file, MYF(MY_WME));
400
meta_file The meta-file filedes
401
ha_rows The number of rows we have in the datafile.
402
dirty A flag, which marks whether we have a corrupt table
406
Write meta-info the the file. Only rows count, crashed bit and
407
magic number matter now.
411
non-zero - error occurred
414
static int write_meta_file(int meta_file, ha_rows rows, bool dirty)
416
unsigned char meta_buffer[META_BUFFER_SIZE];
417
unsigned char *ptr= meta_buffer;
419
*ptr= (unsigned char)TINA_CHECK_HEADER;
420
ptr+= sizeof(unsigned char);
421
*ptr= (unsigned char)TINA_VERSION;
422
ptr+= sizeof(unsigned char);
423
int8store(ptr, (uint64_t)rows);
424
ptr+= sizeof(uint64_t);
425
memset(ptr, 0, 3*sizeof(uint64_t));
427
Skip over checkpoint, autoincrement and forced_flushes fields.
428
We'll need them later.
430
ptr+= 3*sizeof(uint64_t);
431
*ptr= (unsigned char)dirty;
433
lseek(meta_file, 0, SEEK_SET);
434
if (internal::my_write(meta_file, (unsigned char *)meta_buffer, META_BUFFER_SIZE, 0)
438
internal::my_sync(meta_file, MYF(MY_WME));
443
int ha_tina::init_tina_writer()
446
Mark the file as crashed. We will set the flag back when we close
447
the file. In the case of the crash it will remain marked crashed,
448
which enforce recovery.
450
(void)write_meta_file(share->meta_file, share->rows_recorded, true);
452
if ((share->tina_write_filedes=
453
internal::my_open(share->data_file_name.c_str(), O_RDWR|O_APPEND, MYF(0))) == -1)
455
share->crashed= true;
458
share->tina_write_opened= true;
467
int ha_tina::free_share()
469
pthread_mutex_lock(&tina_mutex);
471
if (!--share->use_count){
472
/* Write the meta file. Mark it as crashed if needed. */
473
(void)write_meta_file(share->meta_file, share->rows_recorded,
474
share->crashed ? true :false);
475
if (internal::my_close(share->meta_file, MYF(0)))
477
if (share->tina_write_opened)
479
if (internal::my_close(share->tina_write_filedes, MYF(0)))
481
share->tina_write_opened= false;
484
Tina *a_tina= static_cast<Tina *>(getEngine());
485
a_tina->deleteOpenTable(share->table_name);
488
pthread_mutex_unlock(&tina_mutex);
495
This function finds the end of a line and returns the length
498
We support three kinds of line endings:
499
'\r' -- Old Mac OS line ending
500
'\n' -- Traditional Unix and Mac OS X line ending
501
'\r''\n' -- DOS\Windows line ending
504
static off_t find_eoln_buff(Transparent_file *data_buff, off_t begin,
505
off_t end, int *eoln_len)
509
for (off_t x= begin; x < end; x++)
511
/* Unix (includes Mac OS X) */
512
if (data_buff->get_value(x) == '\n')
515
if (data_buff->get_value(x) == '\r') // Mac or Dos
517
/* old Mac line ending */
518
if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
520
else // DOS style ending
524
if (*eoln_len) // end of line was found
533
ha_tina::ha_tina(drizzled::plugin::StorageEngine &engine_arg, Table &table_arg)
534
:Cursor(engine_arg, table_arg),
536
These definitions are found in Cursor.h
537
They are not probably completely right.
539
current_position(0), next_position(0), local_saved_data_file_length(0),
540
file_buff(0), local_data_file_version(0), records_is_known(0)
542
/* Set our original buffers from pre-allocated memory */
543
buffer.set((char*)byte_buffer, IO_SIZE, &my_charset_bin);
544
file_buff= new Transparent_file();
549
Encode a buffer into the quoted format.
552
int ha_tina::encode_quote(unsigned char *)
554
char attribute_buffer[1024];
555
String attribute(attribute_buffer, sizeof(attribute_buffer),
560
for (Field **field= getTable()->getFields() ; *field ; field++)
564
const bool was_null= (*field)->is_null();
567
assistance for backwards compatibility in production builds.
568
note: this will not work for ENUM columns.
572
(*field)->set_default();
573
(*field)->set_notnull();
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.
581
(*field)->setReadSet();
582
(*field)->val_str(&attribute,&attribute);
585
(*field)->set_null();
587
if ((*field)->str_needs_quotes())
589
ptr= attribute.ptr();
590
end_ptr= attribute.length() + ptr;
594
while (ptr < end_ptr)
602
else if (*ptr == '\r')
608
else if (*ptr == '\\')
614
else if (*ptr == '\n')
621
buffer.append(*ptr++);
627
buffer.append(attribute);
632
// Remove the comma, add a line feed
633
buffer.length(buffer.length() - 1);
636
//buffer.replace(buffer.length(), 0, "\n", 1);
638
return (buffer.length());
642
chain_append() adds delete positions to the chain that we use to keep
643
track of space. Then the chain will be used to cleanup "holes", occurred
644
due to deletes and updates.
646
int ha_tina::chain_append()
648
if (chain.size() > 0 && chain.back().second == current_position)
649
chain.back().second= next_position;
651
chain.push_back(make_pair(current_position, next_position));
659
int ha_tina::find_current_row(unsigned char *buf)
661
off_t end_offset, curr_offset= current_position;
665
blobroot.free_root(MYF(drizzled::memory::MARK_BLOCKS_FREE));
668
We do not read further then local_saved_data_file_length in order
669
not to conflict with undergoing concurrent insert.
672
find_eoln_buff(file_buff, current_position,
673
local_saved_data_file_length, &eoln_len)) == 0)
674
return(HA_ERR_END_OF_FILE);
676
error= HA_ERR_CRASHED_ON_USAGE;
678
memset(buf, 0, getTable()->getShare()->null_bytes);
680
for (Field **field= getTable()->getFields() ; *field ; field++)
685
if (curr_offset >= end_offset)
687
curr_char= file_buff->get_value(curr_offset);
688
if (curr_char == '"')
690
curr_offset++; // Incrementpast the first quote
692
for(; curr_offset < end_offset; curr_offset++)
694
curr_char= file_buff->get_value(curr_offset);
695
// Need to convert line feeds!
696
if (curr_char == '"' &&
697
(curr_offset == end_offset - 1 ||
698
file_buff->get_value(curr_offset + 1) == ','))
700
curr_offset+= 2; // Move past the , and the "
703
if (curr_char == '\\' && curr_offset != (end_offset - 1))
706
curr_char= file_buff->get_value(curr_offset);
707
if (curr_char == 'r')
709
else if (curr_char == 'n' )
711
else if (curr_char == '\\' || curr_char == '"')
712
buffer.append(curr_char);
713
else /* This could only happed with an externally created file */
716
buffer.append(curr_char);
719
else // ordinary symbol
722
We are at final symbol and no last quote was found =>
723
we are working with a damaged file.
725
if (curr_offset == end_offset - 1)
727
buffer.append(curr_char);
733
for(; curr_offset < end_offset; curr_offset++)
735
curr_char= file_buff->get_value(curr_offset);
736
if (curr_char == ',')
738
curr_offset++; // Skip the ,
741
buffer.append(curr_char);
745
if ((*field)->isReadSet() || (*field)->isWriteSet())
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()))
754
if ((*field)->flags & BLOB_FLAG)
756
Field_blob *blob= *(Field_blob**) field;
757
unsigned char *src, *tgt;
758
uint32_t length, packlength;
760
packlength= blob->pack_length_no_ptr();
761
length= blob->get_length(blob->ptr);
762
memcpy(&src, blob->ptr + packlength, sizeof(char*));
765
tgt= (unsigned char*) blobroot.alloc_root(length);
766
memmove(tgt, src, length);
767
memcpy(blob->ptr + packlength, &tgt, sizeof(char*));
772
next_position= end_offset + eoln_len;
781
Open a database file. Keep in mind that tables are caches, so
782
this will not be called for every request. Any sort of positions
783
that need to be reset should be kept in the ::extra() call.
785
int ha_tina::doOpen(const TableIdentifier &identifier, int , uint32_t )
787
if (not (share= get_share(identifier.getPath().c_str())))
793
return(HA_ERR_CRASHED_ON_USAGE);
796
local_data_file_version= share->data_file_version;
797
if ((data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1)
801
Init locking. Pass Cursor object to the locking routines,
802
so that they could save/update local_saved_data_file_length value
803
during locking. This is needed to enable concurrent inserts.
805
ref_length=sizeof(off_t);
811
Close a database file. We remove ourselves from the shared strucutre.
812
If it is empty we destroy it.
814
int ha_tina::close(void)
817
rc= internal::my_close(data_file, MYF(0));
818
return(free_share() || rc);
822
This is an INSERT. At the moment this Cursor just seeks to the end
823
of the file and appends the data. In an error case it really should
824
just truncate to the original position (this is not done yet).
826
int ha_tina::doInsertRecord(unsigned char * buf)
831
return(HA_ERR_CRASHED_ON_USAGE);
833
size= encode_quote(buf);
835
if (!share->tina_write_opened)
836
if (init_tina_writer())
839
/* use pwrite, as concurrent reader could have changed the position */
840
if (internal::my_write(share->tina_write_filedes, (unsigned char*)buffer.ptr(), size,
841
MYF(MY_WME | MY_NABP)))
844
/* update local copy of the max position to see our own changes */
845
local_saved_data_file_length+= size;
847
/* update shared info */
848
pthread_mutex_lock(&share->mutex);
849
share->rows_recorded++;
850
/* update status for the log tables */
851
pthread_mutex_unlock(&share->mutex);
858
int ha_tina::open_update_temp_file_if_needed()
860
char updated_fname[FN_REFLEN];
862
if (!share->update_file_opened)
864
if ((update_temp_file=
865
internal::my_create(internal::fn_format(updated_fname, share->table_name.c_str(),
867
MY_REPLACE_EXT | MY_UNPACK_FILENAME),
868
0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
870
share->update_file_opened= true;
877
This is called for an update.
878
Make sure you put in code to increment the auto increment, also
879
update any timestamp data. Currently auto increment is not being
880
fixed since autoincrements have yet to be added to this table Cursor.
881
This will be called in a table scan right before the previous ::rnd_next()
884
int ha_tina::doUpdateRecord(const unsigned char *, unsigned char * new_data)
889
size= encode_quote(new_data);
892
During update we mark each updating record as deleted
893
(see the chain_append()) then write new one to the temporary data file.
894
At the end of the sequence in the doEndTableScan() we append all non-marked
895
records from the data file to the temporary data file then rename it.
896
The temp_file_length is used to calculate new data file length.
901
if (open_update_temp_file_if_needed())
904
if (internal::my_write(update_temp_file, (unsigned char*)buffer.ptr(), size,
905
MYF(MY_WME | MY_NABP)))
907
temp_file_length+= size;
916
Deletes a row. First the database will find the row, and then call this
917
method. In the case of a table scan, the previous call to this will be
918
the ::rnd_next() that found this row.
919
The exception to this is an ORDER BY. This will cause the table Cursor
920
to walk the table noting the positions of all rows that match a query.
921
The table will then be deleted/positioned based on the ORDER (so RANDOM,
924
int ha_tina::doDeleteRecord(const unsigned char *)
931
/* Update shared info */
932
assert(share->rows_recorded);
933
pthread_mutex_lock(&share->mutex);
934
share->rows_recorded--;
935
pthread_mutex_unlock(&share->mutex);
942
@brief Initialize the data file.
944
@details Compare the local version of the data file with the shared one.
945
If they differ, there are some changes behind and we have to reopen
946
the data file to make the changes visible.
947
Call @c file_buff->init_buff() at the end to read the beginning of the
948
data file into buffer.
951
@retval 1 There was an error.
954
int ha_tina::init_data_file()
956
if (local_data_file_version != share->data_file_version)
958
local_data_file_version= share->data_file_version;
959
if (internal::my_close(data_file, MYF(0)) ||
960
(data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1)
963
file_buff->init_buff(data_file);
969
All table scans call this first.
970
The order of a table scan is:
975
ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
986
ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
988
ENUM HA_EXTRA_RESET Reset database to after open
990
Each call to ::rnd_next() represents a row returned in the can. When no more
991
rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
992
The ::info() call is just for the optimizer.
996
int ha_tina::doStartTableScan(bool)
998
/* set buffer to the beginning of the file */
999
if (share->crashed || init_data_file())
1000
return(HA_ERR_CRASHED_ON_USAGE);
1002
current_position= next_position= 0;
1004
records_is_known= 0;
1007
blobroot.init_alloc_root(BLOB_MEMROOT_ALLOC_SIZE);
1013
::rnd_next() does all the heavy lifting for a table scan. You will need to
1014
populate *buf with the correct field data. You can walk the field to
1015
determine at what position you should store the data (take a look at how
1016
::find_current_row() works). The structure is something like:
1018
The first offset is for the first attribute. All space before that is
1019
reserved for null count.
1020
Basically this works as a mask for which rows are nulled (compared to just
1022
This table Cursor doesn't do nulls and does not know the difference between
1023
NULL and "". This is ok since this table Cursor is for spreadsheets and
1024
they don't know about them either :)
1026
int ha_tina::rnd_next(unsigned char *buf)
1031
return(HA_ERR_CRASHED_ON_USAGE);
1033
ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
1035
current_position= next_position;
1037
/* don't scan an empty file */
1038
if (!local_saved_data_file_length)
1039
return(HA_ERR_END_OF_FILE);
1041
if ((rc= find_current_row(buf)))
1049
In the case of an order by rows will need to be sorted.
1050
::position() is called after each call to ::rnd_next(),
1051
the data it stores is to a byte array. You can store this
1052
data via my_store_ptr(). ref_length is a variable defined to the
1053
class that is the sizeof() of position being stored. In our case
1054
its just a position. Look at the bdb code if you want to see a case
1055
where something other then a number is stored.
1057
void ha_tina::position(const unsigned char *)
1059
internal::my_store_ptr(ref, ref_length, current_position);
1065
Used to fetch a row from a posiion stored with ::position().
1066
internal::my_get_ptr() retrieves the data for you.
1069
int ha_tina::rnd_pos(unsigned char * buf, unsigned char *pos)
1071
ha_statistic_increment(&system_status_var::ha_read_rnd_count);
1072
current_position= (off_t)internal::my_get_ptr(pos,ref_length);
1073
return(find_current_row(buf));
1077
::info() is used to return information to the optimizer.
1078
Currently this table Cursor doesn't implement most of the fields
1079
really needed. SHOW also makes use of this data
1081
int ha_tina::info(uint32_t)
1083
/* This is a lie, but you don't want the optimizer to see zero or 1 */
1084
if (!records_is_known && stats.records < 2)
1090
Set end_pos to the last valid byte of continuous area, closest
1091
to the given "hole", stored in the buffer. "Valid" here means,
1092
not listed in the chain of deleted records ("holes").
1094
bool ha_tina::get_write_pos(off_t *end_pos, vector< pair<off_t, off_t> >::iterator &closest_hole)
1096
if (closest_hole == chain.end()) /* no more chains */
1097
*end_pos= file_buff->end();
1099
*end_pos= std::min(file_buff->end(),
1100
closest_hole->first);
1101
return (closest_hole != chain.end()) && (*end_pos == closest_hole->first);
1106
Called after each table scan. In particular after deletes,
1107
and updates. In the last case we employ chain of deleted
1108
slots to clean up all of the dead space we have collected while
1109
performing deletes/updates.
1111
int ha_tina::doEndTableScan()
1113
off_t file_buffer_start= 0;
1115
blobroot.free_root(MYF(0));
1116
records_is_known= 1;
1118
if (chain.size() > 0)
1120
vector< pair<off_t, off_t> >::iterator ptr= chain.begin();
1123
Re-read the beginning of a file (as the buffer should point to the
1124
end of file after the scan).
1126
file_buff->init_buff(data_file);
1129
The sort is needed when there were updates/deletes with random orders.
1130
It sorts so that we move the firts blocks to the beginning.
1132
sort(chain.begin(), chain.end());
1134
off_t write_begin= 0, write_end;
1136
/* create the file to write updated table if it wasn't yet created */
1137
if (open_update_temp_file_if_needed())
1140
/* write the file with updated info */
1141
while ((file_buffer_start != -1)) // while not end of file
1143
bool in_hole= get_write_pos(&write_end, ptr);
1144
off_t write_length= write_end - write_begin;
1145
if ((uint64_t)write_length > SIZE_MAX)
1150
/* if there is something to write, write it */
1153
if (internal::my_write(update_temp_file,
1154
(unsigned char*) (file_buff->ptr() +
1155
(write_begin - file_buff->start())),
1156
(size_t)write_length, MYF_RW))
1158
temp_file_length+= write_length;
1163
while (file_buff->end() <= ptr->second && file_buffer_start != -1)
1164
file_buffer_start= file_buff->read_next();
1165
write_begin= ptr->second;
1169
write_begin= write_end;
1171
if (write_end == file_buff->end())
1172
file_buffer_start= file_buff->read_next(); /* shift the buffer */
1176
if (internal::my_sync(update_temp_file, MYF(MY_WME)) ||
1177
internal::my_close(update_temp_file, MYF(0)))
1180
share->update_file_opened= false;
1182
if (share->tina_write_opened)
1184
if (internal::my_close(share->tina_write_filedes, MYF(0)))
1187
Mark that the writer fd is closed, so that init_tina_writer()
1188
will reopen it later.
1190
share->tina_write_opened= false;
1194
Close opened fildes's. Then move updated file in place
1195
of the old datafile.
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)))
1204
/* Open the file again */
1205
if (((data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1))
1208
As we reopened the data file, increase share->data_file_version
1209
in order to force other threads waiting on a table lock and
1210
have already opened the table to reopen the data file.
1211
That makes the latest changes become visible to them.
1212
Update local_data_file_version as no need to reopen it in the
1215
share->data_file_version++;
1216
local_data_file_version= share->data_file_version;
1218
The datafile is consistent at this point and the write filedes is
1219
closed, so nothing worrying will happen to it in case of a crash.
1220
Here we record this fact to the meta-file.
1222
(void)write_meta_file(share->meta_file, share->rows_recorded, false);
1224
Update local_saved_data_file_length with the real length of the
1227
local_saved_data_file_length= temp_file_length;
1232
internal::my_close(update_temp_file, MYF(0));
1233
share->update_file_opened= false;
1239
DELETE without WHERE calls this
1242
int ha_tina::delete_all_rows()
1246
if (!records_is_known)
1247
return(errno=HA_ERR_WRONG_COMMAND);
1249
if (!share->tina_write_opened)
1250
if (init_tina_writer())
1253
/* Truncate the file to zero size */
1254
rc= ftruncate(share->tina_write_filedes, 0);
1257
/* Update shared info */
1258
pthread_mutex_lock(&share->mutex);
1259
share->rows_recorded= 0;
1260
pthread_mutex_unlock(&share->mutex);
1261
local_saved_data_file_length= 0;
1266
Create a table. You do not want to leave the table open after a call to
1267
this (the database will call ::open() if it needs to).
1270
int Tina::doCreateTable(Session &session,
1272
const drizzled::TableIdentifier &identifier,
1273
drizzled::message::Table &create_proto)
1275
char name_buff[FN_REFLEN];
1281
const drizzled::TableShare::Fields fields(table_arg.getShare()->getFields());
1282
for (drizzled::TableShare::Fields::const_iterator iter= fields.begin();
1283
iter != fields.end();
1286
if (not *iter) // Historical legacy for NULL array end.
1289
if ((*iter)->real_maybe_null())
1291
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1292
return(HA_ERR_UNSUPPORTED);
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)
1302
write_meta_file(create_file, 0, false);
1303
internal::my_close(create_file, MYF(0));
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)
1310
internal::my_close(create_file, MYF(0));
1312
session.getMessageCache().storeTableMessage(identifier, create_proto);
1318
DRIZZLE_DECLARE_PLUGIN
1323
"Brian Aker, MySQL AB",
1324
"CSV storage engine",
1326
tina_init_func, /* Plugin Init */
1327
NULL, /* system variables */
1328
NULL /* config options */
1330
DRIZZLE_DECLARE_PLUGIN_END;