1use crate::core::case::Payload;
2use crate::db::format::Format;
3use crate::db::layout::flatten::{FlattenLayout, FlattenVerbose};
4use crate::db::layout::table::{TableHeader, TableLayout};
5use crate::db::metadata::Metadata;
6use crate::error::MyResult;
7use crate::util::convert::format_null;
8use crate::util::painter::{Painter, Style};
9use csv::{Reader, ReaderBuilder};
10use odbc_api::buffers::TextRowSet;
11use odbc_api::handles::StatementImpl;
12use odbc_api::{Cursor, CursorImpl, DataType, Nullability};
13use std::borrow::Cow;
14use std::cell::RefCell;
15use std::cmp::Ordering;
16use std::io::Read;
17use std::iter::zip;
18
19pub type Column = (String, Format, Metadata);
20pub type Record = Vec<(String, Format)>;
21
22enum Source<'a, R: Read> {
23 Cursor(CursorImpl<StatementImpl<'a>>),
24 File(Reader<R>),
25 None,
26}
27
28pub struct Dataset<'a, R: Read> {
29 pub columns: Vec<Column>,
30 records: RefCell<Vec<(Record, bool)>>,
31 source: RefCell<Source<'a, R>>,
32 widths: RefCell<Option<Vec<Format>>>,
33 auto: bool,
34}
35
36impl<'a, R: Read> Dataset<'a, R> {
37 pub fn from_cursor(mut cursor: CursorImpl<StatementImpl<'a>>) -> MyResult<Option<Self>> {
38 let columns = fetch_columns(&mut cursor)?;
39 if !columns.is_empty() {
40 let records = RefCell::new(Vec::new());
41 let source = RefCell::new(Source::Cursor(cursor));
42 let widths = RefCell::new(None);
43 let dataset = Self { columns, records, source, widths, auto: false };
44 Ok(Some(dataset))
45 } else {
46 Ok(None)
47 }
48 }
49
50 pub fn from_file(reader: R, auto: bool) -> MyResult<Option<Self>> {
51 let mut reader = ReaderBuilder::new().from_reader(reader);
52 let headers = reader.headers()?;
53 if !headers.is_empty() {
54 let dtype = if auto { DataType::Unknown } else { DataType::Char { length: None } };
55 let columns = headers
56 .iter()
57 .map(|name| measure_text(name))
58 .map(|(name, format)| (name, format, Metadata::new(dtype, Nullability::Unknown)))
59 .collect();
60 let records = RefCell::new(Vec::new());
61 let source = RefCell::new(Source::File(reader));
62 let widths = RefCell::new(None);
63 let dataset = Self { columns, records, source, widths, auto };
64 Ok(Some(dataset))
65 } else {
66 Ok(None)
67 }
68 }
69
70 pub fn from_keywords(
71 column: &str,
72 records: Vec<&str>,
73 ) -> Self {
74 let (name, format) = measure_text(column);
75 let columns = vec![
76 (name, format, Metadata::new(DataType::Unknown, Nullability::Unknown)),
77 ];
78 let records = records
79 .into_iter()
80 .map(|value| measure_text(value))
81 .map(|pair| (vec![pair], false))
82 .collect();
83 let records = RefCell::new(records);
84 let source = RefCell::new(Source::None);
85 let widths = RefCell::new(None);
86 Self { columns, records, source, widths, auto: false }
87 }
88
89 pub fn from_tables(
91 column1: Option<&str>,
92 column2: Option<&str>,
93 column3: &str,
94 records: Vec<(&str, &str, &str)>,
95 ) -> Self {
96 let mut columns = Vec::new();
97 let mut append = |column: &str, dtype: DataType| {
98 let (name, format) = measure_text(column);
99 columns.push((name, format, Metadata::new(dtype, Nullability::Unknown)));
100 };
101 let records = if let Some(column1) = column1 {
102 if let Some(column2) = column2 {
103 append(column1, DataType::Unknown);
104 append(column2, DataType::Unknown);
105 append(column3, DataType::Unknown);
106 records
107 .into_iter()
108 .map(|(v1, v2, v3)| (measure_text(v1), measure_text(v2), measure_text(v3)))
109 .map(|(p1, p2, p3)| (vec![p1, p2, p3], false))
110 .collect()
111 } else {
112 append(column1, DataType::Unknown);
113 append(column3, DataType::Unknown);
114 records
115 .into_iter()
116 .map(|(v1, _, v3)| (measure_text(v1), measure_text(v3)))
117 .map(|(p1, p3)| (vec![p1, p3], false))
118 .collect()
119 }
120 } else {
121 append(column3, DataType::Unknown);
122 records
123 .into_iter()
124 .map(|(_, _, v3)| measure_text(v3))
125 .map(|p3| (vec![p3], false))
126 .collect()
127 };
128 let records = RefCell::new(records);
129 let source = RefCell::new(Source::None);
130 let widths = RefCell::new(None);
131 Self { columns, records, source, widths, auto: false }
132 }
133
134 pub fn from_columns(
136 column1: Option<&str>,
137 column2: Option<&str>,
138 records: Vec<(&str, &str, &str, &str, Option<&Payload>)>,
139 ) -> Self {
140 let mut columns = Vec::new();
141 let mut append = |column: &str, dtype: DataType| {
142 let (name, format) = measure_text(column);
143 columns.push((name, format, Metadata::new(dtype, Nullability::Unknown)));
144 };
145 let metadata = Metadata::new(DataType::Integer, Nullability::Unknown);
146 let records = if let Some(column1) = column1 {
147 if let Some(column2) = column2 {
148 append(column1, DataType::Unknown);
149 append(column2, DataType::Unknown);
150 append("table", DataType::Unknown);
151 append("column", DataType::Unknown);
152 append("type", DataType::Unknown);
153 append("nullable", DataType::Unknown);
154 append("index", DataType::Integer);
155 records
156 .into_iter()
157 .map(|(v1, v2, v3, v4, p5)| (
158 measure_text(v1),
159 measure_text(v2),
160 measure_text(v3),
161 measure_text(v4),
162 measure_type(p5),
163 measure_null(p5),
164 measure_index(p5, &metadata),
165 group_index(p5),
166 ))
167 .map(|(p1, p2, p3, p4, p5, p6, p7, sep)| (vec![p1, p2, p3, p4, p5, p6, p7], sep))
168 .collect()
169 } else {
170 append(column1, DataType::Unknown);
171 append("table", DataType::Unknown);
172 append("column", DataType::Unknown);
173 append("type", DataType::Unknown);
174 append("nullable", DataType::Unknown);
175 append("index", DataType::Integer);
176 records
177 .into_iter()
178 .map(|(v1, _, v3, v4, p5)| (
179 measure_text(v1),
180 measure_text(v3),
181 measure_text(v4),
182 measure_type(p5),
183 measure_null(p5),
184 measure_index(p5, &metadata),
185 group_index(p5),
186 ))
187 .map(|(p1, p3, p4, p5, p6, p7, sep)| (vec![p1, p3, p4, p5, p6, p7], sep))
188 .collect()
189 }
190 } else {
191 append("table", DataType::Unknown);
192 append("column", DataType::Unknown);
193 append("type", DataType::Unknown);
194 append("nullable", DataType::Unknown);
195 append("index", DataType::Integer);
196 records
197 .into_iter()
198 .map(|(_, _, v3, v4, p5)| (
199 measure_text(v3),
200 measure_text(v4),
201 measure_type(p5),
202 measure_null(p5),
203 measure_index(p5, &metadata),
204 group_index(p5),
205 ))
206 .map(|(p3, p4, p5, p6, p7, sep)| (vec![p3, p4, p5, p6, p7], sep))
207 .collect()
208 };
209 let records = RefCell::new(records);
210 let source = RefCell::new(Source::None);
211 let widths = RefCell::new(None);
212 Self { columns, records, source, widths, auto: false }
213 }
214
215 pub fn get_columns(&self, painter: Painter) -> Vec<String> {
216 self.columns
217 .iter()
218 .map(|(x, _, _)| painter.wrap_style(x, Style::Column))
219 .collect()
220 }
221
222 pub fn has_records(&self) -> bool {
223 !self.records.borrow().is_empty()
225 }
226
227 pub fn group_records(&mut self, group: usize) {
228 self.records.borrow_mut().sort_by(|(l, _), (r, _)| Self::compare_records(l, r, group));
232 }
233
234 fn compare_records(left: &Record, right: &Record, group: usize) -> Ordering {
235 let left = left.get(group).map(|(x, _)| x.as_str()).unwrap_or_default();
236 let right = right.get(group).map(|(x, _)| x.as_str()).unwrap_or_default();
237 left.cmp(right)
238 }
239
240 pub fn get_records<F>(&self, limit: usize, mut function: F) -> MyResult<()> where
241 F: FnMut(Vec<Cow<str>>) -> MyResult<()>,
242 {
243 match self.source.replace(Source::None) {
244 Source::Cursor(cursor) => {
245 fetch_cursor(cursor, limit, function)
246 }
247 Source::File(reader) => {
248 fetch_file(reader, function)
249 }
250 Source::None => {
251 let records = self.records.borrow();
252 for (record, _) in records.iter() {
253 let record = record.iter().map(|(x, _)| Cow::Borrowed(x.as_ref())).collect();
254 function(record)?;
255 }
256 Ok(())
257 }
258 }
259 }
260
261 pub fn get_measured<F>(&self, limit: usize, mut function: F) -> MyResult<()> where
262 F: FnMut(Record, bool) -> MyResult<()>,
263 {
264 match self.source.replace(Source::None) {
265 Source::Cursor(cursor) => {
266 fetch_cursor(cursor, limit, |record| {
267 let record = zip(record, &self.columns)
268 .map(|(value, (_, _, metadata))| measure_value(value, metadata))
269 .collect();
270 function(record, false)?;
271 Ok(())
272 })
273 }
274 Source::File(reader) => {
275 fetch_file(reader, |record| {
276 let record = record
277 .into_iter()
278 .map(measure_text)
279 .collect();
280 function(record, false)?;
281 Ok(())
282 })
283 }
284 Source::None => {
285 let mut records = self.records.borrow_mut();
286 let mut first = true;
287 for (record, sep) in records.drain(..) {
288 function(record, sep && !first)?;
289 first = false;
290 }
291 Ok(())
292 }
293 }
294 }
295
296 pub fn store_records(&mut self) -> MyResult<()> {
297 match self.source.replace(Source::None) {
298 Source::Cursor(cursor) => {
299 fetch_cursor(cursor, 4096, |record| {
300 self.push_record(record);
301 Ok(())
302 })
303 }
304 Source::File(reader) => {
305 fetch_file(reader, |record| {
306 self.push_record(record);
307 Ok(())
308 })
309 }
310 Source::None => {
311 Ok(())
312 }
313 }
314 }
315
316 pub fn push_record(&mut self, record: Vec<Cow<str>>) {
317 let record = zip(record, &self.columns)
318 .map(|(value, (_, _, metadata))| measure_value(value, metadata))
319 .collect::<Vec<_>>();
320 self.records.borrow_mut().push((record, false));
321 }
322
323 pub fn get_widths(&self) -> Vec<Format> {
324 self.widths.borrow_mut().get_or_insert_with(|| {
325 if self.auto {
332 let records = self.records.borrow_mut().drain(..)
333 .map(|(record, sep)| (self.do_reformat(record), sep))
334 .collect();
335 self.records.replace(records);
336 }
337 let mut widths = [Format::Text(0)].repeat(self.columns.len());
342 for (record, _) in self.records.borrow().iter() {
343 widths = zip(record, &widths)
344 .map(|((_, format), width)| Format::max(format, width))
345 .collect();
346 }
347 widths
348 }).clone()
349 }
350
351 fn do_reformat(&self, record: Record) -> Record {
352 zip(record, &self.columns)
356 .map(|((value, format), (_, _, metadata))| (value, metadata.do_reformat(format)))
357 .collect()
358 }
359
360 pub fn measure_table(&self) -> usize {
361 let widths = self.get_widths();
362 let total = zip(&self.columns, &widths)
363 .map(|((_, format, _), width)| Format::max(format, width))
364 .map(|format| format.total())
365 .sum::<usize>();
366 total + (widths.len() * 2)
367 }
368
369 pub fn adjust_header(&self, header: TableHeader, interact: bool) -> TableHeader {
370 if interact || self.columns.len() >= 2 { header } else { TableHeader::Simple }
371 }
372
373 pub fn into_table(self, header: TableHeader, group: Option<&str>) -> TableLayout<'a, R> {
374 TableLayout::from_dataset(self, header, group)
375 }
376
377 pub fn into_flatten(self, verbose: FlattenVerbose) -> FlattenLayout<'a, R> {
378 FlattenLayout::from_dataset(self, verbose)
379 }
380}
381
382fn fetch_columns<C: Cursor>(cursor: &mut C) -> MyResult<Vec<Column>> {
383 let size = cursor.num_result_cols()? as u16;
384 let mut columns = Vec::with_capacity(size as usize);
385 for index in 1..=size {
386 let name = cursor.col_name(index)?;
387 let dtype = cursor.col_data_type(index)?;
388 let null = cursor.col_nullability(index)?;
389 let (name, format) = measure_text(name);
390 let metadata = Metadata::new(dtype, null);
391 columns.push((name, format, metadata));
392 }
393 Ok(columns)
394}
395
396fn fetch_cursor<C, F>(
397 mut cursor: C,
398 limit: usize,
399 mut function: F,
400) -> MyResult<()> where
401 C: Cursor,
402 F: FnMut(Vec<Cow<str>>) -> MyResult<()>,
403{
404 let rowset = TextRowSet::for_cursor(5000, &mut cursor, Some(limit))?;
405 let mut cursor = cursor.bind_buffer(rowset)?;
406 while let Some(batch) = cursor.fetch()? {
407 let rows = batch.num_rows();
408 let cols = batch.num_cols();
409 for row in 0..rows {
410 let record = (0..cols)
411 .map(|col| fetch_value(batch, col, row))
412 .collect();
413 function(record)?;
414 }
415 }
416 Ok(())
417}
418
419fn fetch_value(
420 batch: &TextRowSet,
421 col: usize,
422 row: usize,
423) -> Cow<'_, str> {
424 let value = batch
425 .at_as_str(col, row)
426 .unwrap_or_default()
427 .unwrap_or_default();
428 Cow::Borrowed(value)
429}
430
431fn fetch_file<R, F>(
432 mut reader: Reader<R>,
433 mut function: F,
434) -> MyResult<()> where
435 R: Read,
436 F: FnMut(Vec<Cow<str>>) -> MyResult<()>,
437{
438 for record in reader.records() {
439 let record = record?;
440 let record = record.iter().map(Cow::from).collect();
441 function(record)?;
442 }
443 Ok(())
444}
445
446fn measure_text<V: Into<String>>(value: V) -> (String, Format) {
447 let value = value.into();
448 let length = value.chars().count();
449 let format = Format::Text(length);
450 (value, format)
451}
452
453fn measure_value<V: Into<String>>(value: V, metadata: &Metadata) -> (String, Format) {
454 let value = value.into();
455 let length = value.chars().count();
456 let format = metadata.measure_value(&value, length);
457 (value, format)
458}
459
460fn measure_type(payload: Option<&Payload>) -> (String, Format) {
461 let dtype = payload.map(|p| p.dtype.as_str()).unwrap_or_default();
462 measure_text(dtype)
463}
464
465fn measure_null(payload: Option<&Payload>) -> (String, Format) {
466 let null = payload.map(|p| p.null).unwrap_or_default();
467 measure_text(format_null(&null))
468}
469
470fn measure_index(payload: Option<&Payload>, metadata: &Metadata) -> (String, Format) {
471 let index = payload.map(|p| p.index).unwrap_or_default();
472 measure_value(index.to_string(), metadata)
473}
474
475fn group_index(payload: Option<&Payload>) -> bool {
476 let index = payload.map(|p| p.index).unwrap_or_default();
477 index == 1
478}
479
480#[cfg(test)]
481mod tests {
482 use crate::core::case::Payload;
483 use crate::db::dataset::{Dataset, Record};
484 use crate::{cow_str, str_vec};
485 use odbc_api::Nullability::{NoNulls, Nullable};
486 use pretty_assertions::assert_eq;
487 use std::borrow::Cow;
488 use std::fs::File;
489
490 #[test]
491 fn test_dataset_is_created_from_keywords() {
492 let dataset = Dataset::<File>::from_keywords("keyword", vec![
493 "ADD",
494 "ALTER",
495 "AND",
496 ]);
497 assert_eq!(get_columns(&dataset), str_vec![
498 "keyword",
499 ]);
500 assert_eq!(get_records(&dataset), vec![
501 str_vec!["ADD"],
502 str_vec!["ALTER"],
503 str_vec!["AND"],
504 ]);
505 }
506
507 #[test]
508 fn test_dataset_is_created_from_tables_with_database_and_schema() {
509 let dataset = Dataset::<File>::from_tables(Some("database"), Some("schema"), "table", vec![
510 ("Archive", "Main", "Article"),
511 ("Archive", "Main", "Journal"),
512 ("Business", "Admin", "Server"),
513 ("Business", "Main", "Account"),
514 ("Business", "Main", "Person"),
515 ]);
516 assert_eq!(get_columns(&dataset), str_vec![
517 "database",
518 "schema",
519 "table",
520 ]);
521 assert_eq!(get_records(&dataset), vec![
522 str_vec!["Archive", "Main", "Article"],
523 str_vec!["Archive", "Main", "Journal"],
524 str_vec!["Business", "Admin", "Server"],
525 str_vec!["Business", "Main", "Account"],
526 str_vec!["Business", "Main", "Person"],
527 ]);
528 }
529
530 #[test]
531 fn test_dataset_is_created_from_tables_with_database_no_schema() {
532 let dataset = Dataset::<File>::from_tables(Some("database"), None, "table", vec![
533 ("Archive", "", "Article"),
534 ("Archive", "", "Journal"),
535 ("Business", "", "Account"),
536 ("Business", "", "Person"),
537 ]);
538 assert_eq!(get_columns(&dataset), str_vec![
539 "database",
540 "table",
541 ]);
542 assert_eq!(get_records(&dataset), vec![
543 str_vec!["Archive", "Article"],
544 str_vec!["Archive", "Journal"],
545 str_vec!["Business", "Account"],
546 str_vec!["Business", "Person"],
547 ]);
548 }
549
550 #[test]
551 fn test_dataset_is_created_from_tables_no_database_or_schema() {
552 let dataset = Dataset::<File>::from_tables(None, None, "table", vec![
553 ("", "", "Account"),
554 ("", "", "Person"),
555 ]);
556 assert_eq!(get_columns(&dataset), str_vec![
557 "table",
558 ]);
559 assert_eq!(get_records(&dataset), vec![
560 str_vec!["Account"],
561 str_vec!["Person"],
562 ]);
563 }
564
565 #[test]
566 fn test_dataset_is_created_from_columns_with_database_and_schema() {
567 let dataset = Dataset::<File>::from_columns(Some("database"), Some("schema"), vec![
568 ("Archive", "Main", "Article", "Journal", Some(&Payload::new(cow_str!("INTEGER"), NoNulls, 1))),
569 ("Archive", "Main", "Article", "Date", Some(&Payload::new(cow_str!("DATE"), NoNulls, 2))),
570 ("Archive", "Main", "Article", "Author", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 3))),
571 ("Archive", "Main", "Article", "Text", Some(&Payload::new(cow_str!("VARCHAR(4000)"), NoNulls, 4))),
572 ("Archive", "Main", "Article", "Appendix", Some(&Payload::new(cow_str!("VARCHAR(4000)"), Nullable, 5))),
573 ("Archive", "Main", "Journal", "Country", Some(&Payload::new(cow_str!("INTEGER"), NoNulls, 1))),
574 ("Archive", "Main", "Journal", "Price", Some(&Payload::new(cow_str!("DECIMAL(10, 2)"), NoNulls, 2))),
575 ("Archive", "Main", "Journal", "Available", Some(&Payload::new(cow_str!("BIT"), NoNulls, 3))),
576 ("Business", "Admin", "Server", "Name", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
577 ("Business", "Admin", "Server", "Location", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
578 ("Business", "Admin", "Server", "Hardware", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 3))),
579 ("Business", "Main", "Account", "Company", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
580 ("Business", "Main", "Account", "Branch", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
581 ("Business", "Main", "Account", "Amount", Some(&Payload::new(cow_str!("DECIMAL(10, 2)"), NoNulls, 3))),
582 ("Business", "Main", "Account", "Active", Some(&Payload::new(cow_str!("BIT"), NoNulls, 4))),
583 ("Business", "Main", "Person", "Forename", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
584 ("Business", "Main", "Person", "Surname", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
585 ("Business", "Main", "Person", "Address", Some(&Payload::new(cow_str!("VARCHAR(20)"), Nullable, 3))),
586 ("Business", "Main", "Person", "Birthday", Some(&Payload::new(cow_str!("DATE"), Nullable, 4))),
587 ]);
588 assert_eq!(get_columns(&dataset), str_vec![
589 "database",
590 "schema",
591 "table",
592 "column",
593 "type",
594 "nullable",
595 "index",
596 ]);
597 assert_eq!(get_records(&dataset), vec![
598 str_vec!["Archive", "Main", "Article", "Journal", "INTEGER", "NOT NULL", "1"],
599 str_vec!["Archive", "Main", "Article", "Date", "DATE", "NOT NULL", "2"],
600 str_vec!["Archive", "Main", "Article", "Author", "VARCHAR(20)", "NOT NULL", "3"],
601 str_vec!["Archive", "Main", "Article", "Text", "VARCHAR(4000)", "NOT NULL", "4"],
602 str_vec!["Archive", "Main", "Article", "Appendix", "VARCHAR(4000)", "NULL", "5"],
603 str_vec!["Archive", "Main", "Journal", "Country", "INTEGER", "NOT NULL", "1"],
604 str_vec!["Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", "NOT NULL", "2"],
605 str_vec!["Archive", "Main", "Journal", "Available", "BIT", "NOT NULL", "3"],
606 str_vec!["Business", "Admin", "Server", "Name", "VARCHAR(20)", "NOT NULL", "1"],
607 str_vec!["Business", "Admin", "Server", "Location", "VARCHAR(20)", "NOT NULL", "2"],
608 str_vec!["Business", "Admin", "Server", "Hardware", "VARCHAR(20)", "NOT NULL", "3"],
609 str_vec!["Business", "Main", "Account", "Company", "VARCHAR(20)", "NOT NULL", "1"],
610 str_vec!["Business", "Main", "Account", "Branch", "VARCHAR(20)", "NOT NULL", "2"],
611 str_vec!["Business", "Main", "Account", "Amount", "DECIMAL(10, 2)", "NOT NULL", "3"],
612 str_vec!["Business", "Main", "Account", "Active", "BIT", "NOT NULL", "4"],
613 str_vec!["Business", "Main", "Person", "Forename", "VARCHAR(20)", "NOT NULL", "1"],
614 str_vec!["Business", "Main", "Person", "Surname", "VARCHAR(20)", "NOT NULL", "2"],
615 str_vec!["Business", "Main", "Person", "Address", "VARCHAR(20)", "NULL", "3"],
616 str_vec!["Business", "Main", "Person", "Birthday", "DATE", "NULL", "4"],
617 ]);
618 }
619
620 #[test]
621 fn test_dataset_is_created_from_columns_with_database_no_schema() {
622 let dataset = Dataset::<File>::from_columns(Some("database"), None, vec![
623 ("Archive", "", "Article", "Journal", Some(&Payload::new(cow_str!("INTEGER"), NoNulls, 1))),
624 ("Archive", "", "Article", "Date", Some(&Payload::new(cow_str!("DATE"), NoNulls, 2))),
625 ("Archive", "", "Article", "Author", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 3))),
626 ("Archive", "", "Article", "Text", Some(&Payload::new(cow_str!("VARCHAR(4000)"), NoNulls, 4))),
627 ("Archive", "", "Article", "Appendix", Some(&Payload::new(cow_str!("VARCHAR(4000)"), Nullable, 5))),
628 ("Archive", "", "Journal", "Country", Some(&Payload::new(cow_str!("INTEGER"), NoNulls, 1))),
629 ("Archive", "", "Journal", "Price", Some(&Payload::new(cow_str!("DECIMAL(10, 2)"), NoNulls, 2))),
630 ("Archive", "", "Journal", "Available", Some(&Payload::new(cow_str!("BIT"), NoNulls, 3))),
631 ("Business", "", "Account", "Company", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
632 ("Business", "", "Account", "Branch", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
633 ("Business", "", "Account", "Amount", Some(&Payload::new(cow_str!("DECIMAL(10, 2)"), NoNulls, 3))),
634 ("Business", "", "Account", "Active", Some(&Payload::new(cow_str!("BIT"), NoNulls, 4))),
635 ("Business", "", "Person", "Forename", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
636 ("Business", "", "Person", "Surname", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
637 ("Business", "", "Person", "Address", Some(&Payload::new(cow_str!("VARCHAR(20)"), Nullable, 3))),
638 ("Business", "", "Person", "Birthday", Some(&Payload::new(cow_str!("DATE"), Nullable, 4))),
639 ]);
640 assert_eq!(get_columns(&dataset), str_vec![
641 "database",
642 "table",
643 "column",
644 "type",
645 "nullable",
646 "index",
647 ]);
648 assert_eq!(get_records(&dataset), vec![
649 str_vec!["Archive", "Article", "Journal", "INTEGER", "NOT NULL", "1"],
650 str_vec!["Archive", "Article", "Date", "DATE", "NOT NULL", "2"],
651 str_vec!["Archive", "Article", "Author", "VARCHAR(20)", "NOT NULL", "3"],
652 str_vec!["Archive", "Article", "Text", "VARCHAR(4000)", "NOT NULL", "4"],
653 str_vec!["Archive", "Article", "Appendix", "VARCHAR(4000)", "NULL", "5"],
654 str_vec!["Archive", "Journal", "Country", "INTEGER", "NOT NULL", "1"],
655 str_vec!["Archive", "Journal", "Price", "DECIMAL(10, 2)", "NOT NULL", "2"],
656 str_vec!["Archive", "Journal", "Available", "BIT", "NOT NULL", "3"],
657 str_vec!["Business", "Account", "Company", "VARCHAR(20)", "NOT NULL", "1"],
658 str_vec!["Business", "Account", "Branch", "VARCHAR(20)", "NOT NULL", "2"],
659 str_vec!["Business", "Account", "Amount", "DECIMAL(10, 2)", "NOT NULL", "3"],
660 str_vec!["Business", "Account", "Active", "BIT", "NOT NULL", "4"],
661 str_vec!["Business", "Person", "Forename", "VARCHAR(20)", "NOT NULL", "1"],
662 str_vec!["Business", "Person", "Surname", "VARCHAR(20)", "NOT NULL", "2"],
663 str_vec!["Business", "Person", "Address", "VARCHAR(20)", "NULL", "3"],
664 str_vec!["Business", "Person", "Birthday", "DATE", "NULL", "4"],
665 ]);
666 }
667
668 #[test]
669 fn test_dataset_is_created_from_columns_no_database_or_schema() {
670 let dataset = Dataset::<File>::from_columns(None, None, vec![
671 ("", "", "Account", "Company", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
672 ("", "", "Account", "Branch", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
673 ("", "", "Account", "Amount", Some(&Payload::new(cow_str!("DECIMAL(10, 2)"), NoNulls, 3))),
674 ("", "", "Account", "Active", Some(&Payload::new(cow_str!("BIT"), NoNulls, 4))),
675 ("", "", "Person", "Forename", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 1))),
676 ("", "", "Person", "Surname", Some(&Payload::new(cow_str!("VARCHAR(20)"), NoNulls, 2))),
677 ("", "", "Person", "Address", Some(&Payload::new(cow_str!("VARCHAR(20)"), Nullable, 3))),
678 ("", "", "Person", "Birthday", Some(&Payload::new(cow_str!("DATE"), Nullable, 4))),
679 ]);
680 assert_eq!(get_columns(&dataset), str_vec![
681 "table",
682 "column",
683 "type",
684 "nullable",
685 "index",
686 ]);
687 assert_eq!(get_records(&dataset), vec![
688 str_vec!["Account", "Company", "VARCHAR(20)", "NOT NULL", "1"],
689 str_vec!["Account", "Branch", "VARCHAR(20)", "NOT NULL", "2"],
690 str_vec!["Account", "Amount", "DECIMAL(10, 2)", "NOT NULL", "3"],
691 str_vec!["Account", "Active", "BIT", "NOT NULL", "4"],
692 str_vec!["Person", "Forename", "VARCHAR(20)", "NOT NULL", "1"],
693 str_vec!["Person", "Surname", "VARCHAR(20)", "NOT NULL", "2"],
694 str_vec!["Person", "Address", "VARCHAR(20)", "NULL", "3"],
695 str_vec!["Person", "Birthday", "DATE", "NULL", "4"],
696 ]);
697 }
698
699 fn get_columns(dataset: &Dataset<File>) -> Vec<String> {
700 dataset.columns.iter().map(|(x, _, _)| x.clone()).collect()
701 }
702
703 fn get_records(dataset: &Dataset<File>) -> Vec<Vec<String>> {
704 dataset.records.borrow().iter().map(|(x, _)| get_values(x)).collect()
705 }
706
707 fn get_values(record: &Record) -> Vec<String> {
708 record.iter().map(|(x, _)| x.clone()).collect()
709 }
710}