diff --git a/CHANGELOG.md b/CHANGELOG.md index 86f55a5b8f2f..9698cbf2ef0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ **Language**: - _Breaking_: Exclude `group`'s `by` columns from the partition. See #3490 +- _Breaking_: `lower` and `upper` are now in the `string` module and need to be + called via `string.lower` and `string.upper`. See #3913 **Features**: @@ -13,6 +15,10 @@ `ceil`, `pi`, `exp`, `ln`, `log10`, `log`, `sqrt`, `degrees`, `radians`, `cos`, `acos`, `sin`, `asin`, `tan`, `atan` and `pow`.\ Those functions are in the `math` module (@PrettyWood, #3909 & #3916) +- Most standard string functions are now supported: `ltrim`, `rtrim`, `trim`, + `length`, `substring`, `replace`. Utility functions `starts_with`, `contains` + and `ends_with` are also available.\ + Those functions are in the `string` module (@PrettyWood, #3913) **Fixes**: diff --git a/Cargo.lock b/Cargo.lock index a07d2c16c7bf..2018199bffdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1468,11 +1468,26 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1480,21 +1495,32 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" + +[[package]] +name = "futures-executor" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", @@ -1503,22 +1529,29 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -2892,6 +2925,7 @@ dependencies = [ "prqlc-ast", "prqlc-parser", "regex", + "rstest", "rusqlite", "semver", "serde", @@ -3282,6 +3316,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca" + [[package]] name = "rend" version = "0.4.0" @@ -3319,6 +3359,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.40", + "unicode-ident", +] + [[package]] name = "rusqlite" version = "0.29.0" @@ -3364,6 +3433,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.38.19" diff --git a/prqlc/prql-compiler/Cargo.toml b/prqlc/prql-compiler/Cargo.toml index d003afda2f71..903d9773e094 100644 --- a/prqlc/prql-compiler/Cargo.toml +++ b/prqlc/prql-compiler/Cargo.toml @@ -59,6 +59,7 @@ tokio-util = {version = "0.7", optional = true, features = ["compat"]} [dev-dependencies] cfg-if = "1.0" insta = {version = "1.34", features = ["colors", "yaml"]} +rstest = "0.18.2" similar = {version = "2.3.0"} similar-asserts = "1.5.0" test_each_file = "0.3.0" diff --git a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap index 36fc608f3c09..3b2bcfcf060f 100644 --- a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap +++ b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-2.snap @@ -4,18 +4,18 @@ expression: "resolve_lineage(r#\"\n from table_1\n join cu --- columns: - All: - input_id: 105 + input_id: 115 except: [] - All: - input_id: 102 + input_id: 112 except: [] inputs: - - id: 105 + - id: 115 name: table_1 table: - default_db - table_1 - - id: 102 + - id: 112 name: customers table: - default_db diff --git a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap index f88305f14bd4..adfb6fb05969 100644 --- a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap +++ b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names-3.snap @@ -7,26 +7,26 @@ columns: name: - e - emp_no - target_id: 114 + target_id: 124 target_name: ~ - Single: name: - e - gender - target_id: 115 + target_id: 125 target_name: ~ - Single: name: - emp_salary - target_id: 133 + target_id: 143 target_name: ~ inputs: - - id: 108 + - id: 118 name: e table: - default_db - employees - - id: 105 + - id: 115 name: salaries table: - default_db diff --git a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap index 95f1b2a5fbd5..d3366aa579b2 100644 --- a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap +++ b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__test__frames_and_names.snap @@ -7,26 +7,26 @@ columns: name: - orders - customer_no - target_id: 108 + target_id: 118 target_name: ~ - Single: name: - orders - gross - target_id: 109 + target_id: 119 target_name: ~ - Single: name: - orders - tax - target_id: 110 + target_id: 120 target_name: ~ - Single: name: ~ - target_id: 111 + target_id: 121 target_name: ~ inputs: - - id: 107 + - id: 117 name: orders table: - default_db diff --git a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__transforms__tests__aggregate_positional_arg-2.snap b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__transforms__tests__aggregate_positional_arg-2.snap index 7f79dff5d3e5..b84af8d44994 100644 --- a/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__transforms__tests__aggregate_positional_arg-2.snap +++ b/prqlc/prql-compiler/src/semantic/resolver/snapshots/prql_compiler__semantic__resolver__transforms__tests__aggregate_positional_arg-2.snap @@ -20,10 +20,10 @@ TransformCall: lineage: columns: - All: - input_id: 104 + input_id: 114 except: [] inputs: - - id: 104 + - id: 114 name: c_invoice table: - default_db @@ -304,14 +304,14 @@ lineage: name: - c_invoice - issued_at - target_id: 105 + target_id: 115 target_name: ~ - Single: name: ~ - target_id: 121 + target_id: 131 target_name: ~ inputs: - - id: 104 + - id: 114 name: c_invoice table: - default_db diff --git a/prqlc/prql-compiler/src/semantic/std.prql b/prqlc/prql-compiler/src/semantic/std.prql index 520581fa3b91..c49dcda742d7 100644 --- a/prqlc/prql-compiler/src/semantic/std.prql +++ b/prqlc/prql-compiler/src/semantic/std.prql @@ -215,8 +215,19 @@ let _is_null = func a -> _param.a == null let from_text = input `noresolve.format`:csv -> internal from_text ## String functions -let lower = column -> internal std.lower -let upper = column -> internal std.upper +module string { + let lower = column -> internal std.string.lower + let upper = column -> internal std.string.upper + let ltrim = column -> internal std.string.ltrim + let rtrim = column -> internal std.string.rtrim + let trim = column -> internal std.string.trim + let length = column -> internal std.string.length + let substring = offset length column -> internal std.string.substring + let replace = pattern replacement column -> internal std.string.replace + let starts_with = prefix column -> internal std.string.starts_with + let contains = substr column -> internal std.string.contains + let ends_with = suffix column -> internal std.string.ends_with +} ## File-reading functions, primarily for DuckDB let read_parquet = source -> internal std.read_parquet diff --git a/prqlc/prql-compiler/src/sql/std.sql.prql b/prqlc/prql-compiler/src/sql/std.sql.prql index 5eb4af0e87fe..cd33203061c3 100644 --- a/prqlc/prql-compiler/src/sql/std.sql.prql +++ b/prqlc/prql-compiler/src/sql/std.sql.prql @@ -62,9 +62,9 @@ let row_number = -> s"ROW_NUMBER()" # Mathematical functions module math { - # Clickhouse: https://clickhouse.com/docs/en/sql-reference/functions + # Clickhouse: https://clickhouse.com/docs/en/sql-reference/functions/math-functions # DuckDB: https://duckdb.org/docs/test/functions/math.html - # MySQL: https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html#function_truncate + # MySQL: https://dev.mysql.com/doc/refman/8.0/en/mathematical-functions.html # Postgres: https://www.postgresql.org/docs/7.4/functions-math.html # SQLite: https://www.sqlite.org/lang_mathfunc.html # MSSQL: https://learn.microsoft.com/en-us/sql/t-sql/functions/mathematical-functions-transact-sql @@ -95,8 +95,27 @@ let round = n_digits column -> s"ROUND({column:0}, {n_digits:0})" let as = `type` column -> s"CAST({column:0} AS {type:0})" # String functions -let lower = column -> s"LOWER({column:0})" -let upper = column -> s"UPPER({column:0})" +module string { + # Clickhouse: https://clickhouse.com/docs/en/sql-reference/functions/string-functions + # DuckDB: https://duckdb.org/docs/sql/functions/char + # MySQL: https://dev.mysql.com/doc/refman/8.0/en/string-functions.html + # Postgres: https://www.postgresql.org/docs/7.4/functions-string.html + # SQLite: https://www.sqlite.org/lang_corefunc.html + # MSSQL: https://learn.microsoft.com/en-us/sql/t-sql/functions/string-functions-transact-sql + # BigQuery: https://cloud.google.com/bigquery/docs/reference/standard-sql/string_functions + # Snowflake: https://docs.snowflake.com/en/sql-reference/functions-string + let lower = column -> s"LOWER({column:0})" + let upper = column -> s"UPPER({column:0})" + let ltrim = column -> s"LTRIM({column:0})" + let rtrim = column -> s"RTRIM({column:0})" + let trim = column -> s"TRIM({column:0})" + let length = column -> s"CHAR_LENGTH({column:0})" + let substring = offset length column -> s"SUBSTRING({column:0}, {offset:0}, {length:0})" + let replace = pattern replacement column -> s"REPLACE({column:0}, {pattern:0}, {replacement:0})" + let starts_with = prefix column -> s"{column:0} LIKE CONCAT({prefix:0}, '%')" + let contains = substr column -> s"{column:0} LIKE CONCAT('%', {substr:0}, '%')" + let ends_with = suffix column -> s"{column:0} LIKE CONCAT('%', {suffix:0})" +} # Source-reading functions, primarily for DuckDB let read_parquet = source -> s"read_parquet({source:0})" @@ -196,6 +215,12 @@ module duckdb { @{binding_strength=11} let div_i = l r -> s"TRUNC({l:11} / {r:11})" + # String functions + module string { + # DuckDB: https://duckdb.org/docs/sql/functions/char + let length = column -> s"LENGTH({column:0})" + } + let regex_search = text pattern -> s"REGEXP_MATCHES({text:0}, {pattern:0})" let read_csv = source -> s"read_csv_auto({source:0})" @@ -213,6 +238,12 @@ module mssql { let pow = exponent column -> s"POWER({column:0}, {exponent:0})" } + # String functions + module string { + # https://learn.microsoft.com/en-us/sql/t-sql/functions/string-functions-transact-sql + let length = column -> s"LEN({column:0})" + } + let regex_search = text pattern -> null } @@ -240,6 +271,12 @@ module postgres { @{binding_strength=100} let round = n_digits column -> s"ROUND(({column:0})::numeric, {n_digits:0})" + # String functions + module string { + # Postgres: https://www.postgresql.org/docs/7.4/functions-string.html + let substring = offset length column -> s"SUBSTR({column:0}, {offset:0}, {length:0})" + } + @{binding_strength=9} let regex_search = text pattern -> s"{text} ~ {pattern}" } @@ -278,6 +315,14 @@ module sqlite { @{binding_strength=100} let div_i = l r -> s"ROUND(ABS({l:11} / {r:11}) - 0.5) * SIGN({l:0}) * SIGN({r:0})" + # String functions + module string { + # SQLite: https://www.sqlite.org/lang_corefunc.html + let starts_with = prefix column -> s"{column:0} LIKE {prefix:0} || '%'" + let contains = substr column -> s"{column:0} LIKE '%' || {substr:0} || '%'" + let ends_with = suffix column -> s"{column:0} LIKE '%' || {suffix:0}" + } + @{binding_strength=9} let regex_search = text pattern -> s"{text} REGEXP {pattern}" } diff --git a/prqlc/prql-compiler/tests/integration/parser.rs b/prqlc/prql-compiler/tests/integration/parser.rs index 9dc899bad9c3..3edf06c42f58 100644 --- a/prqlc/prql-compiler/tests/integration/parser.rs +++ b/prqlc/prql-compiler/tests/integration/parser.rs @@ -770,7 +770,7 @@ fn test_filter() { "###); assert_yaml_snapshot!( - parse_single(r#"filter (upper country) == "USA""#).unwrap(), @r###" + parse_single(r#"filter (string.upper country) == "USA""#).unwrap(), @r###" --- - VarDef: kind: Main @@ -786,6 +786,7 @@ fn test_filter() { FuncCall: name: Ident: + - string - upper args: - Ident: @@ -794,7 +795,7 @@ fn test_filter() { right: Literal: String: USA - span: "0:0-31" + span: "0:0-38" "### ); } diff --git a/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_generic.snap b/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_generic.snap new file mode 100644 index 000000000000..6244101f4151 --- /dev/null +++ b/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_generic.snap @@ -0,0 +1,9 @@ +--- +source: prqlc/prql-compiler/tests/integration/sql.rs +expression: "\n SELECT\n name LIKE CONCAT('%', 'pika', '%') AS name_ends_with\n FROM\n employees\n " +--- +SELECT + name LIKE CONCAT('%', 'pika', '%') AS name_ends_with +FROM + employees + diff --git a/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_sqlite.snap b/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_sqlite.snap new file mode 100644 index 000000000000..c0d5685444c2 --- /dev/null +++ b/prqlc/prql-compiler/tests/integration/snapshots/integration__sql__like_concat_sqlite.snap @@ -0,0 +1,9 @@ +--- +source: prqlc/prql-compiler/tests/integration/sql.rs +expression: "\n SELECT\n name LIKE '%' || 'pika' || '%' AS name_ends_with\n FROM\n employees\n " +--- +SELECT + name LIKE '%' || 'pika' || '%' AS name_ends_with +FROM + employees + diff --git a/prqlc/prql-compiler/tests/integration/sql.rs b/prqlc/prql-compiler/tests/integration/sql.rs index 7a7a7eadd405..557dac4baf75 100644 --- a/prqlc/prql-compiler/tests/integration/sql.rs +++ b/prqlc/prql-compiler/tests/integration/sql.rs @@ -1,12 +1,23 @@ //! Simple tests for "this PRQL creates this SQL" go here. use insta::{assert_display_snapshot, assert_snapshot}; use prql_compiler::{sql, ErrorMessages, Options, SourceTree, Target}; +use rstest::rstest; pub fn compile(prql: &str) -> Result { anstream::ColorChoice::Never.write_global(); prql_compiler::compile(prql, &Options::default().no_signature()) } +fn compile_with_sql_dialect(prql: &str, dialect: sql::Dialect) -> Result { + anstream::ColorChoice::Never.write_global(); + prql_compiler::compile( + prql, + &Options::default() + .no_signature() + .with_target(Target::Sql(Some(dialect))), + ) +} + #[test] fn test_stdlib() { assert_snapshot!(compile(r###" @@ -140,6 +151,67 @@ fn test_stdlib_math_mssql() { ); } +#[test] +fn test_stdlib_string() { + assert_snapshot!(compile(r#" + from employees + select { + name_lower = name | string.lower, + name_upper = name | string.upper, + name_ltrim = name | string.ltrim, + name_rtrim = name | string.rtrim, + name_trim = name | string.trim, + name_length = name | string.length, + name_substring = name | string.substring 3 5, + name_replace = name | string.replace "pika" "chu", + name_starts_with = name | string.starts_with "pika", + name_contains = name | string.contains "pika", + name_ends_with = name | string.ends_with "pika", + } + "#).unwrap(), @r#" + SELECT + LOWER(name) AS name_lower, + UPPER(name) AS name_upper, + LTRIM(name) AS name_ltrim, + RTRIM(name) AS name_rtrim, + TRIM(name) AS name_trim, + CHAR_LENGTH(name) AS name_length, + SUBSTRING(name, 3, 5) AS name_substring, + REPLACE(name, 'pika', 'chu') AS name_replace, + name LIKE CONCAT('pika', '%') AS name_starts_with, + name LIKE CONCAT('%', 'pika', '%') AS name_contains, + name LIKE CONCAT('%', 'pika') AS name_ends_with + FROM + employees + "# + ); +} + +#[rstest] +#[case::generic(sql::Dialect::Generic, "LIKE CONCAT('%', 'pika', '%')")] +#[case::sqlite(sql::Dialect::SQLite, "LIKE '%' || 'pika' || '%'")] // `CONCAT` is not supported in SQLite +fn like_concat(#[case] dialect: sql::Dialect, #[case] expected_like: &'static str) { + let query = r#" + from employees + select { + name_ends_with = name | string.contains "pika", + } + "#; + let expected = format!( + r#" + SELECT + name {expected_like} AS name_ends_with + FROM + employees + "# + ); + assert_snapshot!( + format!("like_concat_{}", dialect), + compile_with_sql_dialect(query, dialect).unwrap(), + &expected + ) +} + #[test] fn json_of_test() { let json = prql_compiler::prql_to_pl("from employees | take 10") @@ -3999,7 +4071,7 @@ fn test_datetime_parsing() { fn test_lower() { assert_display_snapshot!(compile(r#" from test_tables - derive {lower_name = (name | lower)} + derive {lower_name = (name | string.lower)} "#).unwrap(), @r###" SELECT @@ -4015,7 +4087,7 @@ fn test_lower() { fn test_upper() { assert_display_snapshot!(compile(r#" from test_tables - derive {upper_name = upper name} + derive {upper_name = string.upper name} select {upper_name} "#).unwrap(), @r###" diff --git a/web/book/highlight-prql.js b/web/book/highlight-prql.js index 0cff44b76a52..77986f9361c5 100644 --- a/web/book/highlight-prql.js +++ b/web/book/highlight-prql.js @@ -50,9 +50,8 @@ formatting = function (hljs) { "_is_null", // Misc functions "from_text", - // String functions - "lower", - "upper", + // String functions module + "string", // Window functions "lag", "lead", @@ -61,7 +60,7 @@ formatting = function (hljs) { "rank", "rank_dense", "row_number", - // Mathematical-functions module + // Mathematical functions module "math", ]; diff --git a/web/book/src/reference/declarations/functions.md b/web/book/src/reference/declarations/functions.md index 257c45f9855e..a29f3faec674 100644 --- a/web/book/src/reference/declarations/functions.md +++ b/web/book/src/reference/declarations/functions.md @@ -40,6 +40,7 @@ derive { let is_adult = col -> col >= 18 let writes_code = col -> (col | in ["PRQL", "Rust"]) let square = col -> (col | math.pow 2) +let starts_with_a = col -> (col | string.lower | string.starts_with("a")) from employees select { @@ -49,7 +50,7 @@ select { adult = is_adult age, age_squared = square age, } -filter (writes_code hobby) +filter ((starts_with_a last_name) && (writes_code hobby)) ``` ## Piping values into functions diff --git a/web/book/tests/documentation/snapshots/documentation__book__reference__declarations__functions__other-examples__0.snap b/web/book/tests/documentation/snapshots/documentation__book__reference__declarations__functions__other-examples__0.snap index 1b638c49c78f..5a961a70339d 100644 --- a/web/book/tests/documentation/snapshots/documentation__book__reference__declarations__functions__other-examples__0.snap +++ b/web/book/tests/documentation/snapshots/documentation__book__reference__declarations__functions__other-examples__0.snap @@ -1,6 +1,6 @@ --- source: web/book/tests/documentation/book.rs -expression: "let is_adult = col -> col >= 18\nlet writes_code = col -> (col | in [\"PRQL\", \"Rust\"])\nlet square = col -> (col | math.pow 2)\n\nfrom employees\nselect {\n first_name,\n last_name,\n hobby,\n adult = is_adult age,\n age_squared = square age,\n}\nfilter (writes_code hobby)\n" +expression: "let is_adult = col -> col >= 18\nlet writes_code = col -> (col | in [\"PRQL\", \"Rust\"])\nlet square = col -> (col | math.pow 2)\nlet starts_with_a = col -> (col | string.lower | string.starts_with(\"a\"))\n\nfrom employees\nselect {\n first_name,\n last_name,\n hobby,\n adult = is_adult age,\n age_squared = square age,\n}\nfilter ((starts_with_a last_name) && (writes_code hobby))\n" --- WITH table_0 AS ( SELECT @@ -21,5 +21,6 @@ SELECT FROM table_0 WHERE - hobby IN ('PRQL', 'Rust') + LOWER(last_name) LIKE CONCAT('a', '%') + AND hobby IN ('PRQL', 'Rust') diff --git a/web/prql-codemirror-demo/src/lang-prql/complete.ts b/web/prql-codemirror-demo/src/lang-prql/complete.ts index 491be9a6b0fe..8df5c46131c1 100644 --- a/web/prql-codemirror-demo/src/lang-prql/complete.ts +++ b/web/prql-codemirror-demo/src/lang-prql/complete.ts @@ -55,9 +55,8 @@ const globals: readonly Completion[] = ["false", "null", "true"] "_is_null", // misc-functions "from_text", - // string-functions - "lower", - "upper", + // string functions module + "string", // window-functions "lag", "lead", diff --git a/web/website/themes/prql-theme/static/plugins/highlight/prql.js b/web/website/themes/prql-theme/static/plugins/highlight/prql.js index 9e3d7de73b41..23f61a17c86b 100644 --- a/web/website/themes/prql-theme/static/plugins/highlight/prql.js +++ b/web/website/themes/prql-theme/static/plugins/highlight/prql.js @@ -38,9 +38,8 @@ formatting = function (hljs) { "_is_null", // Misc functions "from_text", - // String functions - "lower", - "upper", + // String functions module + "string", // Window functions "lag", "lead",