📄 ha_tina.cc
字号:
/* 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;handlerton tina_hton= { "CSV", SHOW_OPTION_YES, "CSV storage engine", DB_TYPE_CSV_DB, NULL, /* One needs to be written! */ 0, /* slot */ 0, /* savepoint size. */ NULL, /* close_connection */ NULL, /* savepoint */ NULL, /* rollback to savepoint */ NULL, /* release savepoint */ NULL, /* commit */ NULL, /* rollback */ NULL, /* prepare */ NULL, /* recover */ NULL, /* commit_by_xid */ NULL, /* rollback_by_xid */ NULL, /* create_cursor_read_view */ NULL, /* set_cursor_read_view */ NULL, /* close_cursor_read_view */ HTON_CAN_RECREATE};/***************************************************************************** ** 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);}bool tina_end(){ if (tina_init) { hash_free(&tina_open_tables); VOID(pthread_mutex_destroy(&tina_mutex)); } tina_init= 0; return FALSE;}/* 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;}ha_tina::ha_tina(TABLE *table_arg) :handler(&tina_hton, table_arg), /* These definitions are found in hanler.h These are not probably completely right. */ current_position(0), next_position(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), records_is_known(0){ /* Set our original buffers from pre-allocated memory */ buffer.set(byte_buffer, IO_SIZE, system_charset_info); chain= chain_buffer;}/* 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->s->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.*/static const char *ha_tina_exts[] = { ".CSV", NullS};const char **ha_tina::bas_ext() const{ return ha_tina_exts;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -