mySQL 5.0.11 sources for tomato
[tomato.git] / release / src / router / mysql / storage / csv / ha_tina.cc
blobf072803455ea079fdd7275b89a8d255ed88f0878
1 /*
2 Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 Make sure to look at ha_tina.h for more details.
21 First off, this is a play thing for me, there are a number of things
22 wrong with it:
23 *) It was designed for csv and therefore its performance is highly
24 questionable.
25 *) Indexes have not been implemented. This is because the files can
26 be traded in and out of the table directory without having to worry
27 about rebuilding anything.
28 *) NULLs and "" are treated equally (like a spreadsheet).
29 *) There was in the beginning no point to anyone seeing this other
30 then me, so there is a good chance that I haven't quite documented
31 it well.
32 *) Less design, more "make it work"
34 Now there are a few cool things with it:
35 *) Errors can result in corrupted data files.
36 *) Data files can be read by spreadsheets directly.
38 TODO:
39 *) Move to a block system for larger files
40 *) Error recovery, its all there, just need to finish it
41 *) Document how the chains work.
43 -Brian
46 #ifdef USE_PRAGMA_IMPLEMENTATION
47 #pragma implementation // gcc: Class implementation
48 #endif
50 #include "mysql_priv.h"
51 #include <mysql/plugin.h>
52 #include "ha_tina.h"
56 uchar + uchar + ulonglong + ulonglong + ulonglong + ulonglong + uchar
58 #define META_BUFFER_SIZE sizeof(uchar) + sizeof(uchar) + sizeof(ulonglong) \
59 + sizeof(ulonglong) + sizeof(ulonglong) + sizeof(ulonglong) + sizeof(uchar)
60 #define TINA_CHECK_HEADER 254 // The number we use to determine corruption
61 #define BLOB_MEMROOT_ALLOC_SIZE 8192
63 /* The file extension */
64 #define CSV_EXT ".CSV" // The data file
65 #define CSN_EXT ".CSN" // Files used during repair and update
66 #define CSM_EXT ".CSM" // Meta file
69 static TINA_SHARE *get_share(const char *table_name, TABLE *table);
70 static int free_share(TINA_SHARE *share);
71 static int read_meta_file(File meta_file, ha_rows *rows);
72 static int write_meta_file(File meta_file, ha_rows rows, bool dirty);
74 extern "C" void tina_get_status(void* param, int concurrent_insert);
75 extern "C" void tina_update_status(void* param);
76 extern "C" my_bool tina_check_status(void* param);
78 /* Stuff for shares */
79 pthread_mutex_t tina_mutex;
80 static HASH tina_open_tables;
81 static handler *tina_create_handler(handlerton *hton,
82 TABLE_SHARE *table,
83 MEM_ROOT *mem_root);
86 /*****************************************************************************
87 ** TINA tables
88 *****************************************************************************/
91 Used for sorting chains with qsort().
93 int sort_set (tina_set *a, tina_set *b)
96 We assume that intervals do not intersect. So, it is enought to compare
97 any two points. Here we take start of intervals for comparison.
99 return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) );
102 static uchar* tina_get_key(TINA_SHARE *share, size_t *length,
103 my_bool not_used __attribute__((unused)))
105 *length=share->table_name_length;
106 return (uchar*) share->table_name;
109 static int tina_init_func(void *p)
111 handlerton *tina_hton;
113 tina_hton= (handlerton *)p;
114 VOID(pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST));
115 (void) hash_init(&tina_open_tables,system_charset_info,32,0,0,
116 (hash_get_key) tina_get_key,0,0);
117 tina_hton->state= SHOW_OPTION_YES;
118 tina_hton->db_type= DB_TYPE_CSV_DB;
119 tina_hton->create= tina_create_handler;
120 tina_hton->flags= (HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES |
121 HTON_NO_PARTITION);
122 return 0;
125 static int tina_done_func(void *p)
127 hash_free(&tina_open_tables);
128 pthread_mutex_destroy(&tina_mutex);
130 return 0;
135 Simple lock controls.
137 static TINA_SHARE *get_share(const char *table_name, TABLE *table)
139 TINA_SHARE *share;
140 char meta_file_name[FN_REFLEN];
141 MY_STAT file_stat; /* Stat information for the data file */
142 char *tmp_name;
143 uint length;
145 pthread_mutex_lock(&tina_mutex);
146 length=(uint) strlen(table_name);
149 If share is not present in the hash, create a new share and
150 initialize its members.
152 if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
153 (uchar*) table_name,
154 length)))
156 if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
157 &share, sizeof(*share),
158 &tmp_name, length+1,
159 NullS))
161 pthread_mutex_unlock(&tina_mutex);
162 return NULL;
165 share->use_count= 0;
166 share->is_log_table= FALSE;
167 share->table_name_length= length;
168 share->table_name= tmp_name;
169 share->crashed= FALSE;
170 share->rows_recorded= 0;
171 share->update_file_opened= FALSE;
172 share->tina_write_opened= FALSE;
173 share->data_file_version= 0;
174 strmov(share->table_name, table_name);
175 fn_format(share->data_file_name, table_name, "", CSV_EXT,
176 MY_REPLACE_EXT|MY_UNPACK_FILENAME);
177 fn_format(meta_file_name, table_name, "", CSM_EXT,
178 MY_REPLACE_EXT|MY_UNPACK_FILENAME);
180 if (my_stat(share->data_file_name, &file_stat, MYF(MY_WME)) == NULL)
181 goto error;
182 share->saved_data_file_length= file_stat.st_size;
184 if (my_hash_insert(&tina_open_tables, (uchar*) share))
185 goto error;
186 thr_lock_init(&share->lock);
187 pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
190 Open or create the meta file. In the latter case, we'll get
191 an error during read_meta_file and mark the table as crashed.
192 Usually this will result in auto-repair, and we will get a good
193 meta-file in the end.
195 if (((share->meta_file= my_open(meta_file_name,
196 O_RDWR|O_CREAT, MYF(MY_WME))) == -1) ||
197 read_meta_file(share->meta_file, &share->rows_recorded))
198 share->crashed= TRUE;
201 share->use_count++;
202 pthread_mutex_unlock(&tina_mutex);
204 return share;
206 error:
207 pthread_mutex_unlock(&tina_mutex);
208 my_free((uchar*) share, MYF(0));
210 return NULL;
215 Read CSV meta-file
217 SYNOPSIS
218 read_meta_file()
219 meta_file The meta-file filedes
220 ha_rows Pointer to the var we use to store rows count.
221 These are read from the meta-file.
223 DESCRIPTION
225 Read the meta-file info. For now we are only interested in
226 rows counf, crashed bit and magic number.
228 RETURN
229 0 - OK
230 non-zero - error occurred
233 static int read_meta_file(File meta_file, ha_rows *rows)
235 uchar meta_buffer[META_BUFFER_SIZE];
236 uchar *ptr= meta_buffer;
238 DBUG_ENTER("ha_tina::read_meta_file");
240 VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0)));
241 if (my_read(meta_file, (uchar*)meta_buffer, META_BUFFER_SIZE, 0)
242 != META_BUFFER_SIZE)
243 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
246 Parse out the meta data, we ignore version at the moment
249 ptr+= sizeof(uchar)*2; // Move past header
250 *rows= (ha_rows)uint8korr(ptr);
251 ptr+= sizeof(ulonglong); // Move past rows
253 Move past check_point, auto_increment and forced_flushes fields.
254 They are present in the format, but we do not use them yet.
256 ptr+= 3*sizeof(ulonglong);
258 /* check crashed bit and magic number */
259 if ((meta_buffer[0] != (uchar)TINA_CHECK_HEADER) ||
260 ((bool)(*ptr)== TRUE))
261 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
263 my_sync(meta_file, MYF(MY_WME));
265 DBUG_RETURN(0);
270 Write CSV meta-file
272 SYNOPSIS
273 write_meta_file()
274 meta_file The meta-file filedes
275 ha_rows The number of rows we have in the datafile.
276 dirty A flag, which marks whether we have a corrupt table
278 DESCRIPTION
280 Write meta-info the the file. Only rows count, crashed bit and
281 magic number matter now.
283 RETURN
284 0 - OK
285 non-zero - error occurred
288 static int write_meta_file(File meta_file, ha_rows rows, bool dirty)
290 uchar meta_buffer[META_BUFFER_SIZE];
291 uchar *ptr= meta_buffer;
293 DBUG_ENTER("ha_tina::write_meta_file");
295 *ptr= (uchar)TINA_CHECK_HEADER;
296 ptr+= sizeof(uchar);
297 *ptr= (uchar)TINA_VERSION;
298 ptr+= sizeof(uchar);
299 int8store(ptr, (ulonglong)rows);
300 ptr+= sizeof(ulonglong);
301 memset(ptr, 0, 3*sizeof(ulonglong));
303 Skip over checkpoint, autoincrement and forced_flushes fields.
304 We'll need them later.
306 ptr+= 3*sizeof(ulonglong);
307 *ptr= (uchar)dirty;
309 VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0)));
310 if (my_write(meta_file, (uchar *)meta_buffer, META_BUFFER_SIZE, 0)
311 != META_BUFFER_SIZE)
312 DBUG_RETURN(-1);
314 my_sync(meta_file, MYF(MY_WME));
316 DBUG_RETURN(0);
319 bool ha_tina::check_and_repair(THD *thd)
321 HA_CHECK_OPT check_opt;
322 DBUG_ENTER("ha_tina::check_and_repair");
324 check_opt.init();
326 DBUG_RETURN(repair(thd, &check_opt));
330 int ha_tina::init_tina_writer()
332 DBUG_ENTER("ha_tina::init_tina_writer");
335 Mark the file as crashed. We will set the flag back when we close
336 the file. In the case of the crash it will remain marked crashed,
337 which enforce recovery.
339 (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE);
341 if ((share->tina_write_filedes=
342 my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(MY_WME))) == -1)
344 DBUG_PRINT("info", ("Could not open tina file writes"));
345 share->crashed= TRUE;
346 DBUG_RETURN(my_errno ? my_errno : -1);
348 share->tina_write_opened= TRUE;
350 DBUG_RETURN(0);
354 bool ha_tina::is_crashed() const
356 DBUG_ENTER("ha_tina::is_crashed");
357 DBUG_RETURN(share->crashed);
361 Free lock controls.
363 static int free_share(TINA_SHARE *share)
365 DBUG_ENTER("ha_tina::free_share");
366 pthread_mutex_lock(&tina_mutex);
367 int result_code= 0;
368 if (!--share->use_count){
369 /* Write the meta file. Mark it as crashed if needed. */
370 (void)write_meta_file(share->meta_file, share->rows_recorded,
371 share->crashed ? TRUE :FALSE);
372 if (my_close(share->meta_file, MYF(0)))
373 result_code= 1;
374 if (share->tina_write_opened)
376 if (my_close(share->tina_write_filedes, MYF(0)))
377 result_code= 1;
378 share->tina_write_opened= FALSE;
381 hash_delete(&tina_open_tables, (uchar*) share);
382 thr_lock_delete(&share->lock);
383 pthread_mutex_destroy(&share->mutex);
384 my_free((uchar*) share, MYF(0));
386 pthread_mutex_unlock(&tina_mutex);
388 DBUG_RETURN(result_code);
393 This function finds the end of a line and returns the length
394 of the line ending.
396 We support three kinds of line endings:
397 '\r' -- Old Mac OS line ending
398 '\n' -- Traditional Unix and Mac OS X line ending
399 '\r''\n' -- DOS\Windows line ending
402 my_off_t find_eoln_buff(Transparent_file *data_buff, my_off_t begin,
403 my_off_t end, int *eoln_len)
405 *eoln_len= 0;
407 for (my_off_t x= begin; x < end; x++)
409 /* Unix (includes Mac OS X) */
410 if (data_buff->get_value(x) == '\n')
411 *eoln_len= 1;
412 else
413 if (data_buff->get_value(x) == '\r') // Mac or Dos
415 /* old Mac line ending */
416 if (x + 1 == end || (data_buff->get_value(x + 1) != '\n'))
417 *eoln_len= 1;
418 else // DOS style ending
419 *eoln_len= 2;
422 if (*eoln_len) // end of line was found
423 return x;
426 return 0;
430 static handler *tina_create_handler(handlerton *hton,
431 TABLE_SHARE *table,
432 MEM_ROOT *mem_root)
434 return new (mem_root) ha_tina(hton, table);
438 ha_tina::ha_tina(handlerton *hton, TABLE_SHARE *table_arg)
439 :handler(hton, table_arg),
441 These definitions are found in handler.h
442 They are not probably completely right.
444 current_position(0), next_position(0), local_saved_data_file_length(0),
445 file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH),
446 local_data_file_version(0), records_is_known(0)
448 /* Set our original buffers from pre-allocated memory */
449 buffer.set((char*)byte_buffer, IO_SIZE, &my_charset_bin);
450 chain= chain_buffer;
451 file_buff= new Transparent_file();
456 Encode a buffer into the quoted format.
459 int ha_tina::encode_quote(uchar *buf)
461 char attribute_buffer[1024];
462 String attribute(attribute_buffer, sizeof(attribute_buffer),
463 &my_charset_bin);
465 my_bitmap_map *org_bitmap= dbug_tmp_use_all_columns(table, table->read_set);
466 buffer.length(0);
468 for (Field **field=table->field ; *field ; field++)
470 const char *ptr;
471 const char *end_ptr;
472 const bool was_null= (*field)->is_null();
475 assistance for backwards compatibility in production builds.
476 note: this will not work for ENUM columns.
478 if (was_null)
480 (*field)->set_default();
481 (*field)->set_notnull();
484 (*field)->val_str(&attribute,&attribute);
486 if (was_null)
487 (*field)->set_null();
489 if ((*field)->str_needs_quotes())
491 ptr= attribute.ptr();
492 end_ptr= attribute.length() + ptr;
494 buffer.append('"');
496 for (; ptr < end_ptr; ptr++)
498 if (*ptr == '"')
500 buffer.append('\\');
501 buffer.append('"');
503 else if (*ptr == '\r')
505 buffer.append('\\');
506 buffer.append('r');
508 else if (*ptr == '\\')
510 buffer.append('\\');
511 buffer.append('\\');
513 else if (*ptr == '\n')
515 buffer.append('\\');
516 buffer.append('n');
518 else
519 buffer.append(*ptr);
521 buffer.append('"');
523 else
525 buffer.append(attribute);
528 buffer.append(',');
530 // Remove the comma, add a line feed
531 buffer.length(buffer.length() - 1);
532 buffer.append('\n');
534 //buffer.replace(buffer.length(), 0, "\n", 1);
536 dbug_tmp_restore_column_map(table->read_set, org_bitmap);
537 return (buffer.length());
541 chain_append() adds delete positions to the chain that we use to keep
542 track of space. Then the chain will be used to cleanup "holes", occurred
543 due to deletes and updates.
545 int ha_tina::chain_append()
547 if ( chain_ptr != chain && (chain_ptr -1)->end == current_position)
548 (chain_ptr -1)->end= next_position;
549 else
551 /* We set up for the next position */
552 if ((off_t)(chain_ptr - chain) == (chain_size -1))
554 my_off_t location= chain_ptr - chain;
555 chain_size += DEFAULT_CHAIN_LENGTH;
556 if (chain_alloced)
558 /* Must cast since my_malloc unlike malloc doesn't have a void ptr */
559 if ((chain= (tina_set *) my_realloc((uchar*)chain,
560 chain_size, MYF(MY_WME))) == NULL)
561 return -1;
563 else
565 tina_set *ptr= (tina_set *) my_malloc(chain_size * sizeof(tina_set),
566 MYF(MY_WME));
567 memcpy(ptr, chain, DEFAULT_CHAIN_LENGTH * sizeof(tina_set));
568 chain= ptr;
569 chain_alloced++;
571 chain_ptr= chain + location;
573 chain_ptr->begin= current_position;
574 chain_ptr->end= next_position;
575 chain_ptr++;
578 return 0;
583 Scans for a row.
585 int ha_tina::find_current_row(uchar *buf)
587 my_off_t end_offset, curr_offset= current_position;
588 int eoln_len;
589 my_bitmap_map *org_bitmap;
590 int error;
591 bool read_all;
592 DBUG_ENTER("ha_tina::find_current_row");
594 free_root(&blobroot, MYF(MY_MARK_BLOCKS_FREE));
597 We do not read further then local_saved_data_file_length in order
598 not to conflict with undergoing concurrent insert.
600 if ((end_offset=
601 find_eoln_buff(file_buff, current_position,
602 local_saved_data_file_length, &eoln_len)) == 0)
603 DBUG_RETURN(HA_ERR_END_OF_FILE);
605 /* We must read all columns in case a table is opened for update */
606 read_all= !bitmap_is_clear_all(table->write_set);
607 /* Avoid asserts in ::store() for columns that are not going to be updated */
608 org_bitmap= dbug_tmp_use_all_columns(table, table->write_set);
609 error= HA_ERR_CRASHED_ON_USAGE;
611 memset(buf, 0, table->s->null_bytes);
613 for (Field **field=table->field ; *field ; field++)
615 char curr_char;
617 buffer.length(0);
618 if (curr_offset >= end_offset)
619 goto err;
620 curr_char= file_buff->get_value(curr_offset);
621 if (curr_char == '"')
623 curr_offset++; // Incrementpast the first quote
625 for(; curr_offset < end_offset; curr_offset++)
627 curr_char= file_buff->get_value(curr_offset);
628 // Need to convert line feeds!
629 if (curr_char == '"' &&
630 (curr_offset == end_offset - 1 ||
631 file_buff->get_value(curr_offset + 1) == ','))
633 curr_offset+= 2; // Move past the , and the "
634 break;
636 if (curr_char == '\\' && curr_offset != (end_offset - 1))
638 curr_offset++;
639 curr_char= file_buff->get_value(curr_offset);
640 if (curr_char == 'r')
641 buffer.append('\r');
642 else if (curr_char == 'n' )
643 buffer.append('\n');
644 else if (curr_char == '\\' || curr_char == '"')
645 buffer.append(curr_char);
646 else /* This could only happed with an externally created file */
648 buffer.append('\\');
649 buffer.append(curr_char);
652 else // ordinary symbol
655 We are at final symbol and no last quote was found =>
656 we are working with a damaged file.
658 if (curr_offset == end_offset - 1)
659 goto err;
660 buffer.append(curr_char);
664 else
666 for(; curr_offset < end_offset; curr_offset++)
668 curr_char= file_buff->get_value(curr_offset);
669 if (curr_char == ',')
671 curr_offset++; // Skip the ,
672 break;
674 buffer.append(curr_char);
678 if (read_all || bitmap_is_set(table->read_set, (*field)->field_index))
680 bool is_enum= ((*field)->real_type() == MYSQL_TYPE_ENUM);
682 Here CHECK_FIELD_WARN checks that all values in the csv file are valid
683 which is normally the case, if they were written by
684 INSERT -> ha_tina::write_row. '0' values on ENUM fields are considered
685 invalid by Field_enum::store() but it can store them on INSERT anyway.
686 Thus, for enums we silence the warning, as it doesn't really mean
687 an invalid value.
689 if ((*field)->store(buffer.ptr(), buffer.length(), buffer.charset(),
690 is_enum ? CHECK_FIELD_IGNORE : CHECK_FIELD_WARN))
692 if (!is_enum)
693 goto err;
695 if ((*field)->flags & BLOB_FLAG)
697 Field_blob *blob= *(Field_blob**) field;
698 uchar *src, *tgt;
699 uint length, packlength;
701 packlength= blob->pack_length_no_ptr();
702 length= blob->get_length(blob->ptr);
703 memcpy_fixed(&src, blob->ptr + packlength, sizeof(char*));
704 if (src)
706 tgt= (uchar*) alloc_root(&blobroot, length);
707 bmove(tgt, src, length);
708 memcpy_fixed(blob->ptr + packlength, &tgt, sizeof(char*));
713 next_position= end_offset + eoln_len;
714 error= 0;
716 err:
717 dbug_tmp_restore_column_map(table->write_set, org_bitmap);
719 DBUG_RETURN(error);
723 If frm_error() is called in table.cc this is called to find out what file
724 extensions exist for this handler.
726 static const char *ha_tina_exts[] = {
727 CSV_EXT,
728 CSM_EXT,
729 NullS
732 const char **ha_tina::bas_ext() const
734 return ha_tina_exts;
738 Three functions below are needed to enable concurrent insert functionality
739 for CSV engine. For more details see mysys/thr_lock.c
742 void tina_get_status(void* param, int concurrent_insert)
744 ha_tina *tina= (ha_tina*) param;
745 tina->get_status();
748 void tina_update_status(void* param)
750 ha_tina *tina= (ha_tina*) param;
751 tina->update_status();
754 /* this should exist and return 0 for concurrent insert to work */
755 my_bool tina_check_status(void* param)
757 return 0;
761 Save the state of the table
763 SYNOPSIS
764 get_status()
766 DESCRIPTION
767 This function is used to retrieve the file length. During the lock
768 phase of concurrent insert. For more details see comment to
769 ha_tina::update_status below.
772 void ha_tina::get_status()
774 if (share->is_log_table)
777 We have to use mutex to follow pthreads memory visibility
778 rules for share->saved_data_file_length
780 pthread_mutex_lock(&share->mutex);
781 local_saved_data_file_length= share->saved_data_file_length;
782 pthread_mutex_unlock(&share->mutex);
783 return;
785 local_saved_data_file_length= share->saved_data_file_length;
790 Correct the state of the table. Called by unlock routines
791 before the write lock is released.
793 SYNOPSIS
794 update_status()
796 DESCRIPTION
797 When we employ concurrent insert lock, we save current length of the file
798 during the lock phase. We do not read further saved value, as we don't
799 want to interfere with undergoing concurrent insert. Writers update file
800 length info during unlock with update_status().
802 NOTE
803 For log tables concurrent insert works different. The reason is that
804 log tables are always opened and locked. And as they do not unlock
805 tables, the file length after writes should be updated in a different
806 way. For this purpose we need is_log_table flag. When this flag is set
807 we call update_status() explicitly after each row write.
810 void ha_tina::update_status()
812 /* correct local_saved_data_file_length for writers */
813 share->saved_data_file_length= local_saved_data_file_length;
818 Open a database file. Keep in mind that tables are caches, so
819 this will not be called for every request. Any sort of positions
820 that need to be reset should be kept in the ::extra() call.
822 int ha_tina::open(const char *name, int mode, uint open_options)
824 DBUG_ENTER("ha_tina::open");
826 if (!(share= get_share(name, table)))
827 DBUG_RETURN(HA_ERR_OUT_OF_MEM);
829 if (share->crashed && !(open_options & HA_OPEN_FOR_REPAIR))
831 free_share(share);
832 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
835 local_data_file_version= share->data_file_version;
836 if ((data_file= my_open(share->data_file_name,
837 O_RDONLY, MYF(MY_WME))) == -1)
839 free_share(share);
840 DBUG_RETURN(my_errno ? my_errno : -1);
844 Init locking. Pass handler object to the locking routines,
845 so that they could save/update local_saved_data_file_length value
846 during locking. This is needed to enable concurrent inserts.
848 thr_lock_data_init(&share->lock, &lock, (void*) this);
849 ref_length= sizeof(my_off_t);
851 share->lock.get_status= tina_get_status;
852 share->lock.update_status= tina_update_status;
853 share->lock.check_status= tina_check_status;
855 DBUG_RETURN(0);
860 Close a database file. We remove ourselves from the shared strucutre.
861 If it is empty we destroy it.
863 int ha_tina::close(void)
865 int rc= 0;
866 DBUG_ENTER("ha_tina::close");
867 rc= my_close(data_file, MYF(0));
868 DBUG_RETURN(free_share(share) || rc);
872 This is an INSERT. At the moment this handler just seeks to the end
873 of the file and appends the data. In an error case it really should
874 just truncate to the original position (this is not done yet).
876 int ha_tina::write_row(uchar * buf)
878 int size;
879 DBUG_ENTER("ha_tina::write_row");
881 if (share->crashed)
882 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
884 ha_statistic_increment(&SSV::ha_write_count);
886 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
887 table->timestamp_field->set_time();
889 size= encode_quote(buf);
891 if (!share->tina_write_opened)
892 if (init_tina_writer())
893 DBUG_RETURN(-1);
895 /* use pwrite, as concurrent reader could have changed the position */
896 if (my_write(share->tina_write_filedes, (uchar*)buffer.ptr(), size,
897 MYF(MY_WME | MY_NABP)))
898 DBUG_RETURN(-1);
900 /* update local copy of the max position to see our own changes */
901 local_saved_data_file_length+= size;
903 /* update shared info */
904 pthread_mutex_lock(&share->mutex);
905 share->rows_recorded++;
906 /* update status for the log tables */
907 if (share->is_log_table)
908 update_status();
909 pthread_mutex_unlock(&share->mutex);
911 stats.records++;
912 DBUG_RETURN(0);
916 int ha_tina::open_update_temp_file_if_needed()
918 char updated_fname[FN_REFLEN];
920 if (!share->update_file_opened)
922 if ((update_temp_file=
923 my_create(fn_format(updated_fname, share->table_name,
924 "", CSN_EXT,
925 MY_REPLACE_EXT | MY_UNPACK_FILENAME),
926 0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0)
927 return 1;
928 share->update_file_opened= TRUE;
929 temp_file_length= 0;
931 return 0;
935 This is called for an update.
936 Make sure you put in code to increment the auto increment, also
937 update any timestamp data. Currently auto increment is not being
938 fixed since autoincrements have yet to be added to this table handler.
939 This will be called in a table scan right before the previous ::rnd_next()
940 call.
942 int ha_tina::update_row(const uchar * old_data, uchar * new_data)
944 int size;
945 int rc= -1;
946 DBUG_ENTER("ha_tina::update_row");
948 ha_statistic_increment(&SSV::ha_update_count);
950 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
951 table->timestamp_field->set_time();
953 size= encode_quote(new_data);
956 During update we mark each updating record as deleted
957 (see the chain_append()) then write new one to the temporary data file.
958 At the end of the sequence in the rnd_end() we append all non-marked
959 records from the data file to the temporary data file then rename it.
960 The temp_file_length is used to calculate new data file length.
962 if (chain_append())
963 goto err;
965 if (open_update_temp_file_if_needed())
966 goto err;
968 if (my_write(update_temp_file, (uchar*)buffer.ptr(), size,
969 MYF(MY_WME | MY_NABP)))
970 goto err;
971 temp_file_length+= size;
972 rc= 0;
974 /* UPDATE should never happen on the log tables */
975 DBUG_ASSERT(!share->is_log_table);
977 err:
978 DBUG_PRINT("info",("rc = %d", rc));
979 DBUG_RETURN(rc);
984 Deletes a row. First the database will find the row, and then call this
985 method. In the case of a table scan, the previous call to this will be
986 the ::rnd_next() that found this row.
987 The exception to this is an ORDER BY. This will cause the table handler
988 to walk the table noting the positions of all rows that match a query.
989 The table will then be deleted/positioned based on the ORDER (so RANDOM,
990 DESC, ASC).
992 int ha_tina::delete_row(const uchar * buf)
994 DBUG_ENTER("ha_tina::delete_row");
995 ha_statistic_increment(&SSV::ha_delete_count);
997 if (chain_append())
998 DBUG_RETURN(-1);
1000 stats.records--;
1001 /* Update shared info */
1002 DBUG_ASSERT(share->rows_recorded);
1003 pthread_mutex_lock(&share->mutex);
1004 share->rows_recorded--;
1005 pthread_mutex_unlock(&share->mutex);
1007 /* DELETE should never happen on the log table */
1008 DBUG_ASSERT(!share->is_log_table);
1010 DBUG_RETURN(0);
1015 @brief Initialize the data file.
1017 @details Compare the local version of the data file with the shared one.
1018 If they differ, there are some changes behind and we have to reopen
1019 the data file to make the changes visible.
1020 Call @c file_buff->init_buff() at the end to read the beginning of the
1021 data file into buffer.
1023 @retval 0 OK.
1024 @retval 1 There was an error.
1027 int ha_tina::init_data_file()
1029 if (local_data_file_version != share->data_file_version)
1031 local_data_file_version= share->data_file_version;
1032 if (my_close(data_file, MYF(0)) ||
1033 (data_file= my_open(share->data_file_name, O_RDONLY, MYF(MY_WME))) == -1)
1034 return my_errno ? my_errno : -1;
1036 file_buff->init_buff(data_file);
1037 return 0;
1042 All table scans call this first.
1043 The order of a table scan is:
1045 ha_tina::store_lock
1046 ha_tina::external_lock
1047 ha_tina::info
1048 ha_tina::rnd_init
1049 ha_tina::extra
1050 ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
1051 ha_tina::rnd_next
1052 ha_tina::rnd_next
1053 ha_tina::rnd_next
1054 ha_tina::rnd_next
1055 ha_tina::rnd_next
1056 ha_tina::rnd_next
1057 ha_tina::rnd_next
1058 ha_tina::rnd_next
1059 ha_tina::rnd_next
1060 ha_tina::extra
1061 ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
1062 ha_tina::external_lock
1063 ha_tina::extra
1064 ENUM HA_EXTRA_RESET Reset database to after open
1066 Each call to ::rnd_next() represents a row returned in the can. When no more
1067 rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
1068 The ::info() call is just for the optimizer.
1072 int ha_tina::rnd_init(bool scan)
1074 DBUG_ENTER("ha_tina::rnd_init");
1076 /* set buffer to the beginning of the file */
1077 if (share->crashed || init_data_file())
1078 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
1080 current_position= next_position= 0;
1081 stats.records= 0;
1082 records_is_known= 0;
1083 chain_ptr= chain;
1085 init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1087 DBUG_RETURN(0);
1091 ::rnd_next() does all the heavy lifting for a table scan. You will need to
1092 populate *buf with the correct field data. You can walk the field to
1093 determine at what position you should store the data (take a look at how
1094 ::find_current_row() works). The structure is something like:
1095 0Foo Dog Friend
1096 The first offset is for the first attribute. All space before that is
1097 reserved for null count.
1098 Basically this works as a mask for which rows are nulled (compared to just
1099 empty).
1100 This table handler doesn't do nulls and does not know the difference between
1101 NULL and "". This is ok since this table handler is for spreadsheets and
1102 they don't know about them either :)
1104 int ha_tina::rnd_next(uchar *buf)
1106 int rc;
1107 DBUG_ENTER("ha_tina::rnd_next");
1109 if (share->crashed)
1110 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE);
1112 ha_statistic_increment(&SSV::ha_read_rnd_next_count);
1114 current_position= next_position;
1116 /* don't scan an empty file */
1117 if (!local_saved_data_file_length)
1118 DBUG_RETURN(HA_ERR_END_OF_FILE);
1120 if ((rc= find_current_row(buf)))
1121 DBUG_RETURN(rc);
1123 stats.records++;
1124 DBUG_RETURN(0);
1128 In the case of an order by rows will need to be sorted.
1129 ::position() is called after each call to ::rnd_next(),
1130 the data it stores is to a byte array. You can store this
1131 data via my_store_ptr(). ref_length is a variable defined to the
1132 class that is the sizeof() of position being stored. In our case
1133 its just a position. Look at the bdb code if you want to see a case
1134 where something other then a number is stored.
1136 void ha_tina::position(const uchar *record)
1138 DBUG_ENTER("ha_tina::position");
1139 my_store_ptr(ref, ref_length, current_position);
1140 DBUG_VOID_RETURN;
1145 Used to fetch a row from a posiion stored with ::position().
1146 my_get_ptr() retrieves the data for you.
1149 int ha_tina::rnd_pos(uchar * buf, uchar *pos)
1151 DBUG_ENTER("ha_tina::rnd_pos");
1152 ha_statistic_increment(&SSV::ha_read_rnd_count);
1153 current_position= my_get_ptr(pos,ref_length);
1154 DBUG_RETURN(find_current_row(buf));
1158 ::info() is used to return information to the optimizer.
1159 Currently this table handler doesn't implement most of the fields
1160 really needed. SHOW also makes use of this data
1162 int ha_tina::info(uint flag)
1164 DBUG_ENTER("ha_tina::info");
1165 /* This is a lie, but you don't want the optimizer to see zero or 1 */
1166 if (!records_is_known && stats.records < 2)
1167 stats.records= 2;
1168 DBUG_RETURN(0);
1172 Grab bag of flags that are sent to the able handler every so often.
1173 HA_EXTRA_RESET and HA_EXTRA_RESET_STATE are the most frequently called.
1174 You are not required to implement any of these.
1176 int ha_tina::extra(enum ha_extra_function operation)
1178 DBUG_ENTER("ha_tina::extra");
1179 if (operation == HA_EXTRA_MARK_AS_LOG_TABLE)
1181 pthread_mutex_lock(&share->mutex);
1182 share->is_log_table= TRUE;
1183 pthread_mutex_unlock(&share->mutex);
1185 DBUG_RETURN(0);
1189 Set end_pos to the last valid byte of continuous area, closest
1190 to the given "hole", stored in the buffer. "Valid" here means,
1191 not listed in the chain of deleted records ("holes").
1193 bool ha_tina::get_write_pos(my_off_t *end_pos, tina_set *closest_hole)
1195 if (closest_hole == chain_ptr) /* no more chains */
1196 *end_pos= file_buff->end();
1197 else
1198 *end_pos= min(file_buff->end(),
1199 closest_hole->begin);
1200 return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin);
1205 Called after each table scan. In particular after deletes,
1206 and updates. In the last case we employ chain of deleted
1207 slots to clean up all of the dead space we have collected while
1208 performing deletes/updates.
1210 int ha_tina::rnd_end()
1212 char updated_fname[FN_REFLEN];
1213 my_off_t file_buffer_start= 0;
1214 DBUG_ENTER("ha_tina::rnd_end");
1216 free_root(&blobroot, MYF(0));
1217 records_is_known= 1;
1219 if ((chain_ptr - chain) > 0)
1221 tina_set *ptr= chain;
1224 Re-read the beginning of a file (as the buffer should point to the
1225 end of file after the scan).
1227 file_buff->init_buff(data_file);
1230 The sort is needed when there were updates/deletes with random orders.
1231 It sorts so that we move the firts blocks to the beginning.
1233 my_qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set),
1234 (qsort_cmp)sort_set);
1236 my_off_t write_begin= 0, write_end;
1238 /* create the file to write updated table if it wasn't yet created */
1239 if (open_update_temp_file_if_needed())
1240 DBUG_RETURN(-1);
1242 /* write the file with updated info */
1243 while ((file_buffer_start != (my_off_t)-1)) // while not end of file
1245 bool in_hole= get_write_pos(&write_end, ptr);
1246 my_off_t write_length= write_end - write_begin;
1248 /* if there is something to write, write it */
1249 if (write_length)
1251 if (my_write(update_temp_file,
1252 (uchar*) (file_buff->ptr() +
1253 (write_begin - file_buff->start())),
1254 (size_t)write_length, MYF_RW))
1255 goto error;
1256 temp_file_length+= write_length;
1258 if (in_hole)
1260 /* skip hole */
1261 while (file_buff->end() <= ptr->end &&
1262 file_buffer_start != (my_off_t)-1)
1263 file_buffer_start= file_buff->read_next();
1264 write_begin= ptr->end;
1265 ptr++;
1267 else
1268 write_begin= write_end;
1270 if (write_end == file_buff->end())
1271 file_buffer_start= file_buff->read_next(); /* shift the buffer */
1275 if (my_sync(update_temp_file, MYF(MY_WME)) ||
1276 my_close(update_temp_file, MYF(0)))
1277 DBUG_RETURN(-1);
1279 share->update_file_opened= FALSE;
1281 if (share->tina_write_opened)
1283 if (my_close(share->tina_write_filedes, MYF(0)))
1284 DBUG_RETURN(-1);
1286 Mark that the writer fd is closed, so that init_tina_writer()
1287 will reopen it later.
1289 share->tina_write_opened= FALSE;
1293 Close opened fildes's. Then move updated file in place
1294 of the old datafile.
1296 if (my_close(data_file, MYF(0)) ||
1297 my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT,
1298 MY_REPLACE_EXT | MY_UNPACK_FILENAME),
1299 share->data_file_name, MYF(0)))
1300 DBUG_RETURN(-1);
1302 /* Open the file again */
1303 if (((data_file= my_open(share->data_file_name,
1304 O_RDONLY, MYF(MY_WME))) == -1))
1305 DBUG_RETURN(my_errno ? my_errno : -1);
1307 As we reopened the data file, increase share->data_file_version
1308 in order to force other threads waiting on a table lock and
1309 have already opened the table to reopen the data file.
1310 That makes the latest changes become visible to them.
1311 Update local_data_file_version as no need to reopen it in the
1312 current thread.
1314 share->data_file_version++;
1315 local_data_file_version= share->data_file_version;
1317 The datafile is consistent at this point and the write filedes is
1318 closed, so nothing worrying will happen to it in case of a crash.
1319 Here we record this fact to the meta-file.
1321 (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE);
1323 Update local_saved_data_file_length with the real length of the
1324 data file.
1326 local_saved_data_file_length= temp_file_length;
1329 DBUG_RETURN(0);
1330 error:
1331 my_close(update_temp_file, MYF(0));
1332 share->update_file_opened= FALSE;
1333 DBUG_RETURN(-1);
1338 Repair CSV table in the case, it is crashed.
1340 SYNOPSIS
1341 repair()
1342 thd The thread, performing repair
1343 check_opt The options for repair. We do not use it currently.
1345 DESCRIPTION
1346 If the file is empty, change # of rows in the file and complete recovery.
1347 Otherwise, scan the table looking for bad rows. If none were found,
1348 we mark file as a good one and return. If a bad row was encountered,
1349 we truncate the datafile up to the last good row.
1351 TODO: Make repair more clever - it should try to recover subsequent
1352 rows (after the first bad one) as well.
1355 int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt)
1357 char repaired_fname[FN_REFLEN];
1358 uchar *buf;
1359 File repair_file;
1360 int rc;
1361 ha_rows rows_repaired= 0;
1362 my_off_t write_begin= 0, write_end;
1363 DBUG_ENTER("ha_tina::repair");
1365 /* empty file */
1366 if (!share->saved_data_file_length)
1368 share->rows_recorded= 0;
1369 goto end;
1372 /* Don't assert in field::val() functions */
1373 table->use_all_columns();
1374 if (!(buf= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME))))
1375 DBUG_RETURN(HA_ERR_OUT_OF_MEM);
1377 /* position buffer to the start of the file */
1378 if (init_data_file())
1379 DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
1382 Local_saved_data_file_length is initialized during the lock phase.
1383 Sometimes this is not getting executed before ::repair (e.g. for
1384 the log tables). We set it manually here.
1386 local_saved_data_file_length= share->saved_data_file_length;
1387 /* set current position to the beginning of the file */
1388 current_position= next_position= 0;
1390 init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1392 /* Read the file row-by-row. If everything is ok, repair is not needed. */
1393 while (!(rc= find_current_row(buf)))
1395 thd_inc_row_count(thd);
1396 rows_repaired++;
1397 current_position= next_position;
1400 free_root(&blobroot, MYF(0));
1402 my_free((char*)buf, MYF(0));
1404 if (rc == HA_ERR_END_OF_FILE)
1407 All rows were read ok until end of file, the file does not need repair.
1408 If rows_recorded != rows_repaired, we should update rows_recorded value
1409 to the current amount of rows.
1411 share->rows_recorded= rows_repaired;
1412 goto end;
1416 Otherwise we've encountered a bad row => repair is needed.
1417 Let us create a temporary file.
1419 if ((repair_file= my_create(fn_format(repaired_fname, share->table_name,
1420 "", CSN_EXT,
1421 MY_REPLACE_EXT|MY_UNPACK_FILENAME),
1422 0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1423 DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR);
1425 file_buff->init_buff(data_file);
1428 /* we just truncated the file up to the first bad row. update rows count. */
1429 share->rows_recorded= rows_repaired;
1431 /* write repaired file */
1432 while (1)
1434 write_end= min(file_buff->end(), current_position);
1435 if ((write_end - write_begin) &&
1436 (my_write(repair_file, (uchar*)file_buff->ptr(),
1437 (size_t) (write_end - write_begin), MYF_RW)))
1438 DBUG_RETURN(-1);
1440 write_begin= write_end;
1441 if (write_end== current_position)
1442 break;
1443 else
1444 file_buff->read_next(); /* shift the buffer */
1448 Close the files and rename repaired file to the datafile.
1449 We have to close the files, as on Windows one cannot rename
1450 a file, which descriptor is still open. EACCES will be returned
1451 when trying to delete the "to"-file in my_rename().
1453 if (share->tina_write_opened)
1456 Data file might be opened twice, on table opening stage and
1457 during write_row execution. We need to close both instances
1458 to satisfy Win.
1460 if (my_close(share->tina_write_filedes, MYF(0)))
1461 DBUG_RETURN(my_errno ? my_errno : -1);
1462 share->tina_write_opened= FALSE;
1464 if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) ||
1465 my_rename(repaired_fname, share->data_file_name, MYF(0)))
1466 DBUG_RETURN(-1);
1468 /* Open the file again, it should now be repaired */
1469 if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND,
1470 MYF(MY_WME))) == -1)
1471 DBUG_RETURN(my_errno ? my_errno : -1);
1473 /* Set new file size. The file size will be updated by ::update_status() */
1474 local_saved_data_file_length= (size_t) current_position;
1476 end:
1477 share->crashed= FALSE;
1478 DBUG_RETURN(HA_ADMIN_OK);
1482 DELETE without WHERE calls this
1485 int ha_tina::delete_all_rows()
1487 int rc;
1488 DBUG_ENTER("ha_tina::delete_all_rows");
1490 if (!records_is_known)
1491 DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND);
1493 if (!share->tina_write_opened)
1494 if (init_tina_writer())
1495 DBUG_RETURN(-1);
1497 /* Truncate the file to zero size */
1498 rc= my_chsize(share->tina_write_filedes, 0, 0, MYF(MY_WME));
1500 stats.records=0;
1501 /* Update shared info */
1502 pthread_mutex_lock(&share->mutex);
1503 share->rows_recorded= 0;
1504 pthread_mutex_unlock(&share->mutex);
1505 local_saved_data_file_length= 0;
1506 DBUG_RETURN(rc);
1510 Called by the database to lock the table. Keep in mind that this
1511 is an internal lock.
1513 THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
1514 THR_LOCK_DATA **to,
1515 enum thr_lock_type lock_type)
1517 if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
1518 lock.type=lock_type;
1519 *to++= &lock;
1520 return to;
1524 Create a table. You do not want to leave the table open after a call to
1525 this (the database will call ::open() if it needs to).
1528 int ha_tina::create(const char *name, TABLE *table_arg,
1529 HA_CREATE_INFO *create_info)
1531 char name_buff[FN_REFLEN];
1532 File create_file;
1533 DBUG_ENTER("ha_tina::create");
1536 check columns
1538 for (Field **field= table_arg->s->field; *field; field++)
1540 if ((*field)->real_maybe_null())
1542 my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "nullable columns");
1543 DBUG_RETURN(HA_ERR_UNSUPPORTED);
1548 if ((create_file= my_create(fn_format(name_buff, name, "", CSM_EXT,
1549 MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0,
1550 O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1551 DBUG_RETURN(-1);
1553 write_meta_file(create_file, 0, FALSE);
1554 my_close(create_file, MYF(0));
1556 if ((create_file= my_create(fn_format(name_buff, name, "", CSV_EXT,
1557 MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
1558 O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
1559 DBUG_RETURN(-1);
1561 my_close(create_file, MYF(0));
1563 DBUG_RETURN(0);
1566 int ha_tina::check(THD* thd, HA_CHECK_OPT* check_opt)
1568 int rc= 0;
1569 uchar *buf;
1570 const char *old_proc_info;
1571 ha_rows count= share->rows_recorded;
1572 DBUG_ENTER("ha_tina::check");
1574 old_proc_info= thd_proc_info(thd, "Checking table");
1575 if (!(buf= (uchar*) my_malloc(table->s->reclength, MYF(MY_WME))))
1576 DBUG_RETURN(HA_ERR_OUT_OF_MEM);
1578 /* position buffer to the start of the file */
1579 if (init_data_file())
1580 DBUG_RETURN(HA_ERR_CRASHED);
1583 Local_saved_data_file_length is initialized during the lock phase.
1584 Check does not use store_lock in certain cases. So, we set it
1585 manually here.
1587 local_saved_data_file_length= share->saved_data_file_length;
1588 /* set current position to the beginning of the file */
1589 current_position= next_position= 0;
1591 init_alloc_root(&blobroot, BLOB_MEMROOT_ALLOC_SIZE, 0);
1593 /* Read the file row-by-row. If everything is ok, repair is not needed. */
1594 while (!(rc= find_current_row(buf)))
1596 thd_inc_row_count(thd);
1597 count--;
1598 current_position= next_position;
1601 free_root(&blobroot, MYF(0));
1603 my_free((char*)buf, MYF(0));
1604 thd_proc_info(thd, old_proc_info);
1606 if ((rc != HA_ERR_END_OF_FILE) || count)
1608 share->crashed= TRUE;
1609 DBUG_RETURN(HA_ADMIN_CORRUPT);
1612 DBUG_RETURN(HA_ADMIN_OK);
1616 bool ha_tina::check_if_incompatible_data(HA_CREATE_INFO *info,
1617 uint table_changes)
1619 return COMPATIBLE_DATA_YES;
1622 struct st_mysql_storage_engine csv_storage_engine=
1623 { MYSQL_HANDLERTON_INTERFACE_VERSION };
1625 mysql_declare_plugin(csv)
1627 MYSQL_STORAGE_ENGINE_PLUGIN,
1628 &csv_storage_engine,
1629 "CSV",
1630 "Brian Aker, MySQL AB",
1631 "CSV storage engine",
1632 PLUGIN_LICENSE_GPL,
1633 tina_init_func, /* Plugin Init */
1634 tina_done_func, /* Plugin Deinit */
1635 0x0100 /* 1.0 */,
1636 NULL, /* status variables */
1637 NULL, /* system variables */
1638 NULL /* config options */
1640 mysql_declare_plugin_end;