From ccfd000d129799f5a106a7d4c8edab88af37367b Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:51:01 -0400 Subject: [PATCH 1/4] `deps`: fine-tune polars dependencies - activate `dtype-categorical` and `temporal` features - remove separate dependency for polars-ops - use upstream version of polars with unreleased fixes/features after polars 0.41.2 release --- Cargo.lock | 60 +++++++++++++++++++----------------------------------- Cargo.toml | 8 ++++++-- 2 files changed, 27 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9e248793..7fd71aa45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3570,9 +3570,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -4045,8 +4045,7 @@ dependencies = [ [[package]] name = "polars" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce49e10a756f68eb99c102c6b2a0cbc0c583a0fa7263536ad0913d94be878d2d" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "getrandom", "polars-arrow", @@ -4065,8 +4064,7 @@ dependencies = [ [[package]] name = "polars-arrow" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b436f83f62e864f0d91871e26528f2c5552c7cf07c8d77547f1b8e3fde22bd27" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "atoi", @@ -4113,8 +4111,7 @@ dependencies = [ [[package]] name = "polars-compute" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6758f834f07e622a2f859bebb542b2b7f8879b8704dbb2b2bbab460ddcdca4b" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "bytemuck", "either", @@ -4129,8 +4126,7 @@ dependencies = [ [[package]] name = "polars-core" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ed262e9bdda15a12a9bfcfc9200bec5253335633dbd86cf5b94fda0194244b3" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", @@ -4163,8 +4159,7 @@ dependencies = [ [[package]] name = "polars-error" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e1707a17475ba5e74c349154b415e3148a1a275e395965427971b5e53ad621" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "avro-schema", "polars-arrow-format", @@ -4176,8 +4171,7 @@ dependencies = [ [[package]] name = "polars-expr" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a9688d5842e7a7fbad88e67a174778794a91d97d3bba1b3c09dd1656fee3b2" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", @@ -4196,8 +4190,7 @@ dependencies = [ [[package]] name = "polars-io" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18798dacd94fb9263f65f63f0feab0908675422646d6f7fc37043b85ff6dca35" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "async-trait", @@ -4236,11 +4229,11 @@ dependencies = [ [[package]] name = "polars-json" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "044ea319f667efbf8007c4c38171c2956e0e7f9b078eb66e31e82f80d1e14b51" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "chrono", + "chrono-tz 0.8.6", "fallible-streaming-iterator", "hashbrown 0.14.5", "indexmap", @@ -4257,8 +4250,7 @@ dependencies = [ [[package]] name = "polars-lazy" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a11994c2211f2e99d9ac31776fd7c2c0607d5fe62d5b5db9e396f7d663f3d5" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "bitflags 2.6.0", @@ -4284,8 +4276,7 @@ dependencies = [ [[package]] name = "polars-mem-engine" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5acd5fde6fadaddfcae3227ec5b64121007928f8e68870c80653438e20c1c587" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "polars-arrow", "polars-core", @@ -4303,8 +4294,7 @@ dependencies = [ [[package]] name = "polars-ops" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4170c59e974727941edfb722f6d430ed623be9e7f30581ee00832c907f1b9fd" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "argminmax", @@ -4337,8 +4327,7 @@ dependencies = [ [[package]] name = "polars-parquet" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c684638c36c60c691d707d414249fe8af4a19a35a39d418464b140fe23732e5d" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "async-stream", @@ -4365,8 +4354,7 @@ dependencies = [ [[package]] name = "polars-pipe" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832af9fbebc4c074d95fb19e1ef9e1bf37c343641238c2476febff296a7028ea" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "crossbeam-channel", "crossbeam-queue", @@ -4391,8 +4379,7 @@ dependencies = [ [[package]] name = "polars-plan" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801390ea815c05c9cf8337f3148090c9c10c9595a839fa0706b77cc2405b4466" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "bytemuck", @@ -4421,8 +4408,7 @@ dependencies = [ [[package]] name = "polars-row" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee955e91b605fc91db4d0a8ea02609d3a09ff79256d905214a2a6f758cd6f7b" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "bytemuck", "polars-arrow", @@ -4433,8 +4419,7 @@ dependencies = [ [[package]] name = "polars-sql" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89c00a4b399501d5bd478e8e8022b9391047fe8570324ecba20c4e4833c0e87" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "hex", "once_cell", @@ -4454,8 +4439,7 @@ dependencies = [ [[package]] name = "polars-time" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9689b3aff99d64befe300495528bdc44c36d2656c3a8b242a790d4f43df027fc" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "atoi", "bytemuck", @@ -4476,8 +4460,7 @@ dependencies = [ [[package]] name = "polars-utils" version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12081e346983a91e26f395597e1d53dea1b4ecd694653aee1cc402d2fae01f04" +source = "git+https://github.com/pola-rs/polars?rev=bcc8a92#bcc8a92d2e8baee0810d29e78f2af5ce0103650e" dependencies = [ "ahash 0.8.11", "bytemuck", @@ -4803,7 +4786,6 @@ dependencies = [ "parking_lot 0.12.3", "phf 0.11.2", "polars", - "polars-ops", "postgres", "pyo3", "qsv-dateparser", diff --git a/Cargo.toml b/Cargo.toml index 4f85aa693..8bcade2c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -146,6 +146,7 @@ polars = { version = "0.41", features = [ "coalesce", "cse", "cross_join", + "dtype-categorical", "dtype-full", "extract_jsonpath", "ipc", @@ -158,9 +159,9 @@ polars = { version = "0.41", features = [ "serde-lazy", "sql", "streaming", + "temporal", "timezones", ], optional = true } -polars-ops = { version = "0.41", optional = true } pyo3 = { version = "0.22", features = [ "auto-initialize", "gil-refs", @@ -258,6 +259,9 @@ grex = { git = "https://github.com/pemistahl/grex", rev = "0c8ab87" } calamine = { git = "https://github.com/tafia/calamine", rev = "6b41309" } # use modernized version of local_encoding local-encoding = { git = "https://github.com/slonopotamus/local-encoding-rs", branch = "travis-madness" } +# use latest upstream version of polars with additional unreleased features/fixes +polars = { git = "https://github.com/pola-rs/polars", rev = "bcc8a92" } + [features] default = ["mimalloc"] @@ -315,7 +319,7 @@ to = ["csvs_convert"] to_parquet = ["csvs_convert/parquet"] lite = [] datapusher_plus = ["self_update"] -polars = ["dep:polars", "polars-ops", "smartstring"] +polars = ["dep:polars", "smartstring"] feature_capable = [] nightly = [ "regex/unstable", From 81c81836d00ba59398d1cc06382a4801c4a3d550 Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:51:34 -0400 Subject: [PATCH 2/4] `joinp`: remove dependency on polars-ops --- src/cmd/joinp.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cmd/joinp.rs b/src/cmd/joinp.rs index 8b011e24b..f9f349142 100644 --- a/src/cmd/joinp.rs +++ b/src/cmd/joinp.rs @@ -186,12 +186,11 @@ use std::{ use polars::{ datatypes::AnyValue, prelude::{ - AsOfOptions, AsofStrategy, CsvWriter, IntoLazy, JoinType, JoinValidation, LazyCsvReader, - LazyFileListReader, LazyFrame, SerWriter, SortMultipleOptions, + AsOfOptions, AsofStrategy, CsvWriter, IntoLazy, JoinCoalesce, JoinType, JoinValidation, + LazyCsvReader, LazyFileListReader, LazyFrame, SerWriter, SortMultipleOptions, }, sql::SQLContext, }; -use polars_ops::frame::JoinCoalesce; use serde::Deserialize; use smartstring::SmartString; use tempfile::tempdir; From f8b84abc6885bda4062ee194c1f08f9aec599656 Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:52:22 -0400 Subject: [PATCH 3/4] `sqlp`: expand usage text examples to show select addl features/functions enabled by latest polars --- src/cmd/sqlp.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cmd/sqlp.rs b/src/cmd/sqlp.rs index aa2838623..e05a08a87 100644 --- a/src/cmd/sqlp.rs +++ b/src/cmd/sqlp.rs @@ -16,7 +16,7 @@ Returns the shape of the query result (number of rows, number of columns) to std Example queries: - qsv sqlp data.csv 'select * from data where col1 > 10 order by col2 desc limit 20' + qsv sqlp data.csv 'select * from data where col1 > 10 order by all desc limit 20' qsv sqlp data.csv 'select col1, col2 as friendlyname from data' --format parquet --output data.parquet @@ -71,6 +71,19 @@ Example queries: # https://en.wikipedia.org/wiki/Three-way_comparison#Spaceship_operator qsv sqlp data.csv data2.csv "select data.c2 <=> data2.c2 from data join data2 on data.c1 = data2.c1" + # support ^@ ("starts with"), and ~~ (like) ,~~* (ilike),!~~ (not like),!~~* (not ilike) operators + qsv sqlp data.csv "select * from data WHERE col1 ^@ 'foo'" + qsv sqlp data.csv "select c1 ^@ 'a' AS c1_starts_with_a from data" + qsv sqlp data.csv "select c1 ~~* '%B' AS c1_ends_with_b_caseinsensitive from data" + + # support SELECT * ILIKE wildcard syntax + # select all columns from customers where the column contains 'a' followed by an 'e' + # with any characters (or no characters), in between, case-insensitive + # if customers.csv has columns LastName, FirstName, Address, City, State, Zip + # this query will return all columns for all rows except the columns that don't + # contain 'a' followed by an 'e' - i.e. except City and Zip + qsv sqlp customers.csv "SELECT * ILIKE '%a%e%' FROM customers ORDER BY LastName, FirstName" + # regex operators: "~" (contains pattern, case-sensitive); "~*" (contains pattern, case-insensitive) # "!~" (does not contain pattern, case-sensitive); "!~*" (does not contain pattern, case-insensitive) qsv sqlp data.csv "select * from data WHERE col1 ~ '^foo' AND col2 > 10" @@ -94,6 +107,8 @@ Example queries: qsv sqlp data.csv "select * from data join read_parquet('data2.parquet') as t2 ON data.c1 = t2.c1" qsv sqlp data.csv "select * from data join read_ndjson('data2.jsonl') as t2 on data.c1 = t2.c1" qsv sqlp data.csv "select * from data join read_ipc('data2.arrow') as t2 ON data.c1 = t2.c1" + qsv sqlp SKIP_INPUT "select * from read_parquet('data.parquet') order by col1 desc limit 100" + qsv sqlp SKIP_INPUT "select * from read_ndjson('data.jsonl') as t1 join read_ipc('data.arrow') as t2 on t1.c1 = t2.c1" # you can also directly load CSVs using the Polars read_csv() SQL function. This is useful when # you want to bypass the regular CSV parser (with SKIP_INPUT) and use Polars' multithreaded, From 7fa475f2b8207388b0e63328fca9c21c57352dc9 Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Fri, 28 Jun 2024 11:53:01 -0400 Subject: [PATCH 4/4] `tests`: addl `sqlp` tests to exercise latest polars sql features/functions --- tests/test_sqlp.rs | 257 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 254 insertions(+), 3 deletions(-) diff --git a/tests/test_sqlp.rs b/tests/test_sqlp.rs index 5be74d9c0..dd554d66b 100644 --- a/tests/test_sqlp.rs +++ b/tests/test_sqlp.rs @@ -159,7 +159,6 @@ fn sqlp_join_same_colname_1820() { } #[test] -// #[ignore = "temporarily disable due to a bug in polars aliasing"] fn sqlp_boston311_groupby_orderby() { let wrk = Workdir::new("sqlp_boston311_groupby_orderby"); let test_file = wrk.load_test_file("boston311-100.csv"); @@ -220,6 +219,65 @@ fn sqlp_boston311_groupby_orderby() { assert_eq!(got, expected); } +#[test] +fn sqlp_boston311_groupby_orderby_all() { + let wrk = Workdir::new("sqlp_boston311_groupby_orderby_all"); + let test_file = wrk.load_test_file("boston311-100.csv"); + + let mut cmd = wrk.command("sqlp"); + + cmd.arg(&test_file) + .arg(r#"select ward, count(*) as cnt from "boston311-100" group by ward order by all"#); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["ward", "cnt"], + svec![" ", "1"], + svec!["01", "1"], + svec!["02", "1"], + svec!["03", "2"], + svec!["04", "1"], + svec!["06", "1"], + svec!["07", "1"], + svec!["1", "1"], + svec!["10", "1"], + svec!["14", "4"], + svec!["16", "1"], + svec!["17", "2"], + svec!["18", "1"], + svec!["19", "1"], + svec!["21", "1"], + svec!["22", "2"], + svec!["3", "5"], + svec!["7", "1"], + svec!["8", "1"], + svec!["9", "1"], + svec!["Ward 1", "6"], + svec!["Ward 10", "1"], + svec!["Ward 11", "2"], + svec!["Ward 12", "1"], + svec!["Ward 13", "4"], + svec!["Ward 14", "1"], + svec!["Ward 15", "1"], + svec!["Ward 16", "4"], + svec!["Ward 17", "1"], + svec!["Ward 18", "3"], + svec!["Ward 19", "3"], + svec!["Ward 2", "1"], + svec!["Ward 20", "5"], + svec!["Ward 21", "2"], + svec!["Ward 22", "1"], + svec!["Ward 3", "10"], + svec!["Ward 4", "5"], + svec!["Ward 5", "5"], + svec!["Ward 6", "7"], + svec!["Ward 7", "3"], + svec!["Ward 8", "3"], + svec!["Ward 9", "1"], + ]; + assert_eq!(got, expected); +} + #[test] // #[ignore = "temporarily disable due to a bug in polars aliasing"] fn sqlp_boston311_groupby_orderby_with_table_alias() { @@ -1747,6 +1805,42 @@ fn sqlp_modulo() { assert_eq!(got, expected); } +#[test] +fn sqlp_try_cast() { + let wrk = Workdir::new("sqlp_try_cast"); + wrk.create( + "test.csv", + vec![ + svec!["foo", "bar"], + svec!["65432", "1999-12-31"], + svec!["101010", "N/A"], + svec!["-3333", "2024-01-01"], + ], + ); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("test.csv").arg( + r#" + SELECT + try_cast(foo as uint2), + try_cast(bar as DATE) + FROM test +"#, + ); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["foo", "bar"], + svec!["65432", "1999-12-31"], + svec!["", ""], + svec!["", "2024-01-01"], + ]; + + assert_eq!(got, expected); +} + #[test] fn sqlp_stddev_variance() { let wrk = Workdir::new("sqlp_stddev_variance"); @@ -2063,8 +2157,8 @@ fn sqlp_date() { wrk.assert_success(&mut cmd); let got: Vec> = wrk.read_stdout(&mut cmd); - // unfortunately, this is the current behavior of the date function - // https://github.com/pola-rs/polars/issues/17093 + // this is the documented behavior of the date function + // use STRFTIME and STRPTIME for more control let expected = vec![ svec!["c1", "c2", "c3"], svec!["2021-03-15", "2021-03-15", "2021-03-15"], @@ -2073,6 +2167,163 @@ fn sqlp_date() { assert_eq!(got, expected); } +#[test] +fn sqlp_date_strftime() { + let wrk = Workdir::new("sqlp_date_strftime"); + + wrk.create( + "data.csv", + vec![ + svec!["dtm", "dt", "tm"], + svec!["1972-03-06 23:50:03", "1978-07-05", "10:10:10"], + svec!["1980-09-30 01:25:50", "1969-12-31", "22:33:55"], + ], + ); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("data.csv") + .arg( + r#" + SELECT + STRFTIME(dtm,'%m.%d.%Y/%T') AS s_dtm, + STRFTIME(dt,'%B %d, %Y') AS s_dt, + STRFTIME(tm,'%S.%M.%H') AS s_tm, + FROM data"#, + ) + .arg("--try-parsedates"); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["s_dtm", "s_dt", "s_tm"], + svec!["03.06.1972/23:50:03", "July 05, 1978", "10.10.10"], + svec!["09.30.1980/01:25:50", "December 31, 1969", "55.33.22"], + ]; + + assert_eq!(got, expected); +} + +#[test] +fn sqlp_string_like_ops() { + let wrk = Workdir::new("sqlp_string_like_ops"); + + wrk.create( + "likedata.csv", + vec![ + svec!["x", "y"], + svec!["aaa", "abc"], + svec!["bbb", "b"], + svec!["a", "aa"], + ], + ); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("likedata.csv").arg( + r#" + SELECT + x, + x ^@ 'a' AS x_starts_with_a, + x ~~* '%B' AS x_ends_with_b_case_insensitive, + x ^@ y AS x_starts_with_y, + x ~~ '%a' AS x_ends_with_a + FROM likedata"#, + ); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec![ + "x", + "x_starts_with_a", + "x_ends_with_b_case_insensitive", + "x_starts_with_y", + "x_ends_with_a" + ], + svec!["aaa", "true", "false", "false", "true"], + svec!["bbb", "false", "true", "true", "false"], + svec!["a", "true", "false", "false", "true"], + ]; + + assert_eq!(got, expected); +} + +#[test] +fn sqlp_star_ilike() { + let wrk = Workdir::new("sqlp_star_ilike"); + + wrk.create( + "starlikedata.csv", + vec![ + svec!["ID", "FirstName", "LastName", "Address", "City"], + svec!["333", "Bruce", "Wayne", "The Batcave", "Gotham"], + svec!["666", "Diana", "Prince", "Paradise Island", "Themyscira"], + svec!["999", "Clark", "Kent", "Fortress of Solitude", "Metropolis"], + ], + ); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("starlikedata.csv").arg( + r#" + SELECT * ILIKE '%a%e%' + FROM starlikedata + ORDER BY FirstName"#, + ); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["FirstName", "LastName", "Address"], + svec!["Bruce", "Wayne", "The Batcave"], + svec!["Clark", "Kent", "Fortress of Solitude"], + svec!["Diana", "Prince", "Paradise Island"], + ]; + + assert_eq!(got, expected); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("starlikedata.csv").arg( + r#" + SELECT * ILIKE '%I%' RENAME (FirstName AS Name) + FROM starlikedata + ORDER BY 3 DESC"#, + ); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["ID", "Name", "City"], + svec!["666", "Diana", "Themyscira"], + svec!["999", "Clark", "Metropolis"], + svec!["333", "Bruce", "Gotham"], + ]; + + assert_eq!(got, expected); + + let mut cmd = wrk.command("sqlp"); + cmd.arg("starlikedata.csv").arg( + r#" + SELECT * EXCLUDE (ID, City, LastName) RENAME FirstName AS Name + FROM starlikedata + ORDER BY Name"#, + ); + + wrk.assert_success(&mut cmd); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec!["Name", "Address"], + svec!["Bruce", "The Batcave"], + svec!["Clark", "Fortress of Solitude"], + svec!["Diana", "Paradise Island"], + ]; + + assert_eq!(got, expected); +} + #[test] fn sqlp_skip_input() { let wrk = Workdir::new("sqlp_skip_input");