diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs index e2304cd..b171700 100644 --- a/src/vtab/csvtab.rs +++ b/src/vtab/csvtab.rs @@ -1,7 +1,9 @@ //! CSV Virtual Table extern crate csv; +use std::ffi::CStr; use std::fs::File; use std::mem; +use std::str; use libc; use {Connection, Error, Result}; @@ -9,8 +11,6 @@ use ffi; use types::Null; use vtab::{declare_vtab, VTab, VTabCursor}; -use self::csv::Reader; - pub fn load_module(conn: &Connection) -> Result<()> { let aux: Option<()> = None; conn.create_module("csv", &CSV_MODULE, aux) @@ -33,23 +33,77 @@ struct CSVTab { impl VTab for CSVTab { fn create(db: *mut ffi::sqlite3, - aux: *mut libc::c_void, - argc: libc::c_int, - _argv: *const *const libc::c_char) + _aux: *mut libc::c_void, + args: &[*const libc::c_char]) -> Result { - if argc < 4 { + if args.len() < 4 { return Err(Error::ModuleError(format!("no CSV file specified"))); } - //let filename = ; - let reader = try!(csv::Reader::from_file("FIXME")); + // pull out name of csv file (remove quotes) + let mut c_filename = unsafe { CStr::from_ptr(args[3]).to_bytes() }; + if c_filename[0] == b'\'' { + c_filename = &c_filename[1..c_filename.len() - 1]; + } + let filename = try!(str::from_utf8(c_filename)); + let mut reader = try!(csv::Reader::from_file(filename)).has_headers(false); // TODO flexible ? + let mut cols = Vec::new(); + + let args = &args[4..]; + for c_arg in args { + let c_slice = unsafe { CStr::from_ptr(*c_arg).to_bytes() }; + if c_slice.len() == 1 { + reader = reader.delimiter(c_slice[0]); + } else if c_slice.len() == 3 && c_slice[0] == b'\'' { + reader = reader.delimiter(c_slice[1]); + } else { + let arg = try!(str::from_utf8(c_slice)); + let uc = arg.to_uppercase(); + if uc.contains("HEADER") { + reader = reader.has_headers(true); + } else if uc.contains("NO_QUOTE") { + reader = reader.quote(0); + } else { + cols.push(String::from(arg)); + } + } + } + + let mut offset_first_row = 0; + if reader.has_headers { + let headers = try!(reader.headers()); + offset_first_row = reader.byte_offset(); + // headers ignored if cols is not empty + if cols.is_empty() { + cols = headers; + } + } + + if cols.is_empty() { + return Err(Error::ModuleError(format!("no column name specified"))); + } + + let mut sql = String::from("CREATE TABLE x("); + for (i, col) in cols.iter().enumerate() { + if col.is_empty() { + return Err(Error::ModuleError(format!("no column name found"))); + } + sql.push('"'); + sql.push_str(col); + sql.push('"'); + if i == cols.len() - 1 { + sql.push_str(");"); + } else { + sql.push_str(", "); + } + } + let vtab = CSVTab { base: Default::default(), reader: reader, - offset_first_row: 0, - cols: vec![], // FIXME + offset_first_row: offset_first_row, + cols: cols, }; - unimplemented!(); - try!(declare_vtab(db, "CREATE TABLE x FIXME")); + try!(declare_vtab(db, &sql)); Ok(vtab) } @@ -102,7 +156,7 @@ impl VTabCursor for CSVTabCursor { } fn eof(&self) -> bool { let vtab = self.vtab(); - unsafe { (*vtab).reader.done() } + vtab.reader.done() } fn column(&self, ctx: *mut ffi::sqlite3_context, col: libc::c_int) -> Result<()> { use functions::ToResult; diff --git a/src/vtab/int_array.rs b/src/vtab/int_array.rs index 00b1d06..17fde9a 100644 --- a/src/vtab/int_array.rs +++ b/src/vtab/int_array.rs @@ -46,8 +46,7 @@ struct IntArrayVTab { impl VTab for IntArrayVTab { fn create(db: *mut ffi::sqlite3, aux: *mut libc::c_void, - _argc: libc::c_int, - _argv: *const *const libc::c_char) + _args: &[*const libc::c_char]) -> Result { let array = unsafe { mem::transmute(aux) }; let vtab = IntArrayVTab { diff --git a/src/vtab/mod.rs b/src/vtab/mod.rs index 5d7bd3a..6ecb36a 100644 --- a/src/vtab/mod.rs +++ b/src/vtab/mod.rs @@ -40,10 +40,9 @@ use ffi; pub trait VTab>: Sized { fn create(db: *mut ffi::sqlite3, aux: *mut libc::c_void, - _argc: libc::c_int, - _argv: *const *const libc::c_char) + args: &[*const libc::c_char]) -> Result; - fn best_index(&self, _info: *mut ffi::sqlite3_index_info); + fn best_index(&self, info: *mut ffi::sqlite3_index_info); fn open(&self) -> Result; } @@ -52,7 +51,7 @@ pub trait VTabCursor>: Sized { fn filter(&mut self) -> Result<()>; fn next(&mut self) -> Result<()>; fn eof(&self) -> bool; - fn column(&self, ctx: *mut ffi::sqlite3_context, _i: libc::c_int) -> Result<()>; + fn column(&self, ctx: *mut ffi::sqlite3_context, i: libc::c_int) -> Result<()>; fn rowid(&self) -> Result; } @@ -156,8 +155,10 @@ unsafe extern "C" fn $create(db: *mut ffi::sqlite3, err_msg: *mut *mut libc::c_char) -> libc::c_int { use std::error::Error as StdError; + use std::slice; use vtab::mprintf; - match $vtab::create(db, aux, argc, argv) { + let args = slice::from_raw_parts(argv, argc as usize); + match $vtab::create(db, aux, args) { Ok(vtab) => { let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab)); *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;