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(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);
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 uchar* tina_get_key(TINA_SHARE *share, size_t *length,
95
bool not_used __attribute__((unused)))
97
*length=share->table_name_length;
98
return (uchar*) share->table_name;
101
static int tina_init_func(void *p)
103
handlerton *tina_hton;
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 |
117
static int tina_done_func(void *p __attribute__((unused)))
119
hash_free(&tina_open_tables);
120
pthread_mutex_destroy(&tina_mutex);
276
127
Simple lock controls.
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)))
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;
133
char meta_file_name[FN_REFLEN];
286
134
struct stat file_stat;
138
pthread_mutex_lock(&tina_mutex);
139
length=(uint) strlen(table_name);
289
142
If share is not present in the hash, create a new share and
290
143
initialize its members.
145
if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
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);
149
if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
150
&share, sizeof(*share),
154
pthread_mutex_unlock(&tina_mutex);
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);
172
if (stat(share->data_file_name, &file_stat))
312
174
share->saved_data_file_length= file_stat.st_size;
314
a_tina->addOpenTable(share->table_name, share);
176
if (my_hash_insert(&tina_open_tables, (uchar*) share))
178
thr_lock_init(&share->lock);
316
179
pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
695
If frm_error() is called in table.cc this is called to find out what file
696
extensions exist for this handler.
698
static const char *ha_tina_exts[] = {
704
const char **ha_tina::bas_ext() const
710
Three functions below are needed to enable concurrent insert functionality
711
for CSV engine. For more details see mysys/thr_lock.c
714
void tina_get_status(void* param,
715
int concurrent_insert __attribute__((unused)))
717
ha_tina *tina= (ha_tina*) param;
721
void tina_update_status(void* param)
723
ha_tina *tina= (ha_tina*) param;
724
tina->update_status();
727
/* this should exist and return 0 for concurrent insert to work */
728
bool tina_check_status(void* param __attribute__((unused)))
734
Save the state of the table
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.
745
void ha_tina::get_status()
747
local_saved_data_file_length= share->saved_data_file_length;
752
Correct the state of the table. Called by unlock routines
753
before the write lock is released.
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().
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
771
void ha_tina::update_status()
773
/* correct local_saved_data_file_length for writers */
774
share->saved_data_file_length= local_saved_data_file_length;
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.
785
int ha_tina::doOpen(const TableIdentifier &identifier, int , uint32_t )
783
int ha_tina::open(const char *name, int mode __attribute__((unused)),
787
if (not (share= get_share(identifier.getPath().c_str())))
786
if (!(share= get_share(name, table)))
787
return(HA_ERR_OUT_OF_MEM);
789
if (share->crashed && !(open_options & HA_OPEN_FOR_REPAIR))
793
792
return(HA_ERR_CRASHED_ON_USAGE);
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)
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
thr_lock_data_init(&share->lock, &lock, (void*) this);
805
805
ref_length=sizeof(off_t);
807
share->lock.get_status= tina_get_status;
808
share->lock.update_status= tina_update_status;
809
share->lock.check_status= tina_check_status;
811
816
Close a database file. We remove ourselves from the shared strucutre.
812
817
If it is empty we destroy it.
1232
internal::my_close(update_temp_file, MYF(0));
1249
my_close(update_temp_file, MYF(0));
1233
1250
share->update_file_opened= false;
1256
Repair CSV table in the case, it is crashed.
1260
thd The thread, performing repair
1261
check_opt The options for repair. We do not use it currently.
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.
1269
TODO: Make repair more clever - it should try to recover subsequent
1270
rows (after the first bad one) as well.
1273
int ha_tina::repair(THD* thd,
1274
HA_CHECK_OPT* check_opt __attribute__((unused)))
1276
char repaired_fname[FN_REFLEN];
1280
ha_rows rows_repaired= 0;
1281
off_t write_begin= 0, write_end;
1284
if (!share->saved_data_file_length)
1286
share->rows_recorded= 0;
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);
1295
/* position buffer to the start of the file */
1296
if (init_data_file())
1297
return(HA_ERR_CRASHED_ON_REPAIR);
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.
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;
1308
init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1310
/* Read the file row-by-row. If everything is ok, repair is not needed. */
1311
while (!(rc= find_current_row(buf)))
1313
thd_inc_row_count(thd);
1315
current_position= next_position;
1318
free_root(&blobroot, MYF(0));
1320
my_free((char*)buf, MYF(0));
1322
if (rc == HA_ERR_END_OF_FILE)
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.
1329
share->rows_recorded= rows_repaired;
1334
Otherwise we've encountered a bad row => repair is needed.
1335
Let us create a temporary file.
1337
if ((repair_file= my_create(fn_format(repaired_fname, share->table_name,
1339
MY_REPLACE_EXT|MY_UNPACK_FILENAME),
1340
0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1341
return(HA_ERR_CRASHED_ON_REPAIR);
1343
file_buff->init_buff(data_file);
1346
/* we just truncated the file up to the first bad row. update rows count. */
1347
share->rows_recorded= rows_repaired;
1349
/* write repaired file */
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)))
1358
write_begin= write_end;
1359
if (write_end== current_position)
1362
file_buff->read_next(); /* shift the buffer */
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().
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)))
1375
/* Open the file again, it should now be repaired */
1376
if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
1380
/* Set new file size. The file size will be updated by ::update_status() */
1381
local_saved_data_file_length= (size_t) current_position;
1384
share->crashed= false;
1385
return(HA_ADMIN_OK);
1239
1389
DELETE without WHERE calls this
1416
Called by the database to lock the table. Keep in mind that this
1417
is an internal lock.
1419
THR_LOCK_DATA **ha_tina::store_lock(THD *thd __attribute__((unused)),
1421
enum thr_lock_type lock_type)
1423
if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1424
lock.type=lock_type;
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).
1270
int Tina::doCreateTable(Session &session,
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)))
1275
1437
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();
1443
for (Field **field= table_arg->s->field; *field; field++)
1286
if (not *iter) // Historical legacy for NULL array end.
1289
if ((*iter)->real_maybe_null())
1445
if ((*field)->real_maybe_null())
1291
1447
my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1292
1448
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)
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)
1302
1458
write_meta_file(create_file, 0, false);
1303
internal::my_close(create_file, MYF(0));
1459
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)
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)
1310
internal::my_close(create_file, MYF(0));
1312
session.getMessageCache().storeTableMessage(identifier, create_proto);
1318
DRIZZLE_DECLARE_PLUGIN
1466
my_close(create_file, MYF(0));
1471
int ha_tina::check(THD* thd,
1472
HA_CHECK_OPT* check_opt __attribute__((unused)))
1476
const char *old_proc_info;
1477
ha_rows count= share->rows_recorded;
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);
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));
1508
my_free((char*)buf, MYF(0));
1509
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
uint table_changes __attribute__((unused)))
1524
return COMPATIBLE_DATA_YES;
1527
mysql_declare_plugin(csv)
1529
DRIZZLE_STORAGE_ENGINE_PLUGIN,
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 */
1330
DRIZZLE_DECLARE_PLUGIN_END;
1541
mysql_declare_plugin_end;