75
58
#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);
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);
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);
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,
84
78
/*****************************************************************************
86
80
*****************************************************************************/
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().
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(TableShare &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::TableIdentifiers &set_of_identifiers);
150
void Tina::doGetTableIdentifiers(drizzled::CachedDirectory&,
151
const drizzled::SchemaIdentifier&,
152
drizzled::TableIdentifiers&)
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.renameTableMessage(from, to);
175
bool Tina::doDoesTableExist(Session &session, const drizzled::TableIdentifier &identifier)
177
return session.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.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.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);
85
int sort_set (tina_set *a, tina_set *b)
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.
91
return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) );
94
static unsigned char* tina_get_key(TINA_SHARE *share, size_t *length,
95
bool not_used __attribute__((unused)))
97
*length=share->table_name_length;
98
return (unsigned char*) share->table_name;
101
static int tina_init_func(void *p)
103
handlerton *tina_hton;
105
tina_hton= (handlerton *)p;
250
106
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);
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->create= tina_create_handler;
111
tina_hton->flags= (HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES |
116
static int tina_done_func(void *p __attribute__((unused)))
118
hash_free(&tina_open_tables);
119
pthread_mutex_destroy(&tina_mutex);
276
126
Simple lock controls.
278
TinaShare *ha_tina::get_share(const std::string &table_name)
128
static TINA_SHARE *get_share(const char *table_name,
129
Table *table __attribute__((unused)))
280
pthread_mutex_lock(&tina_mutex);
282
Tina *a_tina= static_cast<Tina *>(engine);
283
share= a_tina->findOpenTable(table_name);
285
std::string meta_file_name;
132
char meta_file_name[FN_REFLEN];
286
133
struct stat file_stat;
137
pthread_mutex_lock(&tina_mutex);
138
length=(uint) strlen(table_name);
289
141
If share is not present in the hash, create a new share and
290
142
initialize its members.
144
if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
145
(unsigned char*) table_name,
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);
148
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
149
&share, sizeof(*share),
153
pthread_mutex_unlock(&tina_mutex);
158
share->table_name_length= length;
159
share->table_name= tmp_name;
160
share->crashed= false;
161
share->rows_recorded= 0;
162
share->update_file_opened= false;
163
share->tina_write_opened= false;
164
share->data_file_version= 0;
165
my_stpcpy(share->table_name, table_name);
166
fn_format(share->data_file_name, table_name, "", CSV_EXT,
167
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
168
fn_format(meta_file_name, table_name, "", CSM_EXT,
169
MY_REPLACE_EXT|MY_UNPACK_FILENAME);
171
if (stat(share->data_file_name, &file_stat))
312
173
share->saved_data_file_length= file_stat.st_size;
314
a_tina->addOpenTable(share->table_name, share);
175
if (my_hash_insert(&tina_open_tables, (unsigned char*) share))
177
thr_lock_init(&share->lock);
316
178
pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
694
If frm_error() is called in table.cc this is called to find out what file
695
extensions exist for this handler.
697
static const char *ha_tina_exts[] = {
703
const char **ha_tina::bas_ext() const
709
Three functions below are needed to enable concurrent insert functionality
710
for CSV engine. For more details see mysys/thr_lock.c
713
void tina_get_status(void* param,
714
int concurrent_insert __attribute__((unused)))
716
ha_tina *tina= (ha_tina*) param;
720
void tina_update_status(void* param)
722
ha_tina *tina= (ha_tina*) param;
723
tina->update_status();
726
/* this should exist and return 0 for concurrent insert to work */
727
bool tina_check_status(void* param __attribute__((unused)))
733
Save the state of the table
739
This function is used to retrieve the file length. During the lock
740
phase of concurrent insert. For more details see comment to
741
ha_tina::update_status below.
744
void ha_tina::get_status()
746
local_saved_data_file_length= share->saved_data_file_length;
751
Correct the state of the table. Called by unlock routines
752
before the write lock is released.
758
When we employ concurrent insert lock, we save current length of the file
759
during the lock phase. We do not read further saved value, as we don't
760
want to interfere with undergoing concurrent insert. Writers update file
761
length info during unlock with update_status().
764
For log tables concurrent insert works different. The reason is that
765
log tables are always opened and locked. And as they do not unlock
766
tables, the file length after writes should be updated in a different
770
void ha_tina::update_status()
772
/* correct local_saved_data_file_length for writers */
773
share->saved_data_file_length= local_saved_data_file_length;
780
778
Open a database file. Keep in mind that tables are caches, so
781
779
this will not be called for every request. Any sort of positions
782
780
that need to be reset should be kept in the ::extra() call.
784
int ha_tina::doOpen(const TableIdentifier &identifier, int , uint32_t )
782
int ha_tina::open(const char *name, int mode __attribute__((unused)),
783
uint32_t open_options)
786
if (not (share= get_share(identifier.getPath().c_str())))
785
if (!(share= get_share(name, table)))
786
return(HA_ERR_OUT_OF_MEM);
788
if (share->crashed && !(open_options & HA_OPEN_FOR_REPAIR))
792
791
return(HA_ERR_CRASHED_ON_USAGE);
795
794
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)
795
if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)
800
Init locking. Pass Cursor object to the locking routines,
799
Init locking. Pass handler object to the locking routines,
801
800
so that they could save/update local_saved_data_file_length value
802
801
during locking. This is needed to enable concurrent inserts.
803
thr_lock_data_init(&share->lock, &lock, (void*) this);
804
804
ref_length=sizeof(off_t);
806
share->lock.get_status= tina_get_status;
807
share->lock.update_status= tina_update_status;
808
share->lock.check_status= tina_check_status;
810
815
Close a database file. We remove ourselves from the shared strucutre.
811
816
If it is empty we destroy it.
1231
internal::my_close(update_temp_file, MYF(0));
1248
my_close(update_temp_file, MYF(0));
1232
1249
share->update_file_opened= false;
1255
Repair CSV table in the case, it is crashed.
1259
thd The thread, performing repair
1260
check_opt The options for repair. We do not use it currently.
1263
If the file is empty, change # of rows in the file and complete recovery.
1264
Otherwise, scan the table looking for bad rows. If none were found,
1265
we mark file as a good one and return. If a bad row was encountered,
1266
we truncate the datafile up to the last good row.
1268
TODO: Make repair more clever - it should try to recover subsequent
1269
rows (after the first bad one) as well.
1272
int ha_tina::repair(THD* thd,
1273
HA_CHECK_OPT* check_opt __attribute__((unused)))
1275
char repaired_fname[FN_REFLEN];
1279
ha_rows rows_repaired= 0;
1280
off_t write_begin= 0, write_end;
1283
if (!share->saved_data_file_length)
1285
share->rows_recorded= 0;
1289
/* Don't assert in field::val() functions */
1290
table->use_all_columns();
1291
if (!(buf= (unsigned char*) my_malloc(table->s->reclength, MYF(MY_WME))))
1292
return(HA_ERR_OUT_OF_MEM);
1294
/* position buffer to the start of the file */
1295
if (init_data_file())
1296
return(HA_ERR_CRASHED_ON_REPAIR);
1299
Local_saved_data_file_length is initialized during the lock phase.
1300
Sometimes this is not getting executed before ::repair (e.g. for
1301
the log tables). We set it manually here.
1303
local_saved_data_file_length= share->saved_data_file_length;
1304
/* set current position to the beginning of the file */
1305
current_position= next_position= 0;
1307
init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1309
/* Read the file row-by-row. If everything is ok, repair is not needed. */
1310
while (!(rc= find_current_row(buf)))
1312
thd_inc_row_count(thd);
1314
current_position= next_position;
1317
free_root(&blobroot, MYF(0));
1321
if (rc == HA_ERR_END_OF_FILE)
1324
All rows were read ok until end of file, the file does not need repair.
1325
If rows_recorded != rows_repaired, we should update rows_recorded value
1326
to the current amount of rows.
1328
share->rows_recorded= rows_repaired;
1333
Otherwise we've encountered a bad row => repair is needed.
1334
Let us create a temporary file.
1336
if ((repair_file= my_create(fn_format(repaired_fname, share->table_name,
1338
MY_REPLACE_EXT|MY_UNPACK_FILENAME),
1339
0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1340
return(HA_ERR_CRASHED_ON_REPAIR);
1342
file_buff->init_buff(data_file);
1345
/* we just truncated the file up to the first bad row. update rows count. */
1346
share->rows_recorded= rows_repaired;
1348
/* write repaired file */
1351
write_end= std::min(file_buff->end(), current_position);
1352
if ((write_end - write_begin) &&
1353
(my_write(repair_file, (unsigned char*)file_buff->ptr(),
1354
write_end - write_begin, MYF_RW)))
1357
write_begin= write_end;
1358
if (write_end== current_position)
1361
file_buff->read_next(); /* shift the buffer */
1365
Close the files and rename repaired file to the datafile.
1366
We have to close the files, as on Windows one cannot rename
1367
a file, which descriptor is still open. EACCES will be returned
1368
when trying to delete the "to"-file in my_rename().
1370
if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) ||
1371
my_rename(repaired_fname, share->data_file_name, MYF(0)))
1374
/* Open the file again, it should now be repaired */
1375
if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
1379
/* Set new file size. The file size will be updated by ::update_status() */
1380
local_saved_data_file_length= (size_t) current_position;
1383
share->crashed= false;
1384
return(HA_ADMIN_OK);
1238
1388
DELETE without WHERE calls this
1415
Called by the database to lock the table. Keep in mind that this
1416
is an internal lock.
1418
THR_LOCK_DATA **ha_tina::store_lock(THD *thd __attribute__((unused)),
1420
enum thr_lock_type lock_type)
1422
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1423
lock.type=lock_type;
1265
1429
Create a table. You do not want to leave the table open after a call to
1266
1430
this (the database will call ::open() if it needs to).
1269
int Tina::doCreateTable(Session &session,
1271
const drizzled::TableIdentifier &identifier,
1272
drizzled::message::Table &create_proto)
1433
int ha_tina::create(const char *name, Table *table_arg,
1434
HA_CREATE_INFO *create_info __attribute__((unused)))
1274
1436
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();
1442
for (Field **field= table_arg->s->field; *field; field++)
1285
if (not *iter) // Historical legacy for NULL array end.
1288
if ((*iter)->real_maybe_null())
1444
if ((*field)->real_maybe_null())
1290
1446
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1291
1447
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)
1452
if ((create_file= my_create(fn_format(name_buff, name, "", CSM_EXT,
1453
MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0,
1454
O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1301
1457
write_meta_file(create_file, 0, false);
1302
internal::my_close(create_file, MYF(0));
1458
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)
1460
if ((create_file= my_create(fn_format(name_buff, name, "", CSV_EXT,
1461
MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
1462
O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1309
internal::my_close(create_file, MYF(0));
1311
session.storeTableMessage(identifier, create_proto);
1317
DRIZZLE_DECLARE_PLUGIN
1465
my_close(create_file, MYF(0));
1470
int ha_tina::check(THD* thd,
1471
HA_CHECK_OPT* check_opt __attribute__((unused)))
1475
const char *old_proc_info;
1476
ha_rows count= share->rows_recorded;
1478
old_proc_info= get_thd_proc_info(thd);
1479
set_thd_proc_info(thd, "Checking table");
1480
if (!(buf= (unsigned char*) my_malloc(table->s->reclength, MYF(MY_WME))))
1481
return(HA_ERR_OUT_OF_MEM);
1483
/* position buffer to the start of the file */
1484
if (init_data_file())
1485
return(HA_ERR_CRASHED);
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
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;
1496
init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1498
/* Read the file row-by-row. If everything is ok, repair is not needed. */
1499
while (!(rc= find_current_row(buf)))
1501
thd_inc_row_count(thd);
1503
current_position= next_position;
1506
free_root(&blobroot, MYF(0));
1509
set_thd_proc_info(thd, old_proc_info);
1511
if ((rc != HA_ERR_END_OF_FILE) || count)
1513
share->crashed= true;
1514
return(HA_ADMIN_CORRUPT);
1517
return(HA_ADMIN_OK);
1521
bool ha_tina::check_if_incompatible_data(HA_CREATE_INFO *info __attribute__((unused)),
1522
uint32_t table_changes __attribute__((unused)))
1524
return COMPATIBLE_DATA_YES;
1527
mysql_declare_plugin(csv)
1529
DRIZZLE_STORAGE_ENGINE_PLUGIN,
1322
1532
"Brian Aker, MySQL AB",
1323
1533
"CSV storage engine",
1324
1534
PLUGIN_LICENSE_GPL,
1325
1535
tina_init_func, /* Plugin Init */
1536
tina_done_func, /* Plugin Deinit */
1537
NULL, /* status variables */
1326
1538
NULL, /* system variables */
1327
1539
NULL /* config options */
1329
DRIZZLE_DECLARE_PLUGIN_END;
1541
mysql_declare_plugin_end;