ha_tina.cpp
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:23k
- /* Copyright (C) 2003 MySQL AB
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
- /*
- Make sure to look at ha_tina.h for more details.
- First off, this is a play thing for me, there are a number of things wrong with it:
- *) It was designed for csv and therefor its performance is highly questionable.
- *) Indexes have not been implemented. This is because the files can be traded in
- and out of the table directory without having to worry about rebuilding anything.
- *) NULLs and "" are treated equally (like a spreadsheet).
- *) There was in the beginning no point to anyone seeing this other then me, so there
- is a good chance that I haven't quite documented it well.
- *) Less design, more "make it work"
- Now there are a few cool things with it:
- *) Errors can result in corrupted data files.
- *) Data files can be read by spreadsheets directly.
- TODO:
- *) Move to a block system for larger files
- *) Error recovery, its all there, just need to finish it
- *) Document how the chains work.
- -Brian
- */
- #ifdef USE_PRAGMA_IMPLEMENTATION
- #pragma implementation // gcc: Class implementation
- #endif
- #include "mysql_priv.h"
- #ifdef HAVE_CSV_DB
- #include "ha_tina.h"
- #include <sys/mman.h>
- /* Stuff for shares */
- pthread_mutex_t tina_mutex;
- static HASH tina_open_tables;
- static int tina_init= 0;
- /*****************************************************************************
- ** TINA tables
- *****************************************************************************/
- /*
- Used for sorting chains with qsort().
- */
- int sort_set (tina_set *a, tina_set *b)
- {
- /*
- We assume that intervals do not intersect. So, it is enought to compare
- any two points. Here we take start of intervals for comparison.
- */
- return ( a->begin > b->begin ? -1 : ( a->begin < b->begin ? 1 : 0 ) );
- }
- static byte* tina_get_key(TINA_SHARE *share,uint *length,
- my_bool not_used __attribute__((unused)))
- {
- *length=share->table_name_length;
- return (byte*) share->table_name;
- }
- /*
- Reloads the mmap file.
- */
- int get_mmap(TINA_SHARE *share, int write)
- {
- DBUG_ENTER("ha_tina::get_mmap");
- if (share->mapped_file && munmap(share->mapped_file, share->file_stat.st_size))
- DBUG_RETURN(1);
- if (my_fstat(share->data_file, &share->file_stat, MYF(MY_WME)) == -1)
- DBUG_RETURN(1);
- if (share->file_stat.st_size)
- {
- if (write)
- share->mapped_file= (byte *)mmap(NULL, share->file_stat.st_size,
- PROT_READ|PROT_WRITE, MAP_SHARED,
- share->data_file, 0);
- else
- share->mapped_file= (byte *)mmap(NULL, share->file_stat.st_size,
- PROT_READ, MAP_PRIVATE,
- share->data_file, 0);
- if ((share->mapped_file ==(caddr_t)-1))
- {
- /*
- Bad idea you think? See the problem is that nothing actually checks
- the return value of ::rnd_init(), so tossing an error is about
- it for us.
- Never going to happen right? :)
- */
- my_message(errno, "Woops, blew up opening a mapped file", 0);
- DBUG_ASSERT(0);
- DBUG_RETURN(1);
- }
- }
- else
- share->mapped_file= NULL;
- DBUG_RETURN(0);
- }
- /*
- Simple lock controls.
- */
- static TINA_SHARE *get_share(const char *table_name, TABLE *table)
- {
- TINA_SHARE *share;
- char *tmp_name;
- uint length;
- if (!tina_init)
- {
- /* Hijack a mutex for init'ing the storage engine */
- pthread_mutex_lock(&LOCK_mysql_create_db);
- if (!tina_init)
- {
- tina_init++;
- VOID(pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST));
- (void) hash_init(&tina_open_tables,system_charset_info,32,0,0,
- (hash_get_key) tina_get_key,0,0);
- }
- pthread_mutex_unlock(&LOCK_mysql_create_db);
- }
- pthread_mutex_lock(&tina_mutex);
- length=(uint) strlen(table_name);
- if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables,
- (byte*) table_name,
- length)))
- {
- char data_file_name[FN_REFLEN];
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &share, sizeof(*share),
- &tmp_name, length+1,
- NullS))
- {
- pthread_mutex_unlock(&tina_mutex);
- return NULL;
- }
- share->use_count=0;
- share->table_name_length=length;
- share->table_name=tmp_name;
- strmov(share->table_name,table_name);
- fn_format(data_file_name, table_name, "", ".CSV",MY_REPLACE_EXT|MY_UNPACK_FILENAME);
- if (my_hash_insert(&tina_open_tables, (byte*) share))
- goto error;
- thr_lock_init(&share->lock);
- pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST);
- if ((share->data_file= my_open(data_file_name, O_RDWR|O_APPEND,
- MYF(0))) == -1)
- goto error2;
- /* We only use share->data_file for writing, so we scan to the end to append */
- if (my_seek(share->data_file, 0, SEEK_END, MYF(0)) == MY_FILEPOS_ERROR)
- goto error2;
- share->mapped_file= NULL; // We don't know the state since we just allocated it
- if (get_mmap(share, 0) > 0)
- goto error3;
- }
- share->use_count++;
- pthread_mutex_unlock(&tina_mutex);
- return share;
- error3:
- my_close(share->data_file,MYF(0));
- error2:
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- error:
- pthread_mutex_unlock(&tina_mutex);
- my_free((gptr) share, MYF(0));
- return NULL;
- }
- /*
- Free lock controls.
- */
- static int free_share(TINA_SHARE *share)
- {
- DBUG_ENTER("ha_tina::free_share");
- pthread_mutex_lock(&tina_mutex);
- int result_code= 0;
- if (!--share->use_count){
- /* Drop the mapped file */
- if (share->mapped_file)
- munmap(share->mapped_file, share->file_stat.st_size);
- result_code= my_close(share->data_file,MYF(0));
- hash_delete(&tina_open_tables, (byte*) share);
- thr_lock_delete(&share->lock);
- pthread_mutex_destroy(&share->mutex);
- my_free((gptr) share, MYF(0));
- }
- pthread_mutex_unlock(&tina_mutex);
- DBUG_RETURN(result_code);
- }
- /*
- Finds the end of a line.
- Currently only supports files written on a UNIX OS.
- */
- byte * find_eoln(byte *data, off_t begin, off_t end)
- {
- for (off_t x= begin; x < end; x++)
- if (data[x] == 'n')
- return data + x;
- return 0;
- }
- /*
- Encode a buffer into the quoted format.
- */
- int ha_tina::encode_quote(byte *buf)
- {
- char attribute_buffer[1024];
- String attribute(attribute_buffer, sizeof(attribute_buffer), &my_charset_bin);
- buffer.length(0);
- for (Field **field=table->field ; *field ; field++)
- {
- const char *ptr;
- const char *end_ptr;
- (*field)->val_str(&attribute,&attribute);
- ptr= attribute.ptr();
- end_ptr= attribute.length() + ptr;
- buffer.append('"');
- while (ptr < end_ptr)
- {
- if (*ptr == '"')
- {
- buffer.append('\');
- buffer.append('"');
- *ptr++;
- }
- else if (*ptr == 'r')
- {
- buffer.append('\');
- buffer.append('r');
- *ptr++;
- }
- else if (*ptr == '\')
- {
- buffer.append('\');
- buffer.append('\');
- *ptr++;
- }
- else if (*ptr == 'n')
- {
- buffer.append('\');
- buffer.append('n');
- *ptr++;
- }
- else
- buffer.append(*ptr++);
- }
- buffer.append('"');
- buffer.append(',');
- }
- // Remove the comma, add a line feed
- buffer.length(buffer.length() - 1);
- buffer.append('n');
- //buffer.replace(buffer.length(), 0, "n", 1);
- return (buffer.length());
- }
- /*
- chain_append() adds delete positions to the chain that we use to keep track of space.
- */
- int ha_tina::chain_append()
- {
- if ( chain_ptr != chain && (chain_ptr -1)->end == current_position)
- (chain_ptr -1)->end= next_position;
- else
- {
- /* We set up for the next position */
- if ((off_t)(chain_ptr - chain) == (chain_size -1))
- {
- off_t location= chain_ptr - chain;
- chain_size += DEFAULT_CHAIN_LENGTH;
- if (chain_alloced)
- {
- /* Must cast since my_malloc unlike malloc doesn't have a void ptr */
- if ((chain= (tina_set *)my_realloc((gptr)chain,chain_size,MYF(MY_WME))) == NULL)
- return -1;
- }
- else
- {
- tina_set *ptr= (tina_set *)my_malloc(chain_size * sizeof(tina_set),MYF(MY_WME));
- memcpy(ptr, chain, DEFAULT_CHAIN_LENGTH * sizeof(tina_set));
- chain= ptr;
- chain_alloced++;
- }
- chain_ptr= chain + location;
- }
- chain_ptr->begin= current_position;
- chain_ptr->end= next_position;
- chain_ptr++;
- }
- return 0;
- }
- /*
- Scans for a row.
- */
- int ha_tina::find_current_row(byte *buf)
- {
- byte *mapped_ptr= (byte *)share->mapped_file + current_position;
- byte *end_ptr;
- DBUG_ENTER("ha_tina::find_current_row");
- /* EOF should be counted as new line */
- if ((end_ptr= find_eoln(share->mapped_file, current_position, share->file_stat.st_size)) == 0)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- for (Field **field=table->field ; *field ; field++)
- {
- buffer.length(0);
- mapped_ptr++; // Increment past the first quote
- for(;mapped_ptr != end_ptr; mapped_ptr++)
- {
- //Need to convert line feeds!
- if (*mapped_ptr == '"' &&
- (((mapped_ptr[1] == ',') && (mapped_ptr[2] == '"')) || (mapped_ptr == end_ptr -1 )))
- {
- mapped_ptr += 2; // Move past the , and the "
- break;
- }
- if (*mapped_ptr == '\' && mapped_ptr != (end_ptr - 1))
- {
- mapped_ptr++;
- if (*mapped_ptr == 'r')
- buffer.append('r');
- else if (*mapped_ptr == 'n' )
- buffer.append('n');
- else if ((*mapped_ptr == '\') || (*mapped_ptr == '"'))
- buffer.append(*mapped_ptr);
- else /* This could only happed with an externally created file */
- {
- buffer.append('\');
- buffer.append(*mapped_ptr);
- }
- }
- else
- buffer.append(*mapped_ptr);
- }
- (*field)->store(buffer.ptr(), buffer.length(), system_charset_info);
- }
- next_position= (end_ptr - share->mapped_file)+1;
- /* Maybe use N for null? */
- memset(buf, 0, table->null_bytes); /* We do not implement nulls! */
- DBUG_RETURN(0);
- }
- /*
- If frm_error() is called in table.cc this is called to find out what file
- extensions exist for this handler.
- */
- const char **ha_tina::bas_ext() const
- { static const char *ext[]= { ".CSV", NullS }; return ext; }
- /*
- Open a database file. Keep in mind that tables are caches, so
- this will not be called for every request. Any sort of positions
- that need to be reset should be kept in the ::extra() call.
- */
- int ha_tina::open(const char *name, int mode, uint test_if_locked)
- {
- DBUG_ENTER("ha_tina::open");
- if (!(share= get_share(name, table)))
- DBUG_RETURN(1);
- thr_lock_data_init(&share->lock,&lock,NULL);
- ref_length=sizeof(off_t);
- DBUG_RETURN(0);
- }
- /*
- Close a database file. We remove ourselves from the shared strucutre.
- If it is empty we destroy it and free the mapped file.
- */
- int ha_tina::close(void)
- {
- DBUG_ENTER("ha_tina::close");
- DBUG_RETURN(free_share(share));
- }
- /*
- This is an INSERT. At the moment this handler just seeks to the end
- of the file and appends the data. In an error case it really should
- just truncate to the original position (this is not done yet).
- */
- int ha_tina::write_row(byte * buf)
- {
- int size;
- DBUG_ENTER("ha_tina::write_row");
- statistic_increment(ha_write_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT)
- table->timestamp_field->set_time();
- size= encode_quote(buf);
- if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
- DBUG_RETURN(-1);
- /*
- Ok, this is means that we will be doing potentially bad things
- during a bulk insert on some OS'es. What we need is a cleanup
- call for ::write_row that would let us fix up everything after the bulk
- insert. The archive handler does this with an extra mutx call, which
- might be a solution for this.
- */
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
- DBUG_RETURN(0);
- }
- /*
- This is called for an update.
- Make sure you put in code to increment the auto increment, also
- update any timestamp data. Currently auto increment is not being
- fixed since autoincrements have yet to be added to this table handler.
- This will be called in a table scan right before the previous ::rnd_next()
- call.
- */
- int ha_tina::update_row(const byte * old_data, byte * new_data)
- {
- int size;
- DBUG_ENTER("ha_tina::update_row");
- statistic_increment(ha_update_count,&LOCK_status);
- if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE)
- table->timestamp_field->set_time();
- size= encode_quote(new_data);
- if (chain_append())
- DBUG_RETURN(-1);
- if (my_write(share->data_file, buffer.ptr(), size, MYF(MY_WME | MY_NABP)))
- DBUG_RETURN(-1);
- DBUG_RETURN(0);
- }
- /*
- Deletes a row. First the database will find the row, and then call this method.
- In the case of a table scan, the previous call to this will be the ::rnd_next()
- that found this row.
- The exception to this is an ORDER BY. This will cause the table handler to walk
- the table noting the positions of all rows that match a query. The table will
- then be deleted/positioned based on the ORDER (so RANDOM, DESC, ASC).
- */
- int ha_tina::delete_row(const byte * buf)
- {
- DBUG_ENTER("ha_tina::delete_row");
- statistic_increment(ha_delete_count,&LOCK_status);
- if (chain_append())
- DBUG_RETURN(-1);
- --records;
- DBUG_RETURN(0);
- }
- /*
- Fill buf with value from key. Simply this is used for a single index read
- with a key.
- */
- int ha_tina::index_read(byte * buf, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
- {
- DBUG_ENTER("ha_tina::index_read");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- Fill buf with value from key. Simply this is used for a single index read
- with a key.
- Whatever the current key is we will use it. This is what will be in "index".
- */
- int ha_tina::index_read_idx(byte * buf, uint index, const byte * key,
- uint key_len __attribute__((unused)),
- enum ha_rkey_function find_flag
- __attribute__((unused)))
- {
- DBUG_ENTER("ha_tina::index_read_idx");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- Read the next position in the index.
- */
- int ha_tina::index_next(byte * buf)
- {
- DBUG_ENTER("ha_tina::index_next");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- Read the previous position in the index.
- */
- int ha_tina::index_prev(byte * buf)
- {
- DBUG_ENTER("ha_tina::index_prev");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- Read the first position in the index
- */
- int ha_tina::index_first(byte * buf)
- {
- DBUG_ENTER("ha_tina::index_first");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- Read the last position in the index
- With this we don't need to do a filesort() with index.
- We just read the last row and call previous.
- */
- int ha_tina::index_last(byte * buf)
- {
- DBUG_ENTER("ha_tina::index_last");
- DBUG_ASSERT(0);
- DBUG_RETURN(HA_ADMIN_NOT_IMPLEMENTED);
- }
- /*
- All table scans call this first.
- The order of a table scan is:
- ha_tina::store_lock
- ha_tina::external_lock
- ha_tina::info
- ha_tina::rnd_init
- ha_tina::extra
- ENUM HA_EXTRA_CACHE Cash record in HA_rrnd()
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::rnd_next
- ha_tina::extra
- ENUM HA_EXTRA_NO_CACHE End cacheing of records (def)
- ha_tina::external_lock
- ha_tina::extra
- ENUM HA_EXTRA_RESET Reset database to after open
- Each call to ::rnd_next() represents a row returned in the can. When no more
- rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE.
- The ::info() call is just for the optimizer.
- */
- int ha_tina::rnd_init(bool scan)
- {
- DBUG_ENTER("ha_tina::rnd_init");
- current_position= next_position= 0;
- records= 0;
- chain_ptr= chain;
- #ifdef HAVE_MADVISE
- if (scan)
- (void)madvise(share->mapped_file,share->file_stat.st_size,MADV_SEQUENTIAL);
- #endif
- DBUG_RETURN(0);
- }
- /*
- ::rnd_next() does all the heavy lifting for a table scan. You will need to populate *buf
- with the correct field data. You can walk the field to determine at what position you
- should store the data (take a look at how ::find_current_row() works). The structure
- is something like:
- 0Foo Dog Friend
- The first offset is for the first attribute. All space before that is reserved for null count.
- Basically this works as a mask for which rows are nulled (compared to just empty).
- This table handler doesn't do nulls and does not know the difference between NULL and "". This
- is ok since this table handler is for spreadsheets and they don't know about them either :)
- */
- int ha_tina::rnd_next(byte *buf)
- {
- DBUG_ENTER("ha_tina::rnd_next");
- statistic_increment(ha_read_rnd_next_count,&LOCK_status);
- current_position= next_position;
- if (!share->mapped_file)
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- if (HA_ERR_END_OF_FILE == find_current_row(buf) )
- DBUG_RETURN(HA_ERR_END_OF_FILE);
- records++;
- DBUG_RETURN(0);
- }
- /*
- In the case of an order by rows will need to be sorted.
- ::position() is called after each call to ::rnd_next(),
- the data it stores is to a byte array. You can store this
- data via ha_store_ptr(). ref_length is a variable defined to the
- class that is the sizeof() of position being stored. In our case
- its just a position. Look at the bdb code if you want to see a case
- where something other then a number is stored.
- */
- void ha_tina::position(const byte *record)
- {
- DBUG_ENTER("ha_tina::position");
- ha_store_ptr(ref, ref_length, current_position);
- DBUG_VOID_RETURN;
- }
- /*
- Used to fetch a row from a posiion stored with ::position().
- ha_get_ptr() retrieves the data for you.
- */
- int ha_tina::rnd_pos(byte * buf, byte *pos)
- {
- DBUG_ENTER("ha_tina::rnd_pos");
- statistic_increment(ha_read_rnd_count,&LOCK_status);
- current_position= ha_get_ptr(pos,ref_length);
- DBUG_RETURN(find_current_row(buf));
- }
- /*
- ::info() is used to return information to the optimizer.
- Currently this table handler doesn't implement most of the fields
- really needed. SHOW also makes use of this data
- */
- void ha_tina::info(uint flag)
- {
- DBUG_ENTER("ha_tina::info");
- /* This is a lie, but you don't want the optimizer to see zero or 1 */
- if (records < 2)
- records= 2;
- DBUG_VOID_RETURN;
- }
- /*
- Grab bag of flags that are sent to the able handler every so often.
- HA_EXTRA_RESET and HA_EXTRA_RESET_STATE are the most frequently called.
- You are not required to implement any of these.
- */
- int ha_tina::extra(enum ha_extra_function operation)
- {
- DBUG_ENTER("ha_tina::extra");
- DBUG_RETURN(0);
- }
- /*
- This is no longer used.
- */
- int ha_tina::reset(void)
- {
- DBUG_ENTER("ha_tina::reset");
- ha_tina::extra(HA_EXTRA_RESET);
- DBUG_RETURN(0);
- }
- /*
- Called after deletes, inserts, and updates. This is where we clean up all of
- the dead space we have collected while writing the file.
- */
- int ha_tina::rnd_end()
- {
- DBUG_ENTER("ha_tina::rnd_end");
- /* First position will be truncate position, second will be increment */
- if ((chain_ptr - chain) > 0)
- {
- tina_set *ptr;
- off_t length;
- /*
- Setting up writable map, this will contain all of the data after the
- get_mmap call that we have added to the file.
- */
- if (get_mmap(share, 1) > 0)
- DBUG_RETURN(-1);
- length= share->file_stat.st_size;
- /*
- The sort handles updates/deletes with random orders.
- It also sorts so that we move the final blocks to the
- beginning so that we move the smallest amount of data possible.
- */
- qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), (qsort_cmp)sort_set);
- for (ptr= chain; ptr < chain_ptr; ptr++)
- {
- memmove(share->mapped_file + ptr->begin, share->mapped_file + ptr->end,
- length - (size_t)ptr->end);
- length= length - (size_t)(ptr->end - ptr->begin);
- }
- /* Truncate the file to the new size */
- if (my_chsize(share->data_file, length, 0, MYF(MY_WME)))
- DBUG_RETURN(-1);
- if (munmap(share->mapped_file, length))
- DBUG_RETURN(-1);
- /* We set it to null so that get_mmap() won't try to unmap it */
- share->mapped_file= NULL;
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
- }
- DBUG_RETURN(0);
- }
- /*
- Truncate table and others of its ilk call this.
- */
- int ha_tina::delete_all_rows()
- {
- DBUG_ENTER("ha_tina::delete_all_rows");
- int rc= my_chsize(share->data_file, 0, 0, MYF(MY_WME));
- if (get_mmap(share, 0) > 0)
- DBUG_RETURN(-1);
- DBUG_RETURN(rc);
- }
- /*
- Always called by the start of a transaction (or by "lock tables");
- */
- int ha_tina::external_lock(THD *thd, int lock_type)
- {
- DBUG_ENTER("ha_tina::external_lock");
- DBUG_RETURN(0); // No external locking
- }
- /*
- Called by the database to lock the table. Keep in mind that this
- is an internal lock.
- */
- THR_LOCK_DATA **ha_tina::store_lock(THD *thd,
- THR_LOCK_DATA **to,
- enum thr_lock_type lock_type)
- {
- if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK)
- lock.type=lock_type;
- *to++= &lock;
- return to;
- }
- /*
- Range optimizer calls this.
- I need to update the information on this.
- */
- ha_rows ha_tina::records_in_range(int inx,
- const byte *start_key,uint start_key_len,
- enum ha_rkey_function start_search_flag,
- const byte *end_key,uint end_key_len,
- enum ha_rkey_function end_search_flag)
- {
- DBUG_ENTER("ha_tina::records_in_range ");
- DBUG_RETURN(records); // Good guess
- }
- /*
- Create a table. You do not want to leave the table open after a call to
- this (the database will call ::open() if it needs to).
- */
- int ha_tina::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info)
- {
- char name_buff[FN_REFLEN];
- File create_file;
- DBUG_ENTER("ha_tina::create");
- if ((create_file= my_create(fn_format(name_buff,name,"",".CSV",MY_REPLACE_EXT|MY_UNPACK_FILENAME),0,
- O_RDWR | O_TRUNC,MYF(MY_WME))) < 0)
- DBUG_RETURN(-1);
- my_close(create_file,MYF(0));
- DBUG_RETURN(0);
- }
- #endif /* enable CSV */