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/error.h>
47
#include <drizzled/table.h>
48
#include <drizzled/session.h>
49
#include "drizzled/internal/my_sys.h"
61
using namespace drizzled;
64
unsigned char + unsigned char + uint64_t + uint64_t + uint64_t + uint64_t + unsigned char
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
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
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);
80
/* Stuff for shares */
81
pthread_mutex_t tina_mutex;
83
/*****************************************************************************
85
*****************************************************************************/
88
If frm_error() is called in table.cc this is called to find out what file
89
extensions exist for this Cursor.
91
static const char *ha_tina_exts[] = {
97
class Tina : public drizzled::plugin::StorageEngine
99
typedef std::map<string, TinaShare*> TinaMap;
100
TinaMap tina_open_tables;
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),
111
pthread_mutex_destroy(&tina_mutex);
114
virtual Cursor *create(Table &table)
116
return new ha_tina(*this, table);
119
const char **bas_ext() const {
123
int doCreateTable(Session &,
125
const drizzled::identifier::Table &identifier,
126
drizzled::message::Table&);
128
int doGetTableDefinition(Session& session,
129
const drizzled::identifier::Table &identifier,
130
drizzled::message::Table &table_message);
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);
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);
144
void doGetTableIdentifiers(drizzled::CachedDirectory &directory,
145
const drizzled::identifier::Schema &schema_identifier,
146
drizzled::identifier::Table::vector &set_of_identifiers);
149
void Tina::doGetTableIdentifiers(drizzled::CachedDirectory&,
150
const drizzled::identifier::Schema&,
151
drizzled::identifier::Table::vector&)
155
int Tina::doRenameTable(Session &session,
156
const drizzled::identifier::Table &from, const drizzled::identifier::Table &to)
159
for (const char **ext= bas_ext(); *ext ; ext++)
161
if (rename_file_ext(from.getPath().c_str(), to.getPath().c_str(), *ext))
163
if ((error=errno) != ENOENT)
169
session.getMessageCache().renameTableMessage(from, to);
174
bool Tina::doDoesTableExist(Session &session, const drizzled::identifier::Table &identifier)
176
return session.getMessageCache().doesTableMessageExist(identifier);
180
int Tina::doDropTable(Session &session,
181
const drizzled::identifier::Table &identifier)
184
int enoent_or_zero= ENOENT; // Error if no file was deleted
186
for (const char **ext= bas_ext(); *ext ; ext++)
188
std::string full_name= identifier.getPath();
189
full_name.append(*ext);
191
if (internal::my_delete_with_symlink(full_name.c_str(), MYF(0)))
193
if ((error= errno) != ENOENT)
198
enoent_or_zero= 0; // No error for ENOENT
200
error= enoent_or_zero;
203
session.getMessageCache().removeTableMessage(identifier);
208
TinaShare *Tina::findOpenTable(const string table_name)
210
TinaMap::iterator find_iter=
211
tina_open_tables.find(table_name);
213
if (find_iter != tina_open_tables.end())
214
return (*find_iter).second;
219
void Tina::addOpenTable(const string &table_name, TinaShare *share)
221
tina_open_tables[table_name]= share;
224
void Tina::deleteOpenTable(const string &table_name)
226
tina_open_tables.erase(table_name);
230
int Tina::doGetTableDefinition(Session &session,
231
const drizzled::identifier::Table &identifier,
232
drizzled::message::Table &table_message)
234
if (session.getMessageCache().getTableMessage(identifier, table_message))
241
static Tina *tina_engine= NULL;
243
static int tina_init_func(drizzled::module::Context &context)
246
tina_engine= new Tina("CSV");
247
context.add(tina_engine);
249
pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST);
255
TinaShare::TinaShare(const std::string &table_name_arg) :
256
table_name(table_name_arg),
257
data_file_name(table_name_arg),
259
saved_data_file_length(0),
260
update_file_opened(false),
261
tina_write_opened(false),
266
data_file_name.append(CSV_EXT);
269
TinaShare::~TinaShare()
271
pthread_mutex_destroy(&mutex);
275
Simple lock controls.
277
TinaShare *ha_tina::get_share(const std::string &table_name)
279
pthread_mutex_lock(&tina_mutex);
281
Tina *a_tina= static_cast<Tina *>(getEngine());
282
share= a_tina->findOpenTable(table_name);
284
std::string meta_file_name;
285
struct stat file_stat;
288
If share is not present in the hash, create a new share and
289
initialize its members.
293
share= new TinaShare(table_name);
297
pthread_mutex_unlock(&tina_mutex);
301
meta_file_name.assign(table_name);
302
meta_file_name.append(CSM_EXT);
304
if (stat(share->data_file_name.c_str(), &file_stat))
306
pthread_mutex_unlock(&tina_mutex);
311
share->saved_data_file_length= file_stat.st_size;
313
a_tina->addOpenTable(share->table_name, share);
315
pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
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.
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;
328
If the meta file will not open we assume it is crashed and
331
if (read_meta_file(share->meta_file, &share->rows_recorded))
332
share->crashed= true;
335
pthread_mutex_unlock(&tina_mutex);
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.
352
Read the meta-file info. For now we are only interested in
353
rows counf, crashed bit and magic number.
357
non-zero - error occurred
360
static int read_meta_file(int meta_file, ha_rows *rows)
362
unsigned char meta_buffer[META_BUFFER_SIZE];
363
unsigned char *ptr= meta_buffer;
365
lseek(meta_file, 0, SEEK_SET);
366
if (internal::my_read(meta_file, (unsigned char*)meta_buffer, META_BUFFER_SIZE, 0)
368
return(HA_ERR_CRASHED_ON_USAGE);
371
Parse out the meta data, we ignore version at the moment
374
ptr+= sizeof(unsigned char)*2; // Move past header
375
*rows= (ha_rows)uint8korr(ptr);
376
ptr+= sizeof(uint64_t); // Move past rows
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.
381
ptr+= 3*sizeof(uint64_t);
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);
388
internal::my_sync(meta_file, MYF(MY_WME));
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
405
Write meta-info the the file. Only rows count, crashed bit and
406
magic number matter now.
410
non-zero - error occurred
413
static int write_meta_file(int meta_file, ha_rows rows, bool dirty)
415
unsigned char meta_buffer[META_BUFFER_SIZE];
416
unsigned char *ptr= meta_buffer;
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));
426
Skip over checkpoint, autoincrement and forced_flushes fields.
427
We'll need them later.
429
ptr+= 3*sizeof(uint64_t);
430
*ptr= (unsigned char)dirty;
432
lseek(meta_file, 0, SEEK_SET);
433
if (internal::my_write(meta_file, (unsigned char *)meta_buffer, META_BUFFER_SIZE, 0)
437
internal::my_sync(meta_file, MYF(MY_WME));
442
int ha_tina::init_tina_writer()
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.
449
(void)write_meta_file(share->meta_file, share->rows_recorded, true);
451
if ((share->tina_write_filedes=
452
internal::my_open(share->data_file_name.c_str(), O_RDWR|O_APPEND, MYF(0))) == -1)
454
share->crashed= true;
457
share->tina_write_opened= true;
466
int ha_tina::free_share()
468
pthread_mutex_lock(&tina_mutex);
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)))
476
if (share->tina_write_opened)
478
if (internal::my_close(share->tina_write_filedes, MYF(0)))
480
share->tina_write_opened= false;
483
Tina *a_tina= static_cast<Tina *>(getEngine());
484
a_tina->deleteOpenTable(share->table_name);
487
pthread_mutex_unlock(&tina_mutex);
494
This function finds the end of a line and returns the length
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
503
static off_t find_eoln_buff(Transparent_file *data_buff, off_t begin,
504
off_t end, int *eoln_len)
508
for (off_t x= begin; x < end; x++)
510
/* Unix (includes Mac OS X) */
511
if (data_buff->get_value(x) == '\n')
514
if (data_buff->get_value(x) == '\r') // Mac or Dos
516
/* old Mac line ending */
517
if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
519
else // DOS style ending
523
if (*eoln_len) // end of line was found
532
ha_tina::ha_tina(drizzled::plugin::StorageEngine &engine_arg, Table &table_arg)
533
:Cursor(engine_arg, table_arg),
535
These definitions are found in Cursor.h
536
They are not probably completely right.
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)
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();
548
Encode a buffer into the quoted format.
551
int ha_tina::encode_quote(unsigned char *)
553
char attribute_buffer[1024];
554
String attribute(attribute_buffer, sizeof(attribute_buffer),
559
for (Field **field= getTable()->getFields() ; *field ; field++)
563
const bool was_null= (*field)->is_null();
566
assistance for backwards compatibility in production builds.
567
note: this will not work for ENUM columns.
571
(*field)->set_default();
572
(*field)->set_notnull();
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.
580
(*field)->setReadSet();
581
(*field)->val_str(&attribute,&attribute);
584
(*field)->set_null();
586
if ((*field)->str_needs_quotes())
588
ptr= attribute.ptr();
589
end_ptr= attribute.length() + ptr;
593
while (ptr < end_ptr)
601
else if (*ptr == '\r')
607
else if (*ptr == '\\')
613
else if (*ptr == '\n')
620
buffer.append(*ptr++);
626
buffer.append(attribute);
631
// Remove the comma, add a line feed
632
buffer.length(buffer.length() - 1);
635
//buffer.replace(buffer.length(), 0, "\n", 1);
637
return (buffer.length());
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.
645
int ha_tina::chain_append()
647
if (chain.size() > 0 && chain.back().second == current_position)
648
chain.back().second= next_position;
650
chain.push_back(make_pair(current_position, next_position));
658
int ha_tina::find_current_row(unsigned char *buf)
660
off_t end_offset, curr_offset= current_position;
664
blobroot.free_root(MYF(drizzled::memory::MARK_BLOCKS_FREE));
667
We do not read further then local_saved_data_file_length in order
668
not to conflict with undergoing concurrent insert.
671
find_eoln_buff(file_buff, current_position,
672
local_saved_data_file_length, &eoln_len)) == 0)
673
return(HA_ERR_END_OF_FILE);
675
error= HA_ERR_CRASHED_ON_USAGE;
677
memset(buf, 0, getTable()->getShare()->null_bytes);
679
for (Field **field= getTable()->getFields() ; *field ; field++)
684
if (curr_offset >= end_offset)
686
curr_char= file_buff->get_value(curr_offset);
687
if (curr_char == '"')
689
curr_offset++; // Incrementpast the first quote
691
for(; curr_offset < end_offset; curr_offset++)
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) == ','))
699
curr_offset+= 2; // Move past the , and the "
702
if (curr_char == '\\' && curr_offset != (end_offset - 1))
705
curr_char= file_buff->get_value(curr_offset);
706
if (curr_char == 'r')
708
else if (curr_char == 'n' )
710
else if (curr_char == '\\' || curr_char == '"')
711
buffer.append(curr_char);
712
else /* This could only happed with an externally created file */
715
buffer.append(curr_char);
718
else // ordinary symbol
721
We are at final symbol and no last quote was found =>
722
we are working with a damaged file.
724
if (curr_offset == end_offset - 1)
726
buffer.append(curr_char);
732
for(; curr_offset < end_offset; curr_offset++)
734
curr_char= file_buff->get_value(curr_offset);
735
if (curr_char == ',')
737
curr_offset++; // Skip the ,
740
buffer.append(curr_char);
744
if ((*field)->isReadSet() || (*field)->isWriteSet())
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()))
753
if ((*field)->flags & BLOB_FLAG)
755
Field_blob *blob= *(Field_blob**) field;
756
unsigned char *src, *tgt;
757
uint32_t length, packlength;
759
packlength= blob->pack_length_no_ptr();
760
length= blob->get_length(blob->ptr);
761
memcpy(&src, blob->ptr + packlength, sizeof(char*));
764
tgt= (unsigned char*) blobroot.alloc_root(length);
765
memmove(tgt, src, length);
766
memcpy(blob->ptr + packlength, &tgt, sizeof(char*));
771
next_position= end_offset + eoln_len;
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.
784
int ha_tina::doOpen(const identifier::Table &identifier, int , uint32_t )
786
if (not (share= get_share(identifier.getPath().c_str())))
792
return(HA_ERR_CRASHED_ON_USAGE);
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)
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.
804
ref_length=sizeof(off_t);
810
Close a database file. We remove ourselves from the shared strucutre.
811
If it is empty we destroy it.
813
int ha_tina::close(void)
816
rc= internal::my_close(data_file, MYF(0));
817
return(free_share() || rc);
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).
825
int ha_tina::doInsertRecord(unsigned char * buf)
830
return(HA_ERR_CRASHED_ON_USAGE);
832
size= encode_quote(buf);
834
if (!share->tina_write_opened)
835
if (init_tina_writer())
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)))
843
/* update local copy of the max position to see our own changes */
844
local_saved_data_file_length+= size;
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);
857
int ha_tina::open_update_temp_file_if_needed()
859
char updated_fname[FN_REFLEN];
861
if (!share->update_file_opened)
863
if ((update_temp_file=
864
internal::my_create(internal::fn_format(updated_fname, share->table_name.c_str(),
866
MY_REPLACE_EXT | MY_UNPACK_FILENAME),
867
0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
869
share->update_file_opened= true;
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()
883
int ha_tina::doUpdateRecord(const unsigned char *, unsigned char * new_data)
888
size= encode_quote(new_data);
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.
900
if (open_update_temp_file_if_needed())
903
if (internal::my_write(update_temp_file, (unsigned char*)buffer.ptr(), size,
904
MYF(MY_WME | MY_NABP)))
906
temp_file_length+= size;
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,
923
int ha_tina::doDeleteRecord(const unsigned char *)
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);
941
@brief Initialize the data file.
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.
950
@retval 1 There was an error.
953
int ha_tina::init_data_file()
955
if (local_data_file_version != share->data_file_version)
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)
962
file_buff->init_buff(data_file);
968
All table scans call this first.
969
The order of a table scan is:
974
ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
985
ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
987
ENUM HA_EXTRA_RESET Reset database to after open
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.
995
int ha_tina::doStartTableScan(bool)
997
/* set buffer to the beginning of the file */
998
if (share->crashed || init_data_file())
999
return(HA_ERR_CRASHED_ON_USAGE);
1001
current_position= next_position= 0;
1003
records_is_known= 0;
1006
blobroot.init_alloc_root(BLOB_MEMROOT_ALLOC_SIZE);
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:
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
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 :)
1025
int ha_tina::rnd_next(unsigned char *buf)
1030
return(HA_ERR_CRASHED_ON_USAGE);
1032
ha_statistic_increment(&system_status_var::ha_read_rnd_next_count);
1034
current_position= next_position;
1036
/* don't scan an empty file */
1037
if (!local_saved_data_file_length)
1038
return(HA_ERR_END_OF_FILE);
1040
if ((rc= find_current_row(buf)))
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.
1056
void ha_tina::position(const unsigned char *)
1058
internal::my_store_ptr(ref, ref_length, current_position);
1064
Used to fetch a row from a posiion stored with ::position().
1065
internal::my_get_ptr() retrieves the data for you.
1068
int ha_tina::rnd_pos(unsigned char * buf, unsigned char *pos)
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));
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
1080
int ha_tina::info(uint32_t)
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)
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").
1093
bool ha_tina::get_write_pos(off_t *end_pos, vector< pair<off_t, off_t> >::iterator &closest_hole)
1095
if (closest_hole == chain.end()) /* no more chains */
1096
*end_pos= file_buff->end();
1098
*end_pos= std::min(file_buff->end(),
1099
closest_hole->first);
1100
return (closest_hole != chain.end()) && (*end_pos == closest_hole->first);
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.
1110
int ha_tina::doEndTableScan()
1112
off_t file_buffer_start= 0;
1114
blobroot.free_root(MYF(0));
1115
records_is_known= 1;
1117
if (chain.size() > 0)
1119
vector< pair<off_t, off_t> >::iterator ptr= chain.begin();
1122
Re-read the beginning of a file (as the buffer should point to the
1123
end of file after the scan).
1125
file_buff->init_buff(data_file);
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.
1131
sort(chain.begin(), chain.end());
1133
off_t write_begin= 0, write_end;
1135
/* create the file to write updated table if it wasn't yet created */
1136
if (open_update_temp_file_if_needed())
1139
/* write the file with updated info */
1140
while ((file_buffer_start != -1)) // while not end of file
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)
1149
/* if there is something to write, write it */
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))
1157
temp_file_length+= write_length;
1162
while (file_buff->end() <= ptr->second && file_buffer_start != -1)
1163
file_buffer_start= file_buff->read_next();
1164
write_begin= ptr->second;
1168
write_begin= write_end;
1170
if (write_end == file_buff->end())
1171
file_buffer_start= file_buff->read_next(); /* shift the buffer */
1175
if (internal::my_sync(update_temp_file, MYF(MY_WME)) ||
1176
internal::my_close(update_temp_file, MYF(0)))
1179
share->update_file_opened= false;
1181
if (share->tina_write_opened)
1183
if (internal::my_close(share->tina_write_filedes, MYF(0)))
1186
Mark that the writer fd is closed, so that init_tina_writer()
1187
will reopen it later.
1189
share->tina_write_opened= false;
1193
Close opened fildes's. Then move updated file in place
1194
of the old datafile.
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)))
1203
/* Open the file again */
1204
if (((data_file= internal::my_open(share->data_file_name.c_str(), O_RDONLY, MYF(0))) == -1))
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
1214
share->data_file_version++;
1215
local_data_file_version= share->data_file_version;
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.
1221
(void)write_meta_file(share->meta_file, share->rows_recorded, false);
1223
Update local_saved_data_file_length with the real length of the
1226
local_saved_data_file_length= temp_file_length;
1231
internal::my_close(update_temp_file, MYF(0));
1232
share->update_file_opened= false;
1238
DELETE without WHERE calls this
1241
int ha_tina::delete_all_rows()
1245
if (!records_is_known)
1246
return(errno=HA_ERR_WRONG_COMMAND);
1248
if (!share->tina_write_opened)
1249
if (init_tina_writer())
1252
/* Truncate the file to zero size */
1253
rc= ftruncate(share->tina_write_filedes, 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;
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).
1269
int Tina::doCreateTable(Session &session,
1271
const drizzled::identifier::Table &identifier,
1272
drizzled::message::Table &create_proto)
1274
char name_buff[FN_REFLEN];
1280
const drizzled::TableShare::Fields fields(table_arg.getShare()->getFields());
1281
for (drizzled::TableShare::Fields::const_iterator iter= fields.begin();
1282
iter != fields.end();
1285
if (not *iter) // Historical legacy for NULL array end.
1288
if ((*iter)->real_maybe_null())
1290
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1291
return(HA_ERR_UNSUPPORTED);
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)
1301
write_meta_file(create_file, 0, false);
1302
internal::my_close(create_file, MYF(0));
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)
1309
internal::my_close(create_file, MYF(0));
1311
session.getMessageCache().storeTableMessage(identifier, create_proto);
1317
DRIZZLE_DECLARE_PLUGIN
1322
"Brian Aker, MySQL AB",
1323
"CSV storage engine",
1325
tina_init_func, /* Plugin Init */
1327
NULL /* config options */
1329
DRIZZLE_DECLARE_PLUGIN_END;