00001 /* Copyright (C) 2003 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 Make sure to look at ha_tina.h for more details. 00019 00020 First off, this is a play thing for me, there are a number of things 00021 wrong with it: 00022 *) It was designed for csv and therefore its performance is highly 00023 questionable. 00024 *) Indexes have not been implemented. This is because the files can 00025 be traded in and out of the table directory without having to worry 00026 about rebuilding anything. 00027 *) NULLs and "" are treated equally (like a spreadsheet). 00028 *) There was in the beginning no point to anyone seeing this other 00029 then me, so there is a good chance that I haven't quite documented 00030 it well. 00031 *) Less design, more "make it work" 00032 00033 Now there are a few cool things with it: 00034 *) Errors can result in corrupted data files. 00035 *) Data files can be read by spreadsheets directly. 00036 00037 TODO: 00038 *) Move to a block system for larger files 00039 *) Error recovery, its all there, just need to finish it 00040 *) Document how the chains work. 00041 00042 -Brian 00043 */ 00044 00045 #ifdef USE_PRAGMA_IMPLEMENTATION 00046 #pragma implementation // gcc: Class implementation 00047 #endif 00048 00049 #include "mysql_priv.h" 00050 00051 #include "ha_tina.h" 00052 00053 #include <mysql/plugin.h> 00054 00055 /* 00056 uchar + uchar + ulonglong + ulonglong + ulonglong + ulonglong + uchar 00057 */ 00058 #define META_BUFFER_SIZE sizeof(uchar) + sizeof(uchar) + sizeof(ulonglong) \ 00059 + sizeof(ulonglong) + sizeof(ulonglong) + sizeof(ulonglong) + sizeof(uchar) 00060 #define TINA_CHECK_HEADER 254 // The number we use to determine corruption 00061 00062 /* The file extension */ 00063 #define CSV_EXT ".CSV" // The data file 00064 #define CSN_EXT ".CSN" // Files used during repair and update 00065 #define CSM_EXT ".CSM" // Meta file 00066 00067 00068 static TINA_SHARE *get_share(const char *table_name, TABLE *table); 00069 static int free_share(TINA_SHARE *share); 00070 static int read_meta_file(File meta_file, ha_rows *rows); 00071 static int write_meta_file(File meta_file, ha_rows rows, bool dirty); 00072 00073 /* Stuff for shares */ 00074 pthread_mutex_t tina_mutex; 00075 static HASH tina_open_tables; 00076 static int tina_init= 0; 00077 static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root); 00078 static int tina_init_func(); 00079 00080 off_t Transparent_file::read_next() 00081 { 00082 off_t bytes_read; 00083 00084 /* 00085 No need to seek here, as the file managed by Transparent_file class 00086 always points to upper_bound byte 00087 */ 00088 if ((bytes_read= my_read(filedes, buff, buff_size, MYF(0))) == MY_FILE_ERROR) 00089 return -1; 00090 00091 /* end of file */ 00092 if (!bytes_read) 00093 return -1; 00094 00095 lower_bound= upper_bound; 00096 upper_bound+= bytes_read; 00097 00098 return lower_bound; 00099 } 00100 00101 00102 char Transparent_file::get_value(off_t offset) 00103 { 00104 off_t bytes_read; 00105 00106 /* check boundaries */ 00107 if ((lower_bound <= offset) && (offset < upper_bound)) 00108 return buff[offset - lower_bound]; 00109 else 00110 { 00111 VOID(my_seek(filedes, offset, MY_SEEK_SET, MYF(0))); 00112 /* read appropriate portion of the file */ 00113 if ((bytes_read= my_read(filedes, buff, buff_size, 00114 MYF(0))) == MY_FILE_ERROR) 00115 return 0; 00116 00117 lower_bound= offset; 00118 upper_bound= lower_bound + bytes_read; 00119 00120 /* end of file */ 00121 if (upper_bound == offset) 00122 return 0; 00123 00124 return buff[0]; 00125 } 00126 } 00127 handlerton tina_hton; 00128 00129 /***************************************************************************** 00130 ** TINA tables 00131 *****************************************************************************/ 00132 00133 /* 00134 Used for sorting chains with qsort(). 00135 */ 00136 int sort_set (tina_set *a, tina_set *b) 00137 { 00138 /* 00139 We assume that intervals do not intersect. So, it is enought to compare 00140 any two points. Here we take start of intervals for comparison. 00141 */ 00142 return ( a->begin > b->begin ? 1 : ( a->begin < b->begin ? -1 : 0 ) ); 00143 } 00144 00145 static byte* tina_get_key(TINA_SHARE *share,uint *length, 00146 my_bool not_used __attribute__((unused))) 00147 { 00148 *length=share->table_name_length; 00149 return (byte*) share->table_name; 00150 } 00151 00152 static int tina_init_func() 00153 { 00154 if (!tina_init) 00155 { 00156 tina_init++; 00157 VOID(pthread_mutex_init(&tina_mutex,MY_MUTEX_INIT_FAST)); 00158 (void) hash_init(&tina_open_tables,system_charset_info,32,0,0, 00159 (hash_get_key) tina_get_key,0,0); 00160 tina_hton.state= SHOW_OPTION_YES; 00161 tina_hton.db_type= DB_TYPE_CSV_DB; 00162 tina_hton.create= tina_create_handler; 00163 tina_hton.panic= tina_end; 00164 tina_hton.flags= HTON_CAN_RECREATE; 00165 } 00166 return 0; 00167 } 00168 00169 static int tina_done_func() 00170 { 00171 if (tina_init) 00172 { 00173 if (tina_open_tables.records) 00174 { 00175 return 1; 00176 } 00177 hash_free(&tina_open_tables); 00178 pthread_mutex_destroy(&tina_mutex); 00179 tina_init--; 00180 } 00181 return 0; 00182 } 00183 00184 00185 /* 00186 Simple lock controls. 00187 */ 00188 static TINA_SHARE *get_share(const char *table_name, TABLE *table) 00189 { 00190 TINA_SHARE *share; 00191 char meta_file_name[FN_REFLEN]; 00192 MY_STAT file_stat; /* Stat information for the data file */ 00193 char *tmp_name; 00194 uint length; 00195 00196 if (!tina_init) 00197 tina_init_func(); 00198 00199 pthread_mutex_lock(&tina_mutex); 00200 length=(uint) strlen(table_name); 00201 00202 /* 00203 If share is not present in the hash, create a new share and 00204 initialize its members. 00205 */ 00206 if (!(share=(TINA_SHARE*) hash_search(&tina_open_tables, 00207 (byte*) table_name, 00208 length))) 00209 { 00210 if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), 00211 &share, sizeof(*share), 00212 &tmp_name, length+1, 00213 NullS)) 00214 { 00215 pthread_mutex_unlock(&tina_mutex); 00216 return NULL; 00217 } 00218 00219 share->use_count= 0; 00220 share->is_log_table= FALSE; 00221 share->table_name_length= length; 00222 share->table_name= tmp_name; 00223 share->crashed= FALSE; 00224 share->rows_recorded= 0; 00225 share->update_file_opened= FALSE; 00226 share->tina_write_opened= FALSE; 00227 strmov(share->table_name, table_name); 00228 fn_format(share->data_file_name, table_name, "", CSV_EXT, 00229 MY_REPLACE_EXT|MY_UNPACK_FILENAME); 00230 fn_format(meta_file_name, table_name, "", CSM_EXT, 00231 MY_REPLACE_EXT|MY_UNPACK_FILENAME); 00232 00233 if (my_stat(share->data_file_name, &file_stat, MYF(MY_WME)) == NULL) 00234 goto error; 00235 share->saved_data_file_length= file_stat.st_size; 00236 00237 if (my_hash_insert(&tina_open_tables, (byte*) share)) 00238 goto error; 00239 thr_lock_init(&share->lock); 00240 pthread_mutex_init(&share->mutex,MY_MUTEX_INIT_FAST); 00241 00242 /* 00243 Open or create the meta file. In the latter case, we'll get 00244 an error during read_meta_file and mark the table as crashed. 00245 Usually this will result in auto-repair, and we will get a good 00246 meta-file in the end. 00247 */ 00248 if ((share->meta_file= my_open(meta_file_name, 00249 O_RDWR|O_CREAT, MYF(0))) == -1) 00250 share->crashed= TRUE; 00251 00252 /* 00253 If the meta file will not open we assume it is crashed and 00254 mark it as such. 00255 */ 00256 if (read_meta_file(share->meta_file, &share->rows_recorded)) 00257 share->crashed= TRUE; 00258 } 00259 share->use_count++; 00260 pthread_mutex_unlock(&tina_mutex); 00261 00262 return share; 00263 00264 error2: 00265 thr_lock_delete(&share->lock); 00266 pthread_mutex_destroy(&share->mutex); 00267 error: 00268 pthread_mutex_unlock(&tina_mutex); 00269 my_free((gptr) share, MYF(0)); 00270 00271 return NULL; 00272 } 00273 00274 00275 /* 00276 Read CSV meta-file 00277 00278 SYNOPSIS 00279 read_meta_file() 00280 meta_file The meta-file filedes 00281 ha_rows Pointer to the var we use to store rows count. 00282 These are read from the meta-file. 00283 00284 DESCRIPTION 00285 00286 Read the meta-file info. For now we are only interested in 00287 rows counf, crashed bit and magic number. 00288 00289 RETURN 00290 0 - OK 00291 non-zero - error occurred 00292 */ 00293 00294 static int read_meta_file(File meta_file, ha_rows *rows) 00295 { 00296 uchar meta_buffer[META_BUFFER_SIZE]; 00297 uchar *ptr= meta_buffer; 00298 00299 DBUG_ENTER("ha_tina::read_meta_file"); 00300 00301 VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0))); 00302 if (my_read(meta_file, (byte*)meta_buffer, META_BUFFER_SIZE, 0) 00303 != META_BUFFER_SIZE) 00304 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00305 00306 /* 00307 Parse out the meta data, we ignore version at the moment 00308 */ 00309 00310 ptr+= sizeof(uchar)*2; // Move past header 00311 *rows= (ha_rows)uint8korr(ptr); 00312 ptr+= sizeof(ulonglong); // Move past rows 00313 /* 00314 Move past check_point, auto_increment and forced_flushes fields. 00315 They are present in the format, but we do not use them yet. 00316 */ 00317 ptr+= 3*sizeof(ulonglong); 00318 00319 /* check crashed bit and magic number */ 00320 if ((meta_buffer[0] != (uchar)TINA_CHECK_HEADER) || 00321 ((bool)(*ptr)== TRUE)) 00322 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00323 00324 my_sync(meta_file, MYF(MY_WME)); 00325 00326 DBUG_RETURN(0); 00327 } 00328 00329 00330 /* 00331 Write CSV meta-file 00332 00333 SYNOPSIS 00334 write_meta_file() 00335 meta_file The meta-file filedes 00336 ha_rows The number of rows we have in the datafile. 00337 dirty A flag, which marks whether we have a corrupt table 00338 00339 DESCRIPTION 00340 00341 Write meta-info the the file. Only rows count, crashed bit and 00342 magic number matter now. 00343 00344 RETURN 00345 0 - OK 00346 non-zero - error occurred 00347 */ 00348 00349 static int write_meta_file(File meta_file, ha_rows rows, bool dirty) 00350 { 00351 uchar meta_buffer[META_BUFFER_SIZE]; 00352 uchar *ptr= meta_buffer; 00353 00354 DBUG_ENTER("ha_tina::write_meta_file"); 00355 00356 *ptr= (uchar)TINA_CHECK_HEADER; 00357 ptr+= sizeof(uchar); 00358 *ptr= (uchar)TINA_VERSION; 00359 ptr+= sizeof(uchar); 00360 int8store(ptr, (ulonglong)rows); 00361 ptr+= sizeof(ulonglong); 00362 memset(ptr, 0, 3*sizeof(ulonglong)); 00363 /* 00364 Skip over checkpoint, autoincrement and forced_flushes fields. 00365 We'll need them later. 00366 */ 00367 ptr+= 3*sizeof(ulonglong); 00368 *ptr= (uchar)dirty; 00369 00370 VOID(my_seek(meta_file, 0, MY_SEEK_SET, MYF(0))); 00371 if (my_write(meta_file, (byte *)meta_buffer, META_BUFFER_SIZE, 0) 00372 != META_BUFFER_SIZE) 00373 DBUG_RETURN(-1); 00374 00375 my_sync(meta_file, MYF(MY_WME)); 00376 00377 DBUG_RETURN(0); 00378 } 00379 00380 bool ha_tina::check_and_repair(THD *thd) 00381 { 00382 HA_CHECK_OPT check_opt; 00383 DBUG_ENTER("ha_tina::check_and_repair"); 00384 00385 check_opt.init(); 00386 00387 DBUG_RETURN(repair(thd, &check_opt)); 00388 } 00389 00390 00391 int ha_tina::init_tina_writer() 00392 { 00393 DBUG_ENTER("ha_tina::init_tina_writer"); 00394 00395 /* 00396 Mark the file as crashed. We will set the flag back when we close 00397 the file. In the case of the crash it will remain marked crashed, 00398 which enforce recovery. 00399 */ 00400 (void)write_meta_file(share->meta_file, share->rows_recorded, TRUE); 00401 00402 if ((share->tina_write_filedes= 00403 my_open(share->data_file_name, O_RDWR|O_APPEND, MYF(0))) == -1) 00404 { 00405 DBUG_PRINT("info", ("Could not open tina file writes")); 00406 share->crashed= TRUE; 00407 DBUG_RETURN(1); 00408 } 00409 share->tina_write_opened= TRUE; 00410 00411 DBUG_RETURN(0); 00412 } 00413 00414 00415 bool ha_tina::is_crashed() const 00416 { 00417 DBUG_ENTER("ha_tina::is_crashed"); 00418 DBUG_RETURN(share->crashed); 00419 } 00420 00421 /* 00422 Free lock controls. 00423 */ 00424 static int free_share(TINA_SHARE *share) 00425 { 00426 DBUG_ENTER("ha_tina::free_share"); 00427 pthread_mutex_lock(&tina_mutex); 00428 int result_code= 0; 00429 if (!--share->use_count){ 00430 /* Write the meta file. Mark it as crashed if needed. */ 00431 (void)write_meta_file(share->meta_file, share->rows_recorded, 00432 share->crashed ? TRUE :FALSE); 00433 if (my_close(share->meta_file, MYF(0))) 00434 result_code= 1; 00435 if (share->tina_write_opened) 00436 { 00437 if (my_close(share->tina_write_filedes, MYF(0))) 00438 result_code= 1; 00439 share->tina_write_opened= FALSE; 00440 } 00441 00442 hash_delete(&tina_open_tables, (byte*) share); 00443 thr_lock_delete(&share->lock); 00444 pthread_mutex_destroy(&share->mutex); 00445 my_free((gptr) share, MYF(0)); 00446 } 00447 pthread_mutex_unlock(&tina_mutex); 00448 00449 DBUG_RETURN(result_code); 00450 } 00451 00452 int tina_end(ha_panic_function type) 00453 { 00454 return tina_done_func(); 00455 } 00456 00457 00458 /* 00459 This function finds the end of a line and returns the length 00460 of the line ending. 00461 00462 We support three kinds of line endings: 00463 '\r' -- Old Mac OS line ending 00464 '\n' -- Traditional Unix and Mac OS X line ending 00465 '\r''\n' -- DOS\Windows line ending 00466 */ 00467 00468 off_t find_eoln_buff(Transparent_file *data_buff, off_t begin, 00469 off_t end, int *eoln_len) 00470 { 00471 *eoln_len= 0; 00472 00473 for (off_t x= begin; x < end; x++) 00474 { 00475 /* Unix (includes Mac OS X) */ 00476 if (data_buff->get_value(x) == '\n') 00477 *eoln_len= 1; 00478 else 00479 if (data_buff->get_value(x) == '\r') // Mac or Dos 00480 { 00481 /* old Mac line ending */ 00482 if (x + 1 == end || (data_buff->get_value(x + 1) != '\n')) 00483 *eoln_len= 1; 00484 else // DOS style ending 00485 *eoln_len= 2; 00486 } 00487 00488 if (*eoln_len) // end of line was found 00489 return x; 00490 } 00491 00492 return 0; 00493 } 00494 00495 00496 static handler *tina_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) 00497 { 00498 return new (mem_root) ha_tina(table); 00499 } 00500 00501 00502 ha_tina::ha_tina(TABLE_SHARE *table_arg) 00503 :handler(&tina_hton, table_arg), 00504 /* 00505 These definitions are found in handler.h 00506 They are not probably completely right. 00507 */ 00508 current_position(0), next_position(0), local_saved_data_file_length(0), 00509 file_buff(0), chain_alloced(0), chain_size(DEFAULT_CHAIN_LENGTH), 00510 records_is_known(0) 00511 { 00512 /* Set our original buffers from pre-allocated memory */ 00513 buffer.set((char*)byte_buffer, IO_SIZE, system_charset_info); 00514 chain= chain_buffer; 00515 file_buff= new Transparent_file(); 00516 } 00517 00518 00519 /* 00520 Encode a buffer into the quoted format. 00521 */ 00522 00523 int ha_tina::encode_quote(byte *buf) 00524 { 00525 char attribute_buffer[1024]; 00526 String attribute(attribute_buffer, sizeof(attribute_buffer), 00527 &my_charset_bin); 00528 00529 my_bitmap_map *org_bitmap= dbug_tmp_use_all_columns(table, table->read_set); 00530 buffer.length(0); 00531 for (Field **field=table->field ; *field ; field++) 00532 { 00533 const char *ptr; 00534 const char *end_ptr; 00535 00536 /* 00537 Write an empty string to the buffer in case of a NULL value. 00538 Basically this is a safety check, as no one ensures that the 00539 field content is cleaned up every time we use Field::set_null() 00540 in the code. 00541 */ 00542 if ((*field)->is_null()) 00543 ptr= end_ptr= 0; 00544 else 00545 { 00546 (*field)->val_str(&attribute,&attribute); 00547 ptr= attribute.ptr(); 00548 end_ptr= attribute.length() + ptr; 00549 } 00550 00551 buffer.append('"'); 00552 00553 while (ptr < end_ptr) 00554 { 00555 if (*ptr == '"') 00556 { 00557 buffer.append('\\'); 00558 buffer.append('"'); 00559 *ptr++; 00560 } 00561 else if (*ptr == '\r') 00562 { 00563 buffer.append('\\'); 00564 buffer.append('r'); 00565 *ptr++; 00566 } 00567 else if (*ptr == '\\') 00568 { 00569 buffer.append('\\'); 00570 buffer.append('\\'); 00571 *ptr++; 00572 } 00573 else if (*ptr == '\n') 00574 { 00575 buffer.append('\\'); 00576 buffer.append('n'); 00577 *ptr++; 00578 } 00579 else 00580 buffer.append(*ptr++); 00581 } 00582 buffer.append('"'); 00583 buffer.append(','); 00584 } 00585 // Remove the comma, add a line feed 00586 buffer.length(buffer.length() - 1); 00587 buffer.append('\n'); 00588 //buffer.replace(buffer.length(), 0, "\n", 1); 00589 00590 dbug_tmp_restore_column_map(table->read_set, org_bitmap); 00591 return (buffer.length()); 00592 } 00593 00594 /* 00595 chain_append() adds delete positions to the chain that we use to keep 00596 track of space. Then the chain will be used to cleanup "holes", occurred 00597 due to deletes and updates. 00598 */ 00599 int ha_tina::chain_append() 00600 { 00601 if ( chain_ptr != chain && (chain_ptr -1)->end == current_position) 00602 (chain_ptr -1)->end= next_position; 00603 else 00604 { 00605 /* We set up for the next position */ 00606 if ((off_t)(chain_ptr - chain) == (chain_size -1)) 00607 { 00608 off_t location= chain_ptr - chain; 00609 chain_size += DEFAULT_CHAIN_LENGTH; 00610 if (chain_alloced) 00611 { 00612 /* Must cast since my_malloc unlike malloc doesn't have a void ptr */ 00613 if ((chain= (tina_set *) my_realloc((gptr)chain, 00614 chain_size, MYF(MY_WME))) == NULL) 00615 return -1; 00616 } 00617 else 00618 { 00619 tina_set *ptr= (tina_set *) my_malloc(chain_size * sizeof(tina_set), 00620 MYF(MY_WME)); 00621 memcpy(ptr, chain, DEFAULT_CHAIN_LENGTH * sizeof(tina_set)); 00622 chain= ptr; 00623 chain_alloced++; 00624 } 00625 chain_ptr= chain + location; 00626 } 00627 chain_ptr->begin= current_position; 00628 chain_ptr->end= next_position; 00629 chain_ptr++; 00630 } 00631 00632 return 0; 00633 } 00634 00635 00636 /* 00637 Scans for a row. 00638 */ 00639 int ha_tina::find_current_row(byte *buf) 00640 { 00641 off_t end_offset, curr_offset= current_position; 00642 int eoln_len; 00643 my_bitmap_map *org_bitmap; 00644 DBUG_ENTER("ha_tina::find_current_row"); 00645 00646 /* 00647 We do not read further then local_saved_data_file_length in order 00648 not to conflict with undergoing concurrent insert. 00649 */ 00650 if ((end_offset= 00651 find_eoln_buff(file_buff, current_position, 00652 local_saved_data_file_length, &eoln_len)) == 0) 00653 DBUG_RETURN(HA_ERR_END_OF_FILE); 00654 00655 /* Avoid asserts in ::store() for columns that are not going to be updated */ 00656 org_bitmap= dbug_tmp_use_all_columns(table, table->write_set); 00657 00658 for (Field **field=table->field ; *field ; field++) 00659 { 00660 buffer.length(0); 00661 if (file_buff->get_value(curr_offset) == '"') 00662 curr_offset++; // Incrementpast the first quote 00663 else 00664 { 00665 dbug_tmp_restore_column_map(table->write_set, org_bitmap); 00666 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00667 } 00668 for(;curr_offset != end_offset; curr_offset++) 00669 { 00670 // Need to convert line feeds! 00671 if (file_buff->get_value(curr_offset) == '"' && 00672 (((file_buff->get_value(curr_offset + 1) == ',') && 00673 (file_buff->get_value(curr_offset + 2) == '"')) || 00674 (curr_offset == end_offset -1 ))) 00675 { 00676 curr_offset+= 2; // Move past the , and the " 00677 break; 00678 } 00679 if (file_buff->get_value(curr_offset) == '\\' && 00680 curr_offset != (end_offset - 1)) 00681 { 00682 curr_offset++; 00683 if (file_buff->get_value(curr_offset) == 'r') 00684 buffer.append('\r'); 00685 else if (file_buff->get_value(curr_offset) == 'n' ) 00686 buffer.append('\n'); 00687 else if ((file_buff->get_value(curr_offset) == '\\') || 00688 (file_buff->get_value(curr_offset) == '"')) 00689 buffer.append(file_buff->get_value(curr_offset)); 00690 else /* This could only happed with an externally created file */ 00691 { 00692 buffer.append('\\'); 00693 buffer.append(file_buff->get_value(curr_offset)); 00694 } 00695 } 00696 else // ordinary symbol 00697 { 00698 /* 00699 We are at final symbol and no last quote was found => 00700 we are working with a damaged file. 00701 */ 00702 if (curr_offset == end_offset - 1) 00703 { 00704 dbug_tmp_restore_column_map(table->write_set, org_bitmap); 00705 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00706 } 00707 buffer.append(file_buff->get_value(curr_offset)); 00708 } 00709 } 00710 if (bitmap_is_set(table->read_set, (*field)->field_index)) 00711 (*field)->store(buffer.ptr(), buffer.length(), system_charset_info); 00712 } 00713 next_position= end_offset + eoln_len; 00714 /* Maybe use \N for null? */ 00715 memset(buf, 0, table->s->null_bytes); /* We do not implement nulls! */ 00716 dbug_tmp_restore_column_map(table->write_set, org_bitmap); 00717 00718 DBUG_RETURN(0); 00719 } 00720 00721 /* 00722 If frm_error() is called in table.cc this is called to find out what file 00723 extensions exist for this handler. 00724 */ 00725 static const char *ha_tina_exts[] = { 00726 CSV_EXT, 00727 CSM_EXT, 00728 NullS 00729 }; 00730 00731 const char **ha_tina::bas_ext() const 00732 { 00733 return ha_tina_exts; 00734 } 00735 00736 /* 00737 Three functions below are needed to enable concurrent insert functionality 00738 for CSV engine. For more details see mysys/thr_lock.c 00739 */ 00740 00741 void tina_get_status(void* param, int concurrent_insert) 00742 { 00743 ha_tina *tina= (ha_tina*) param; 00744 tina->get_status(); 00745 } 00746 00747 void tina_update_status(void* param) 00748 { 00749 ha_tina *tina= (ha_tina*) param; 00750 tina->update_status(); 00751 } 00752 00753 /* this should exist and return 0 for concurrent insert to work */ 00754 my_bool tina_check_status(void* param) 00755 { 00756 return 0; 00757 } 00758 00759 /* 00760 Save the state of the table 00761 00762 SYNOPSIS 00763 get_status() 00764 00765 DESCRIPTION 00766 This function is used to retrieve the file length. During the lock 00767 phase of concurrent insert. For more details see comment to 00768 ha_tina::update_status below. 00769 */ 00770 00771 void ha_tina::get_status() 00772 { 00773 if (share->is_log_table) 00774 { 00775 /* 00776 We have to use mutex to follow pthreads memory visibility 00777 rules for share->saved_data_file_length 00778 */ 00779 pthread_mutex_lock(&share->mutex); 00780 local_saved_data_file_length= share->saved_data_file_length; 00781 pthread_mutex_unlock(&share->mutex); 00782 return; 00783 } 00784 local_saved_data_file_length= share->saved_data_file_length; 00785 } 00786 00787 00788 /* 00789 Correct the state of the table. Called by unlock routines 00790 before the write lock is released. 00791 00792 SYNOPSIS 00793 update_status() 00794 00795 DESCRIPTION 00796 When we employ concurrent insert lock, we save current length of the file 00797 during the lock phase. We do not read further saved value, as we don't 00798 want to interfere with undergoing concurrent insert. Writers update file 00799 length info during unlock with update_status(). 00800 00801 NOTE 00802 For log tables concurrent insert works different. The reason is that 00803 log tables are always opened and locked. And as they do not unlock 00804 tables, the file length after writes should be updated in a different 00805 way. For this purpose we need is_log_table flag. When this flag is set 00806 we call update_status() explicitly after each row write. 00807 */ 00808 00809 void ha_tina::update_status() 00810 { 00811 /* correct local_saved_data_file_length for writers */ 00812 share->saved_data_file_length= local_saved_data_file_length; 00813 } 00814 00815 00816 bool ha_tina::check_if_locking_is_allowed(uint sql_command, 00817 ulong type, TABLE *table, 00818 uint count, 00819 bool called_by_logger_thread) 00820 { 00821 if (!called_by_logger_thread) 00822 return check_if_log_table_locking_is_allowed(sql_command, type, table); 00823 00824 return TRUE; 00825 } 00826 00827 /* 00828 Open a database file. Keep in mind that tables are caches, so 00829 this will not be called for every request. Any sort of positions 00830 that need to be reset should be kept in the ::extra() call. 00831 */ 00832 int ha_tina::open(const char *name, int mode, uint open_options) 00833 { 00834 DBUG_ENTER("ha_tina::open"); 00835 00836 if (!(share= get_share(name, table))) 00837 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 00838 00839 if (share->crashed && !(open_options & HA_OPEN_FOR_REPAIR)) 00840 { 00841 free_share(share); 00842 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00843 } 00844 00845 if ((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1) 00846 DBUG_RETURN(0); 00847 00848 /* 00849 Init locking. Pass handler object to the locking routines, 00850 so that they could save/update local_saved_data_file_length value 00851 during locking. This is needed to enable concurrent inserts. 00852 */ 00853 thr_lock_data_init(&share->lock, &lock, (void*) this); 00854 ref_length=sizeof(off_t); 00855 00856 share->lock.get_status= tina_get_status; 00857 share->lock.update_status= tina_update_status; 00858 share->lock.check_status= tina_check_status; 00859 00860 DBUG_RETURN(0); 00861 } 00862 00863 00864 /* 00865 Close a database file. We remove ourselves from the shared strucutre. 00866 If it is empty we destroy it. 00867 */ 00868 int ha_tina::close(void) 00869 { 00870 int rc= 0; 00871 DBUG_ENTER("ha_tina::close"); 00872 rc= my_close(data_file, MYF(0)); 00873 DBUG_RETURN(free_share(share) || rc); 00874 } 00875 00876 /* 00877 This is an INSERT. At the moment this handler just seeks to the end 00878 of the file and appends the data. In an error case it really should 00879 just truncate to the original position (this is not done yet). 00880 */ 00881 int ha_tina::write_row(byte * buf) 00882 { 00883 int size; 00884 DBUG_ENTER("ha_tina::write_row"); 00885 00886 if (share->crashed) 00887 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 00888 00889 ha_statistic_increment(&SSV::ha_write_count); 00890 00891 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_INSERT) 00892 table->timestamp_field->set_time(); 00893 00894 size= encode_quote(buf); 00895 00896 if (!share->tina_write_opened) 00897 if (init_tina_writer()) 00898 DBUG_RETURN(-1); 00899 00900 /* use pwrite, as concurrent reader could have changed the position */ 00901 if (my_write(share->tina_write_filedes, (byte*)buffer.ptr(), size, 00902 MYF(MY_WME | MY_NABP))) 00903 DBUG_RETURN(-1); 00904 00905 /* update local copy of the max position to see our own changes */ 00906 local_saved_data_file_length+= size; 00907 00908 /* update shared info */ 00909 pthread_mutex_lock(&share->mutex); 00910 share->rows_recorded++; 00911 /* update status for the log tables */ 00912 if (share->is_log_table) 00913 update_status(); 00914 pthread_mutex_unlock(&share->mutex); 00915 00916 stats.records++; 00917 DBUG_RETURN(0); 00918 } 00919 00920 00921 int ha_tina::open_update_temp_file_if_needed() 00922 { 00923 char updated_fname[FN_REFLEN]; 00924 00925 if (!share->update_file_opened) 00926 { 00927 if ((update_temp_file= 00928 my_create(fn_format(updated_fname, share->table_name, 00929 "", CSN_EXT, 00930 MY_REPLACE_EXT | MY_UNPACK_FILENAME), 00931 0, O_RDWR | O_TRUNC, MYF(MY_WME))) < 0) 00932 return 1; 00933 share->update_file_opened= TRUE; 00934 } 00935 return 0; 00936 } 00937 00938 /* 00939 This is called for an update. 00940 Make sure you put in code to increment the auto increment, also 00941 update any timestamp data. Currently auto increment is not being 00942 fixed since autoincrements have yet to be added to this table handler. 00943 This will be called in a table scan right before the previous ::rnd_next() 00944 call. 00945 */ 00946 int ha_tina::update_row(const byte * old_data, byte * new_data) 00947 { 00948 int size; 00949 DBUG_ENTER("ha_tina::update_row"); 00950 00951 ha_statistic_increment(&SSV::ha_read_rnd_next_count); 00952 00953 if (table->timestamp_field_type & TIMESTAMP_AUTO_SET_ON_UPDATE) 00954 table->timestamp_field->set_time(); 00955 00956 size= encode_quote(new_data); 00957 00958 if (chain_append()) 00959 DBUG_RETURN(-1); 00960 00961 if (open_update_temp_file_if_needed()) 00962 DBUG_RETURN(-1); 00963 00964 if (my_write(update_temp_file, (byte*)buffer.ptr(), size, 00965 MYF(MY_WME | MY_NABP))) 00966 DBUG_RETURN(-1); 00967 00968 /* UPDATE should never happen on the log tables */ 00969 DBUG_ASSERT(!share->is_log_table); 00970 00971 DBUG_RETURN(0); 00972 } 00973 00974 00975 /* 00976 Deletes a row. First the database will find the row, and then call this 00977 method. In the case of a table scan, the previous call to this will be 00978 the ::rnd_next() that found this row. 00979 The exception to this is an ORDER BY. This will cause the table handler 00980 to walk the table noting the positions of all rows that match a query. 00981 The table will then be deleted/positioned based on the ORDER (so RANDOM, 00982 DESC, ASC). 00983 */ 00984 int ha_tina::delete_row(const byte * buf) 00985 { 00986 DBUG_ENTER("ha_tina::delete_row"); 00987 ha_statistic_increment(&SSV::ha_delete_count); 00988 00989 if (chain_append()) 00990 DBUG_RETURN(-1); 00991 00992 stats.records--; 00993 00994 /* DELETE should never happen on the log table */ 00995 DBUG_ASSERT(!share->is_log_table); 00996 00997 DBUG_RETURN(0); 00998 } 00999 01000 01001 /* 01002 All table scans call this first. 01003 The order of a table scan is: 01004 01005 ha_tina::store_lock 01006 ha_tina::external_lock 01007 ha_tina::info 01008 ha_tina::rnd_init 01009 ha_tina::extra 01010 ENUM HA_EXTRA_CACHE Cash record in HA_rrnd() 01011 ha_tina::rnd_next 01012 ha_tina::rnd_next 01013 ha_tina::rnd_next 01014 ha_tina::rnd_next 01015 ha_tina::rnd_next 01016 ha_tina::rnd_next 01017 ha_tina::rnd_next 01018 ha_tina::rnd_next 01019 ha_tina::rnd_next 01020 ha_tina::extra 01021 ENUM HA_EXTRA_NO_CACHE End cacheing of records (def) 01022 ha_tina::external_lock 01023 ha_tina::extra 01024 ENUM HA_EXTRA_RESET Reset database to after open 01025 01026 Each call to ::rnd_next() represents a row returned in the can. When no more 01027 rows can be returned, rnd_next() returns a value of HA_ERR_END_OF_FILE. 01028 The ::info() call is just for the optimizer. 01029 01030 */ 01031 01032 int ha_tina::rnd_init(bool scan) 01033 { 01034 DBUG_ENTER("ha_tina::rnd_init"); 01035 01036 /* set buffer to the beginning of the file */ 01037 file_buff->init_buff(data_file); 01038 if (share->crashed) 01039 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 01040 01041 current_position= next_position= 0; 01042 stats.records= 0; 01043 records_is_known= 0; 01044 chain_ptr= chain; 01045 01046 DBUG_RETURN(0); 01047 } 01048 01049 /* 01050 ::rnd_next() does all the heavy lifting for a table scan. You will need to 01051 populate *buf with the correct field data. You can walk the field to 01052 determine at what position you should store the data (take a look at how 01053 ::find_current_row() works). The structure is something like: 01054 0Foo Dog Friend 01055 The first offset is for the first attribute. All space before that is 01056 reserved for null count. 01057 Basically this works as a mask for which rows are nulled (compared to just 01058 empty). 01059 This table handler doesn't do nulls and does not know the difference between 01060 NULL and "". This is ok since this table handler is for spreadsheets and 01061 they don't know about them either :) 01062 */ 01063 int ha_tina::rnd_next(byte *buf) 01064 { 01065 int rc; 01066 DBUG_ENTER("ha_tina::rnd_next"); 01067 01068 if (share->crashed) 01069 DBUG_RETURN(HA_ERR_CRASHED_ON_USAGE); 01070 01071 ha_statistic_increment(&SSV::ha_read_rnd_next_count); 01072 01073 current_position= next_position; 01074 01075 /* don't scan an empty file */ 01076 if (!local_saved_data_file_length) 01077 DBUG_RETURN(HA_ERR_END_OF_FILE); 01078 01079 if ((rc= find_current_row(buf))) 01080 DBUG_RETURN(rc); 01081 01082 stats.records++; 01083 DBUG_RETURN(0); 01084 } 01085 01086 /* 01087 In the case of an order by rows will need to be sorted. 01088 ::position() is called after each call to ::rnd_next(), 01089 the data it stores is to a byte array. You can store this 01090 data via my_store_ptr(). ref_length is a variable defined to the 01091 class that is the sizeof() of position being stored. In our case 01092 its just a position. Look at the bdb code if you want to see a case 01093 where something other then a number is stored. 01094 */ 01095 void ha_tina::position(const byte *record) 01096 { 01097 DBUG_ENTER("ha_tina::position"); 01098 my_store_ptr(ref, ref_length, current_position); 01099 DBUG_VOID_RETURN; 01100 } 01101 01102 01103 /* 01104 Used to fetch a row from a posiion stored with ::position(). 01105 my_get_ptr() retrieves the data for you. 01106 */ 01107 01108 int ha_tina::rnd_pos(byte * buf, byte *pos) 01109 { 01110 DBUG_ENTER("ha_tina::rnd_pos"); 01111 ha_statistic_increment(&SSV::ha_read_rnd_next_count); 01112 current_position= (off_t)my_get_ptr(pos,ref_length); 01113 DBUG_RETURN(find_current_row(buf)); 01114 } 01115 01116 /* 01117 ::info() is used to return information to the optimizer. 01118 Currently this table handler doesn't implement most of the fields 01119 really needed. SHOW also makes use of this data 01120 */ 01121 void ha_tina::info(uint flag) 01122 { 01123 DBUG_ENTER("ha_tina::info"); 01124 /* This is a lie, but you don't want the optimizer to see zero or 1 */ 01125 if (!records_is_known && stats.records < 2) 01126 stats.records= 2; 01127 DBUG_VOID_RETURN; 01128 } 01129 01130 /* 01131 Grab bag of flags that are sent to the able handler every so often. 01132 HA_EXTRA_RESET and HA_EXTRA_RESET_STATE are the most frequently called. 01133 You are not required to implement any of these. 01134 */ 01135 int ha_tina::extra(enum ha_extra_function operation) 01136 { 01137 DBUG_ENTER("ha_tina::extra"); 01138 if (operation == HA_EXTRA_MARK_AS_LOG_TABLE) 01139 { 01140 pthread_mutex_lock(&share->mutex); 01141 share->is_log_table= TRUE; 01142 pthread_mutex_unlock(&share->mutex); 01143 } 01144 DBUG_RETURN(0); 01145 } 01146 01147 /* 01148 Set end_pos to the last valid byte of continuous area, closest 01149 to the given "hole", stored in the buffer. "Valid" here means, 01150 not listed in the chain of deleted records ("holes"). 01151 */ 01152 bool ha_tina::get_write_pos(off_t *end_pos, tina_set *closest_hole) 01153 { 01154 if (closest_hole == chain_ptr) /* no more chains */ 01155 *end_pos= file_buff->end(); 01156 else 01157 *end_pos= min(file_buff->end(), 01158 closest_hole->begin); 01159 return (closest_hole != chain_ptr) && (*end_pos == closest_hole->begin); 01160 } 01161 01162 01163 /* 01164 Called after each table scan. In particular after deletes, 01165 and updates. In the last case we employ chain of deleted 01166 slots to clean up all of the dead space we have collected while 01167 performing deletes/updates. 01168 */ 01169 int ha_tina::rnd_end() 01170 { 01171 char updated_fname[FN_REFLEN]; 01172 off_t file_buffer_start= 0; 01173 DBUG_ENTER("ha_tina::rnd_end"); 01174 01175 records_is_known= 1; 01176 01177 if ((chain_ptr - chain) > 0) 01178 { 01179 tina_set *ptr= chain; 01180 01181 /* 01182 Re-read the beginning of a file (as the buffer should point to the 01183 end of file after the scan). 01184 */ 01185 file_buff->init_buff(data_file); 01186 01187 /* 01188 The sort is needed when there were updates/deletes with random orders. 01189 It sorts so that we move the firts blocks to the beginning. 01190 */ 01191 qsort(chain, (size_t)(chain_ptr - chain), sizeof(tina_set), 01192 (qsort_cmp)sort_set); 01193 01194 off_t write_begin= 0, write_end; 01195 01196 /* create the file to write updated table if it wasn't yet created */ 01197 if (open_update_temp_file_if_needed()) 01198 DBUG_RETURN(-1); 01199 01200 /* write the file with updated info */ 01201 while ((file_buffer_start != -1)) // while not end of file 01202 { 01203 bool in_hole= get_write_pos(&write_end, ptr); 01204 01205 /* if there is something to write, write it */ 01206 if ((write_end - write_begin) && 01207 (my_write(update_temp_file, 01208 (byte*)(file_buff->ptr() + 01209 (write_begin - file_buff->start())), 01210 write_end - write_begin, MYF_RW))) 01211 goto error; 01212 01213 if (in_hole) 01214 { 01215 /* skip hole */ 01216 while (file_buff->end() <= ptr->end && file_buffer_start != -1) 01217 file_buffer_start= file_buff->read_next(); 01218 write_begin= ptr->end; 01219 ptr++; 01220 } 01221 else 01222 write_begin= write_end; 01223 01224 if (write_end == file_buff->end()) 01225 file_buffer_start= file_buff->read_next(); /* shift the buffer */ 01226 01227 } 01228 01229 if (my_sync(update_temp_file, MYF(MY_WME)) || 01230 my_close(update_temp_file, MYF(0))) 01231 DBUG_RETURN(-1); 01232 01233 share->update_file_opened= FALSE; 01234 01235 if (share->tina_write_opened) 01236 { 01237 if (my_close(share->tina_write_filedes, MYF(0))) 01238 DBUG_RETURN(-1); 01239 /* 01240 Mark that the writer fd is closed, so that init_tina_writer() 01241 will reopen it later. 01242 */ 01243 share->tina_write_opened= FALSE; 01244 } 01245 01246 /* 01247 Close opened fildes's. Then move updated file in place 01248 of the old datafile. 01249 */ 01250 if (my_close(data_file, MYF(0)) || 01251 my_rename(fn_format(updated_fname, share->table_name, "", CSN_EXT, 01252 MY_REPLACE_EXT | MY_UNPACK_FILENAME), 01253 share->data_file_name, MYF(0))) 01254 DBUG_RETURN(-1); 01255 01256 /* Open the file again */ 01257 if (((data_file= my_open(share->data_file_name, O_RDONLY, MYF(0))) == -1)) 01258 DBUG_RETURN(-1); 01259 /* 01260 The datafile is consistent at this point and the write filedes is 01261 closed, so nothing worrying will happen to it in case of a crash. 01262 Here we record this fact to the meta-file. 01263 */ 01264 (void)write_meta_file(share->meta_file, share->rows_recorded, FALSE); 01265 } 01266 01267 DBUG_RETURN(0); 01268 error: 01269 my_close(update_temp_file, MYF(0)); 01270 share->update_file_opened= FALSE; 01271 DBUG_RETURN(-1); 01272 } 01273 01274 01275 /* 01276 Repair CSV table in the case, it is crashed. 01277 01278 SYNOPSIS 01279 repair() 01280 thd The thread, performing repair 01281 check_opt The options for repair. We do not use it currently. 01282 01283 DESCRIPTION 01284 If the file is empty, change # of rows in the file and complete recovery. 01285 Otherwise, scan the table looking for bad rows. If none were found, 01286 we mark file as a good one and return. If a bad row was encountered, 01287 we truncate the datafile up to the last good row. 01288 01289 TODO: Make repair more clever - it should try to recover subsequent 01290 rows (after the first bad one) as well. 01291 */ 01292 01293 int ha_tina::repair(THD* thd, HA_CHECK_OPT* check_opt) 01294 { 01295 char repaired_fname[FN_REFLEN]; 01296 byte *buf; 01297 File repair_file; 01298 int rc; 01299 ha_rows rows_repaired= 0; 01300 off_t write_begin= 0, write_end; 01301 DBUG_ENTER("ha_tina::repair"); 01302 01303 /* empty file */ 01304 if (!share->saved_data_file_length) 01305 { 01306 share->rows_recorded= 0; 01307 goto end; 01308 } 01309 01310 /* Don't assert in field::val() functions */ 01311 table->use_all_columns(); 01312 if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) 01313 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 01314 01315 /* position buffer to the start of the file */ 01316 file_buff->init_buff(data_file); 01317 01318 /* 01319 Local_saved_data_file_length is initialized during the lock phase. 01320 Sometimes this is not getting executed before ::repair (e.g. for 01321 the log tables). We set it manually here. 01322 */ 01323 local_saved_data_file_length= share->saved_data_file_length; 01324 /* set current position to the beginning of the file */ 01325 current_position= next_position= 0; 01326 01327 /* Read the file row-by-row. If everything is ok, repair is not needed. */ 01328 while (!(rc= find_current_row(buf))) 01329 { 01330 rows_repaired++; 01331 current_position= next_position; 01332 } 01333 01334 my_free((char*)buf, MYF(0)); 01335 01336 if (rc == HA_ERR_END_OF_FILE) 01337 { 01338 /* 01339 All rows were read ok until end of file, the file does not need repair. 01340 If rows_recorded != rows_repaired, we should update rows_recorded value 01341 to the current amount of rows. 01342 */ 01343 share->rows_recorded= rows_repaired; 01344 goto end; 01345 } 01346 01347 /* 01348 Otherwise we've encountered a bad row => repair is needed. 01349 Let us create a temporary file. 01350 */ 01351 if ((repair_file= my_create(fn_format(repaired_fname, share->table_name, 01352 "", CSN_EXT, 01353 MY_REPLACE_EXT|MY_UNPACK_FILENAME), 01354 0, O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) 01355 DBUG_RETURN(HA_ERR_CRASHED_ON_REPAIR); 01356 01357 file_buff->init_buff(data_file); 01358 01359 01360 /* we just truncated the file up to the first bad row. update rows count. */ 01361 share->rows_recorded= rows_repaired; 01362 01363 /* write repaired file */ 01364 while (1) 01365 { 01366 write_end= min(file_buff->end(), current_position); 01367 if ((write_end - write_begin) && 01368 (my_write(repair_file, (byte*)file_buff->ptr(), 01369 write_end - write_begin, MYF_RW))) 01370 DBUG_RETURN(-1); 01371 01372 write_begin= write_end; 01373 if (write_end== current_position) 01374 break; 01375 else 01376 file_buff->read_next(); /* shift the buffer */ 01377 } 01378 01379 /* 01380 Close the files and rename repaired file to the datafile. 01381 We have to close the files, as on Windows one cannot rename 01382 a file, which descriptor is still open. EACCES will be returned 01383 when trying to delete the "to"-file in my_rename(). 01384 */ 01385 if (my_close(data_file,MYF(0)) || my_close(repair_file, MYF(0)) || 01386 my_rename(repaired_fname, share->data_file_name, MYF(0))) 01387 DBUG_RETURN(-1); 01388 01389 /* Open the file again, it should now be repaired */ 01390 if ((data_file= my_open(share->data_file_name, O_RDWR|O_APPEND, 01391 MYF(0))) == -1) 01392 DBUG_RETURN(-1); 01393 01394 /* Set new file size. The file size will be updated by ::update_status() */ 01395 local_saved_data_file_length= (size_t) current_position; 01396 01397 end: 01398 share->crashed= FALSE; 01399 DBUG_RETURN(HA_ADMIN_OK); 01400 } 01401 01402 /* 01403 DELETE without WHERE calls this 01404 */ 01405 01406 int ha_tina::delete_all_rows() 01407 { 01408 int rc; 01409 DBUG_ENTER("ha_tina::delete_all_rows"); 01410 01411 if (!records_is_known) 01412 DBUG_RETURN(my_errno=HA_ERR_WRONG_COMMAND); 01413 01414 if (!share->tina_write_opened) 01415 if (init_tina_writer()) 01416 DBUG_RETURN(-1); 01417 01418 /* Truncate the file to zero size */ 01419 rc= my_chsize(share->tina_write_filedes, 0, 0, MYF(MY_WME)); 01420 01421 stats.records=0; 01422 DBUG_RETURN(rc); 01423 } 01424 01425 /* 01426 Called by the database to lock the table. Keep in mind that this 01427 is an internal lock. 01428 */ 01429 THR_LOCK_DATA **ha_tina::store_lock(THD *thd, 01430 THR_LOCK_DATA **to, 01431 enum thr_lock_type lock_type) 01432 { 01433 if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) 01434 lock.type=lock_type; 01435 *to++= &lock; 01436 return to; 01437 } 01438 01439 /* 01440 Create a table. You do not want to leave the table open after a call to 01441 this (the database will call ::open() if it needs to). 01442 */ 01443 01444 int ha_tina::create(const char *name, TABLE *table_arg, 01445 HA_CREATE_INFO *create_info) 01446 { 01447 char name_buff[FN_REFLEN]; 01448 File create_file; 01449 DBUG_ENTER("ha_tina::create"); 01450 01451 if ((create_file= my_create(fn_format(name_buff, name, "", CSM_EXT, 01452 MY_REPLACE_EXT|MY_UNPACK_FILENAME), 0, 01453 O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) 01454 DBUG_RETURN(-1); 01455 01456 write_meta_file(create_file, 0, FALSE); 01457 my_close(create_file, MYF(0)); 01458 01459 if ((create_file= my_create(fn_format(name_buff, name, "", CSV_EXT, 01460 MY_REPLACE_EXT|MY_UNPACK_FILENAME),0, 01461 O_RDWR | O_TRUNC,MYF(MY_WME))) < 0) 01462 DBUG_RETURN(-1); 01463 01464 my_close(create_file, MYF(0)); 01465 01466 DBUG_RETURN(0); 01467 } 01468 01469 int ha_tina::check(THD* thd, HA_CHECK_OPT* check_opt) 01470 { 01471 int rc= 0; 01472 byte *buf; 01473 const char *old_proc_info; 01474 ha_rows count= share->rows_recorded; 01475 DBUG_ENTER("ha_tina::check"); 01476 01477 old_proc_info= thd_proc_info(thd, "Checking table"); 01478 if (!(buf= (byte*) my_malloc(table->s->reclength, MYF(MY_WME)))) 01479 DBUG_RETURN(HA_ERR_OUT_OF_MEM); 01480 01481 /* position buffer to the start of the file */ 01482 file_buff->init_buff(data_file); 01483 01484 /* 01485 Local_saved_data_file_length is initialized during the lock phase. 01486 Check does not use store_lock in certain cases. So, we set it 01487 manually here. 01488 */ 01489 local_saved_data_file_length= share->saved_data_file_length; 01490 /* set current position to the beginning of the file */ 01491 current_position= next_position= 0; 01492 /* Read the file row-by-row. If everything is ok, repair is not needed. */ 01493 while (!(rc= find_current_row(buf))) 01494 { 01495 count--; 01496 current_position= next_position; 01497 } 01498 01499 my_free((char*)buf, MYF(0)); 01500 thd_proc_info(thd, old_proc_info); 01501 01502 if ((rc != HA_ERR_END_OF_FILE) || count) 01503 { 01504 share->crashed= TRUE; 01505 DBUG_RETURN(HA_ADMIN_CORRUPT); 01506 } 01507 else 01508 DBUG_RETURN(HA_ADMIN_OK); 01509 } 01510 01511 01512 bool ha_tina::check_if_incompatible_data(HA_CREATE_INFO *info, 01513 uint table_changes) 01514 { 01515 return COMPATIBLE_DATA_YES; 01516 } 01517 01518 struct st_mysql_storage_engine csv_storage_engine= 01519 { MYSQL_HANDLERTON_INTERFACE_VERSION, &tina_hton }; 01520 01521 mysql_declare_plugin(csv) 01522 { 01523 MYSQL_STORAGE_ENGINE_PLUGIN, 01524 &csv_storage_engine, 01525 "CSV", 01526 "Brian Aker, MySQL AB", 01527 "CSV storage engine", 01528 tina_init_func, /* Plugin Init */ 01529 tina_done_func, /* Plugin Deinit */ 01530 0x0100 /* 1.0 */, 01531 0 01532 } 01533 mysql_declare_plugin_end; 01534
1.4.7

