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