From 7da1ac0eae7a6241d743bc3bbabd4c8f909d9eb7 Mon Sep 17 00:00:00 2001 From: Mikhail Cheshkov Date: Thu, 23 Jan 2025 13:58:30 +0200 Subject: [PATCH] feat(cubesql): Support %s in format Used by Metabase introspection See https://github.com/metabase/metabase/pull/50900 Fixes #9126 --- .../cubesql/src/compile/engine/udf/common.rs | 36 ++ rust/cubesql/cubesql/src/compile/mod.rs | 24 ++ ...tests__formatted_null_string_is_empty.snap | 9 + ...sql__compile__tests__formatted_string.snap | 9 + ...ql__compile__tests__formatted_strings.snap | 9 + ...__test_metabase_v0_51_8_introspection.snap | 308 ++++++++++++++++++ .../src/compile/test/test_introspection.rs | 89 +++++ 7 files changed, 484 insertions(+) create mode 100644 rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_null_string_is_empty.snap create mode 100644 rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_string.snap create mode 100644 rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_strings.snap create mode 100644 rust/cubesql/cubesql/src/compile/test/snapshots/cubesql__compile__test__test_introspection__test_metabase_v0_51_8_introspection.snap diff --git a/rust/cubesql/cubesql/src/compile/engine/udf/common.rs b/rust/cubesql/cubesql/src/compile/engine/udf/common.rs index c31af0d18db8a..127fed402cc73 100644 --- a/rust/cubesql/cubesql/src/compile/engine/udf/common.rs +++ b/rust/cubesql/cubesql/src/compile/engine/udf/common.rs @@ -3281,6 +3281,42 @@ pub fn create_format_udf() -> ScalarUDF { // %% is escaped to single % result.push('%'); } + Some('s') => { + // Handle %s - regular string + if arg_index >= args.len() { + return Err(DataFusionError::Execution( + "Not enough arguments for format string".to_string(), + )); + } + + let arg = &args[arg_index]; + let value = match arg.data_type() { + DataType::Utf8 => { + let str_arr = downcast_string_arg!(arg, "arg", i32); + if str_arr.is_null(i) { + // A null value is treated as an empty string + String::new() + } else { + str_arr.value(i).to_string() + } + } + _ => { + // For other types, try to convert to string + let str_arr = cast(&arg, &DataType::Utf8)?; + let str_arr = + str_arr.as_any().downcast_ref::().unwrap(); + if str_arr.is_null(i) { + // A null value is treated as an empty string + String::new() + } else { + str_arr.value(i).to_string() + } + } + }; + + result.push_str(&value); + arg_index += 1; + } Some(c) => { return Err(DataFusionError::Execution(format!( "Unsupported format specifier %{}", diff --git a/rust/cubesql/cubesql/src/compile/mod.rs b/rust/cubesql/cubesql/src/compile/mod.rs index 22c588f299d9c..2e99f16eb60d4 100644 --- a/rust/cubesql/cubesql/src/compile/mod.rs +++ b/rust/cubesql/cubesql/src/compile/mod.rs @@ -16214,6 +16214,30 @@ LIMIT {{ limit }}{% endif %}"#.to_string(), #[tokio::test] async fn test_format_function() -> Result<(), CubeError> { + // Test: Basic usage with a single string + let result = execute_query( + "SELECT format('%s', 'foo') AS formatted_string".to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await?; + insta::assert_snapshot!("formatted_string", result); + + // Test: Basic usage with a single null string + let result = execute_query( + "SELECT format('%s', NULL) = '' AS formatted_null_string_is_empty".to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await?; + insta::assert_snapshot!("formatted_null_string_is_empty", result); + + // Test: Basic usage with a multiple strings + let result = execute_query( + "SELECT format('%s.%s', 'foo', 'bar') AS formatted_strings".to_string(), + DatabaseProtocol::PostgreSQL, + ) + .await?; + insta::assert_snapshot!("formatted_strings", result); + // Test: Basic usage with a single identifier let result = execute_query( "SELECT format('%I', 'column_name') AS formatted_identifier".to_string(), diff --git a/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_null_string_is_empty.snap b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_null_string_is_empty.snap new file mode 100644 index 0000000000000..9d1498ea38d5a --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_null_string_is_empty.snap @@ -0,0 +1,9 @@ +--- +source: cubesql/src/compile/mod.rs +expression: result +--- ++--------------------------------+ +| formatted_null_string_is_empty | ++--------------------------------+ +| true | ++--------------------------------+ diff --git a/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_string.snap b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_string.snap new file mode 100644 index 0000000000000..88e48153a0574 --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_string.snap @@ -0,0 +1,9 @@ +--- +source: cubesql/src/compile/mod.rs +expression: result +--- ++------------------+ +| formatted_string | ++------------------+ +| foo | ++------------------+ diff --git a/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_strings.snap b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_strings.snap new file mode 100644 index 0000000000000..3f0d49796427d --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/snapshots/cubesql__compile__tests__formatted_strings.snap @@ -0,0 +1,9 @@ +--- +source: cubesql/src/compile/mod.rs +expression: result +--- ++-------------------+ +| formatted_strings | ++-------------------+ +| foo.bar | ++-------------------+ diff --git a/rust/cubesql/cubesql/src/compile/test/snapshots/cubesql__compile__test__test_introspection__test_metabase_v0_51_8_introspection.snap b/rust/cubesql/cubesql/src/compile/test/snapshots/cubesql__compile__test__test_introspection__test_metabase_v0_51_8_introspection.snap new file mode 100644 index 0000000000000..166491e9c523e --- /dev/null +++ b/rust/cubesql/cubesql/src/compile/test/snapshots/cubesql__compile__test__test_introspection__test_metabase_v0_51_8_introspection.snap @@ -0,0 +1,308 @@ +--- +source: cubesql/src/compile/test/test_introspection.rs +expression: "execute_query(r#\"\nselect\n \"c\".\"column_name\" as \"name\",\n case\n when \"c\".\"udt_schema\" in ('public', 'pg_catalog')\n then format('%s', \"c\".\"udt_name\")\n else format('\"%s\".\"%s\"', \"c\".\"udt_schema\", \"c\".\"udt_name\")\n end as \"database-type\",\n \"c\".\"ordinal_position\" - 1 as \"database-position\",\n \"c\".\"table_schema\" as \"table-schema\",\n \"c\".\"table_name\" as \"table-name\",\n \"pk\".\"column_name\" is not null as \"pk?\",\n col_description(\n cast(\n cast(\n format(\n '%I.%I',\n cast(\"c\".\"table_schema\" as text),\n cast(\"c\".\"table_name\" as text)\n ) as regclass\n ) as oid\n ),\n \"c\".\"ordinal_position\"\n ) as \"field-comment\",\n ((\"column_default\" is null) or (lower(\"column_default\") = 'null'))\n and (\"is_nullable\" = 'NO')\n and not (\n ((\"column_default\" is not null) and (\"column_default\" like '%nextval(%'))\n or (\"is_identity\" <> 'NO')\n ) as \"database-required\",\n ((\"column_default\" is not null) and (\"column_default\" like '%nextval(%'))\n or (\"is_identity\" <> 'NO') as \"database-is-auto-increment\"\nfrom \"information_schema\".\"columns\" as \"c\"\nleft join\n (\n select \"tc\".\"table_schema\", \"tc\".\"table_name\", \"kc\".\"column_name\"\n from \"information_schema\".\"table_constraints\" as \"tc\"\n inner join\n \"information_schema\".\"key_column_usage\" as \"kc\"\n on (\"tc\".\"constraint_name\" = \"kc\".\"constraint_name\")\n and (\"tc\".\"table_schema\" = \"kc\".\"table_schema\")\n and (\"tc\".\"table_name\" = \"kc\".\"table_name\")\n where \"tc\".\"constraint_type\" = 'PRIMARY KEY'\n ) as \"pk\"\n on (\"c\".\"table_schema\" = \"pk\".\"table_schema\")\n and (\"c\".\"table_name\" = \"pk\".\"table_name\")\n and (\"c\".\"column_name\" = \"pk\".\"column_name\")\nwhere\n c.table_schema !~ '^information_schema|catalog_history|pg_'\n and (\"c\".\"table_schema\" in ('public'))\nunion all\nselect\n \"pa\".\"attname\" as \"name\",\n case\n when \"ptn\".\"nspname\" in ('public', 'pg_catalog')\n then format('%s', \"pt\".\"typname\")\n else format('\"%s\".\"%s\"', \"ptn\".\"nspname\", \"pt\".\"typname\")\n end as \"database-type\",\n \"pa\".\"attnum\" - 1 as \"database-position\",\n \"pn\".\"nspname\" as \"table-schema\",\n \"pc\".\"relname\" as \"table-name\",\n false as \"pk?\",\n null as \"field-comment\",\n false as \"database-required\",\n false as \"database-is-auto-increment\"\nfrom \"pg_catalog\".\"pg_class\" as \"pc\"\ninner join \"pg_catalog\".\"pg_namespace\" as \"pn\" on \"pn\".\"oid\" = \"pc\".\"relnamespace\"\ninner join \"pg_catalog\".\"pg_attribute\" as \"pa\" on \"pa\".\"attrelid\" = \"pc\".\"oid\"\ninner join \"pg_catalog\".\"pg_type\" as \"pt\" on \"pt\".\"oid\" = \"pa\".\"atttypid\"\ninner join \"pg_catalog\".\"pg_namespace\" as \"ptn\" on \"ptn\".\"oid\" = \"pt\".\"typnamespace\"\nwhere (\"pc\".\"relkind\" = 'm') and (\"pa\".\"attnum\" >= 1) and (\"pn\".\"nspname\" in ('public'))\norder by \"table-schema\" asc, \"table-name\" asc, \"database-position\" asc\n \"#.to_string(),\nDatabaseProtocol::PostgreSQL).await?" +--- ++--------------------+---------------+-------------------+--------------+---------------------------+-------+---------------+-------------------+----------------------------+ +| name | database-type | database-position | table-schema | table-name | pk? | field-comment | database-required | database-is-auto-increment | ++--------------------+---------------+-------------------+--------------+---------------------------+-------+---------------+-------------------+----------------------------+ +| count | int8 | 0 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| maxPrice | numeric | 1 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| sumPrice | numeric | 2 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| minPrice | numeric | 3 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| avgPrice | numeric | 4 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| countDistinct | int8 | 5 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| order_date | timestamp | 6 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| last_mod | timestamp | 7 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| customer_gender | text | 8 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| notes | text | 9 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| taxful_total_price | numeric | 10 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| has_subscription | bool | 11 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| is_male | bool | 12 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| is_female | bool | 13 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| __user | text | 14 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| __cubeJoinField | text | 15 | public | KibanaSampleDataEcommerce | false | NULL | false | false | +| agentCount | int8 | 0 | public | Logs | false | NULL | false | false | +| agentCountApprox | int8 | 1 | public | Logs | false | NULL | false | false | +| id | numeric | 2 | public | Logs | false | NULL | false | false | +| read | bool | 3 | public | Logs | false | NULL | false | false | +| content | text | 4 | public | Logs | false | NULL | false | false | +| __user | text | 5 | public | Logs | false | NULL | false | false | +| __cubeJoinField | text | 6 | public | Logs | false | NULL | false | false | +| measure_num0 | numeric | 0 | public | MultiTypeCube | false | NULL | false | false | +| measure_str0 | numeric | 1 | public | MultiTypeCube | false | NULL | false | false | +| measure_date0 | numeric | 2 | public | MultiTypeCube | false | NULL | false | false | +| measure_num1 | numeric | 3 | public | MultiTypeCube | false | NULL | false | false | +| measure_str1 | numeric | 4 | public | MultiTypeCube | false | NULL | false | false | +| measure_date1 | numeric | 5 | public | MultiTypeCube | false | NULL | false | false | +| measure_num2 | numeric | 6 | public | MultiTypeCube | false | NULL | false | false | +| measure_str2 | numeric | 7 | public | MultiTypeCube | false | NULL | false | false | +| measure_date2 | numeric | 8 | public | MultiTypeCube | false | NULL | false | false | +| measure_num3 | numeric | 9 | public | MultiTypeCube | false | NULL | false | false | +| measure_str3 | numeric | 10 | public | MultiTypeCube | false | NULL | false | false | +| measure_date3 | numeric | 11 | public | MultiTypeCube | false | NULL | false | false | +| measure_num4 | numeric | 12 | public | MultiTypeCube | false | NULL | false | false | +| measure_str4 | numeric | 13 | public | MultiTypeCube | false | NULL | false | false | +| measure_date4 | numeric | 14 | public | MultiTypeCube | false | NULL | false | false | +| measure_num5 | numeric | 15 | public | MultiTypeCube | false | NULL | false | false | +| measure_str5 | numeric | 16 | public | MultiTypeCube | false | NULL | false | false | +| measure_date5 | numeric | 17 | public | MultiTypeCube | false | NULL | false | false | +| measure_num6 | numeric | 18 | public | MultiTypeCube | false | NULL | false | false | +| measure_str6 | numeric | 19 | public | MultiTypeCube | false | NULL | false | false | +| measure_date6 | numeric | 20 | public | MultiTypeCube | false | NULL | false | false | +| measure_num7 | numeric | 21 | public | MultiTypeCube | false | NULL | false | false | +| measure_str7 | numeric | 22 | public | MultiTypeCube | false | NULL | false | false | +| measure_date7 | numeric | 23 | public | MultiTypeCube | false | NULL | false | false | +| measure_num8 | numeric | 24 | public | MultiTypeCube | false | NULL | false | false | +| measure_str8 | numeric | 25 | public | MultiTypeCube | false | NULL | false | false | +| measure_date8 | numeric | 26 | public | MultiTypeCube | false | NULL | false | false | +| measure_num9 | numeric | 27 | public | MultiTypeCube | false | NULL | false | false | +| measure_str9 | numeric | 28 | public | MultiTypeCube | false | NULL | false | false | +| measure_date9 | numeric | 29 | public | MultiTypeCube | false | NULL | false | false | +| count | int8 | 30 | public | MultiTypeCube | false | NULL | false | false | +| maxPrice | numeric | 31 | public | MultiTypeCube | false | NULL | false | false | +| minPrice | numeric | 32 | public | MultiTypeCube | false | NULL | false | false | +| avgPrice | numeric | 33 | public | MultiTypeCube | false | NULL | false | false | +| countDistinct | int8 | 34 | public | MultiTypeCube | false | NULL | false | false | +| dim_num0 | numeric | 35 | public | MultiTypeCube | false | NULL | false | false | +| dim_str0 | text | 36 | public | MultiTypeCube | false | NULL | false | false | +| dim_date0 | timestamp | 37 | public | MultiTypeCube | false | NULL | false | false | +| dim_num1 | numeric | 38 | public | MultiTypeCube | false | NULL | false | false | +| dim_str1 | text | 39 | public | MultiTypeCube | false | NULL | false | false | +| dim_date1 | timestamp | 40 | public | MultiTypeCube | false | NULL | false | false | +| dim_num2 | numeric | 41 | public | MultiTypeCube | false | NULL | false | false | +| dim_str2 | text | 42 | public | MultiTypeCube | false | NULL | false | false | +| dim_date2 | timestamp | 43 | public | MultiTypeCube | false | NULL | false | false | +| dim_num3 | numeric | 44 | public | MultiTypeCube | false | NULL | false | false | +| dim_str3 | text | 45 | public | MultiTypeCube | false | NULL | false | false | +| dim_date3 | timestamp | 46 | public | MultiTypeCube | false | NULL | false | false | +| dim_num4 | numeric | 47 | public | MultiTypeCube | false | NULL | false | false | +| dim_str4 | text | 48 | public | MultiTypeCube | false | NULL | false | false | +| dim_date4 | timestamp | 49 | public | MultiTypeCube | false | NULL | false | false | +| dim_num5 | numeric | 50 | public | MultiTypeCube | false | NULL | false | false | +| dim_str5 | text | 51 | public | MultiTypeCube | false | NULL | false | false | +| dim_date5 | timestamp | 52 | public | MultiTypeCube | false | NULL | false | false | +| dim_num6 | numeric | 53 | public | MultiTypeCube | false | NULL | false | false | +| dim_str6 | text | 54 | public | MultiTypeCube | false | NULL | false | false | +| dim_date6 | timestamp | 55 | public | MultiTypeCube | false | NULL | false | false | +| dim_num7 | numeric | 56 | public | MultiTypeCube | false | NULL | false | false | +| dim_str7 | text | 57 | public | MultiTypeCube | false | NULL | false | false | +| dim_date7 | timestamp | 58 | public | MultiTypeCube | false | NULL | false | false | +| dim_num8 | numeric | 59 | public | MultiTypeCube | false | NULL | false | false | +| dim_str8 | text | 60 | public | MultiTypeCube | false | NULL | false | false | +| dim_date8 | timestamp | 61 | public | MultiTypeCube | false | NULL | false | false | +| dim_num9 | numeric | 62 | public | MultiTypeCube | false | NULL | false | false | +| dim_str9 | text | 63 | public | MultiTypeCube | false | NULL | false | false | +| dim_date9 | timestamp | 64 | public | MultiTypeCube | false | NULL | false | false | +| __user | text | 65 | public | MultiTypeCube | false | NULL | false | false | +| __cubeJoinField | text | 66 | public | MultiTypeCube | false | NULL | false | false | +| someNumber | numeric | 0 | public | NumberCube | false | NULL | false | false | +| __user | text | 1 | public | NumberCube | false | NULL | false | false | +| __cubeJoinField | text | 2 | public | NumberCube | false | NULL | false | false | +| measure0 | numeric | 0 | public | WideCube | false | NULL | false | false | +| measure1 | numeric | 1 | public | WideCube | false | NULL | false | false | +| measure2 | numeric | 2 | public | WideCube | false | NULL | false | false | +| measure3 | numeric | 3 | public | WideCube | false | NULL | false | false | +| measure4 | numeric | 4 | public | WideCube | false | NULL | false | false | +| measure5 | numeric | 5 | public | WideCube | false | NULL | false | false | +| measure6 | numeric | 6 | public | WideCube | false | NULL | false | false | +| measure7 | numeric | 7 | public | WideCube | false | NULL | false | false | +| measure8 | numeric | 8 | public | WideCube | false | NULL | false | false | +| measure9 | numeric | 9 | public | WideCube | false | NULL | false | false | +| measure10 | numeric | 10 | public | WideCube | false | NULL | false | false | +| measure11 | numeric | 11 | public | WideCube | false | NULL | false | false | +| measure12 | numeric | 12 | public | WideCube | false | NULL | false | false | +| measure13 | numeric | 13 | public | WideCube | false | NULL | false | false | +| measure14 | numeric | 14 | public | WideCube | false | NULL | false | false | +| measure15 | numeric | 15 | public | WideCube | false | NULL | false | false | +| measure16 | numeric | 16 | public | WideCube | false | NULL | false | false | +| measure17 | numeric | 17 | public | WideCube | false | NULL | false | false | +| measure18 | numeric | 18 | public | WideCube | false | NULL | false | false | +| measure19 | numeric | 19 | public | WideCube | false | NULL | false | false | +| measure20 | numeric | 20 | public | WideCube | false | NULL | false | false | +| measure21 | numeric | 21 | public | WideCube | false | NULL | false | false | +| measure22 | numeric | 22 | public | WideCube | false | NULL | false | false | +| measure23 | numeric | 23 | public | WideCube | false | NULL | false | false | +| measure24 | numeric | 24 | public | WideCube | false | NULL | false | false | +| measure25 | numeric | 25 | public | WideCube | false | NULL | false | false | +| measure26 | numeric | 26 | public | WideCube | false | NULL | false | false | +| measure27 | numeric | 27 | public | WideCube | false | NULL | false | false | +| measure28 | numeric | 28 | public | WideCube | false | NULL | false | false | +| measure29 | numeric | 29 | public | WideCube | false | NULL | false | false | +| measure30 | numeric | 30 | public | WideCube | false | NULL | false | false | +| measure31 | numeric | 31 | public | WideCube | false | NULL | false | false | +| measure32 | numeric | 32 | public | WideCube | false | NULL | false | false | +| measure33 | numeric | 33 | public | WideCube | false | NULL | false | false | +| measure34 | numeric | 34 | public | WideCube | false | NULL | false | false | +| measure35 | numeric | 35 | public | WideCube | false | NULL | false | false | +| measure36 | numeric | 36 | public | WideCube | false | NULL | false | false | +| measure37 | numeric | 37 | public | WideCube | false | NULL | false | false | +| measure38 | numeric | 38 | public | WideCube | false | NULL | false | false | +| measure39 | numeric | 39 | public | WideCube | false | NULL | false | false | +| measure40 | numeric | 40 | public | WideCube | false | NULL | false | false | +| measure41 | numeric | 41 | public | WideCube | false | NULL | false | false | +| measure42 | numeric | 42 | public | WideCube | false | NULL | false | false | +| measure43 | numeric | 43 | public | WideCube | false | NULL | false | false | +| measure44 | numeric | 44 | public | WideCube | false | NULL | false | false | +| measure45 | numeric | 45 | public | WideCube | false | NULL | false | false | +| measure46 | numeric | 46 | public | WideCube | false | NULL | false | false | +| measure47 | numeric | 47 | public | WideCube | false | NULL | false | false | +| measure48 | numeric | 48 | public | WideCube | false | NULL | false | false | +| measure49 | numeric | 49 | public | WideCube | false | NULL | false | false | +| measure50 | numeric | 50 | public | WideCube | false | NULL | false | false | +| measure51 | numeric | 51 | public | WideCube | false | NULL | false | false | +| measure52 | numeric | 52 | public | WideCube | false | NULL | false | false | +| measure53 | numeric | 53 | public | WideCube | false | NULL | false | false | +| measure54 | numeric | 54 | public | WideCube | false | NULL | false | false | +| measure55 | numeric | 55 | public | WideCube | false | NULL | false | false | +| measure56 | numeric | 56 | public | WideCube | false | NULL | false | false | +| measure57 | numeric | 57 | public | WideCube | false | NULL | false | false | +| measure58 | numeric | 58 | public | WideCube | false | NULL | false | false | +| measure59 | numeric | 59 | public | WideCube | false | NULL | false | false | +| measure60 | numeric | 60 | public | WideCube | false | NULL | false | false | +| measure61 | numeric | 61 | public | WideCube | false | NULL | false | false | +| measure62 | numeric | 62 | public | WideCube | false | NULL | false | false | +| measure63 | numeric | 63 | public | WideCube | false | NULL | false | false | +| measure64 | numeric | 64 | public | WideCube | false | NULL | false | false | +| measure65 | numeric | 65 | public | WideCube | false | NULL | false | false | +| measure66 | numeric | 66 | public | WideCube | false | NULL | false | false | +| measure67 | numeric | 67 | public | WideCube | false | NULL | false | false | +| measure68 | numeric | 68 | public | WideCube | false | NULL | false | false | +| measure69 | numeric | 69 | public | WideCube | false | NULL | false | false | +| measure70 | numeric | 70 | public | WideCube | false | NULL | false | false | +| measure71 | numeric | 71 | public | WideCube | false | NULL | false | false | +| measure72 | numeric | 72 | public | WideCube | false | NULL | false | false | +| measure73 | numeric | 73 | public | WideCube | false | NULL | false | false | +| measure74 | numeric | 74 | public | WideCube | false | NULL | false | false | +| measure75 | numeric | 75 | public | WideCube | false | NULL | false | false | +| measure76 | numeric | 76 | public | WideCube | false | NULL | false | false | +| measure77 | numeric | 77 | public | WideCube | false | NULL | false | false | +| measure78 | numeric | 78 | public | WideCube | false | NULL | false | false | +| measure79 | numeric | 79 | public | WideCube | false | NULL | false | false | +| measure80 | numeric | 80 | public | WideCube | false | NULL | false | false | +| measure81 | numeric | 81 | public | WideCube | false | NULL | false | false | +| measure82 | numeric | 82 | public | WideCube | false | NULL | false | false | +| measure83 | numeric | 83 | public | WideCube | false | NULL | false | false | +| measure84 | numeric | 84 | public | WideCube | false | NULL | false | false | +| measure85 | numeric | 85 | public | WideCube | false | NULL | false | false | +| measure86 | numeric | 86 | public | WideCube | false | NULL | false | false | +| measure87 | numeric | 87 | public | WideCube | false | NULL | false | false | +| measure88 | numeric | 88 | public | WideCube | false | NULL | false | false | +| measure89 | numeric | 89 | public | WideCube | false | NULL | false | false | +| measure90 | numeric | 90 | public | WideCube | false | NULL | false | false | +| measure91 | numeric | 91 | public | WideCube | false | NULL | false | false | +| measure92 | numeric | 92 | public | WideCube | false | NULL | false | false | +| measure93 | numeric | 93 | public | WideCube | false | NULL | false | false | +| measure94 | numeric | 94 | public | WideCube | false | NULL | false | false | +| measure95 | numeric | 95 | public | WideCube | false | NULL | false | false | +| measure96 | numeric | 96 | public | WideCube | false | NULL | false | false | +| measure97 | numeric | 97 | public | WideCube | false | NULL | false | false | +| measure98 | numeric | 98 | public | WideCube | false | NULL | false | false | +| measure99 | numeric | 99 | public | WideCube | false | NULL | false | false | +| count | int8 | 100 | public | WideCube | false | NULL | false | false | +| maxPrice | numeric | 101 | public | WideCube | false | NULL | false | false | +| minPrice | numeric | 102 | public | WideCube | false | NULL | false | false | +| avgPrice | numeric | 103 | public | WideCube | false | NULL | false | false | +| countDistinct | int8 | 104 | public | WideCube | false | NULL | false | false | +| dim0 | numeric | 105 | public | WideCube | false | NULL | false | false | +| dim1 | numeric | 106 | public | WideCube | false | NULL | false | false | +| dim2 | numeric | 107 | public | WideCube | false | NULL | false | false | +| dim3 | numeric | 108 | public | WideCube | false | NULL | false | false | +| dim4 | numeric | 109 | public | WideCube | false | NULL | false | false | +| dim5 | numeric | 110 | public | WideCube | false | NULL | false | false | +| dim6 | numeric | 111 | public | WideCube | false | NULL | false | false | +| dim7 | numeric | 112 | public | WideCube | false | NULL | false | false | +| dim8 | numeric | 113 | public | WideCube | false | NULL | false | false | +| dim9 | numeric | 114 | public | WideCube | false | NULL | false | false | +| dim10 | numeric | 115 | public | WideCube | false | NULL | false | false | +| dim11 | numeric | 116 | public | WideCube | false | NULL | false | false | +| dim12 | numeric | 117 | public | WideCube | false | NULL | false | false | +| dim13 | numeric | 118 | public | WideCube | false | NULL | false | false | +| dim14 | numeric | 119 | public | WideCube | false | NULL | false | false | +| dim15 | numeric | 120 | public | WideCube | false | NULL | false | false | +| dim16 | numeric | 121 | public | WideCube | false | NULL | false | false | +| dim17 | numeric | 122 | public | WideCube | false | NULL | false | false | +| dim18 | numeric | 123 | public | WideCube | false | NULL | false | false | +| dim19 | numeric | 124 | public | WideCube | false | NULL | false | false | +| dim20 | numeric | 125 | public | WideCube | false | NULL | false | false | +| dim21 | numeric | 126 | public | WideCube | false | NULL | false | false | +| dim22 | numeric | 127 | public | WideCube | false | NULL | false | false | +| dim23 | numeric | 128 | public | WideCube | false | NULL | false | false | +| dim24 | numeric | 129 | public | WideCube | false | NULL | false | false | +| dim25 | numeric | 130 | public | WideCube | false | NULL | false | false | +| dim26 | numeric | 131 | public | WideCube | false | NULL | false | false | +| dim27 | numeric | 132 | public | WideCube | false | NULL | false | false | +| dim28 | numeric | 133 | public | WideCube | false | NULL | false | false | +| dim29 | numeric | 134 | public | WideCube | false | NULL | false | false | +| dim30 | numeric | 135 | public | WideCube | false | NULL | false | false | +| dim31 | numeric | 136 | public | WideCube | false | NULL | false | false | +| dim32 | numeric | 137 | public | WideCube | false | NULL | false | false | +| dim33 | numeric | 138 | public | WideCube | false | NULL | false | false | +| dim34 | numeric | 139 | public | WideCube | false | NULL | false | false | +| dim35 | numeric | 140 | public | WideCube | false | NULL | false | false | +| dim36 | numeric | 141 | public | WideCube | false | NULL | false | false | +| dim37 | numeric | 142 | public | WideCube | false | NULL | false | false | +| dim38 | numeric | 143 | public | WideCube | false | NULL | false | false | +| dim39 | numeric | 144 | public | WideCube | false | NULL | false | false | +| dim40 | numeric | 145 | public | WideCube | false | NULL | false | false | +| dim41 | numeric | 146 | public | WideCube | false | NULL | false | false | +| dim42 | numeric | 147 | public | WideCube | false | NULL | false | false | +| dim43 | numeric | 148 | public | WideCube | false | NULL | false | false | +| dim44 | numeric | 149 | public | WideCube | false | NULL | false | false | +| dim45 | numeric | 150 | public | WideCube | false | NULL | false | false | +| dim46 | numeric | 151 | public | WideCube | false | NULL | false | false | +| dim47 | numeric | 152 | public | WideCube | false | NULL | false | false | +| dim48 | numeric | 153 | public | WideCube | false | NULL | false | false | +| dim49 | numeric | 154 | public | WideCube | false | NULL | false | false | +| dim50 | numeric | 155 | public | WideCube | false | NULL | false | false | +| dim51 | numeric | 156 | public | WideCube | false | NULL | false | false | +| dim52 | numeric | 157 | public | WideCube | false | NULL | false | false | +| dim53 | numeric | 158 | public | WideCube | false | NULL | false | false | +| dim54 | numeric | 159 | public | WideCube | false | NULL | false | false | +| dim55 | numeric | 160 | public | WideCube | false | NULL | false | false | +| dim56 | numeric | 161 | public | WideCube | false | NULL | false | false | +| dim57 | numeric | 162 | public | WideCube | false | NULL | false | false | +| dim58 | numeric | 163 | public | WideCube | false | NULL | false | false | +| dim59 | numeric | 164 | public | WideCube | false | NULL | false | false | +| dim60 | numeric | 165 | public | WideCube | false | NULL | false | false | +| dim61 | numeric | 166 | public | WideCube | false | NULL | false | false | +| dim62 | numeric | 167 | public | WideCube | false | NULL | false | false | +| dim63 | numeric | 168 | public | WideCube | false | NULL | false | false | +| dim64 | numeric | 169 | public | WideCube | false | NULL | false | false | +| dim65 | numeric | 170 | public | WideCube | false | NULL | false | false | +| dim66 | numeric | 171 | public | WideCube | false | NULL | false | false | +| dim67 | numeric | 172 | public | WideCube | false | NULL | false | false | +| dim68 | numeric | 173 | public | WideCube | false | NULL | false | false | +| dim69 | numeric | 174 | public | WideCube | false | NULL | false | false | +| dim70 | numeric | 175 | public | WideCube | false | NULL | false | false | +| dim71 | numeric | 176 | public | WideCube | false | NULL | false | false | +| dim72 | numeric | 177 | public | WideCube | false | NULL | false | false | +| dim73 | numeric | 178 | public | WideCube | false | NULL | false | false | +| dim74 | numeric | 179 | public | WideCube | false | NULL | false | false | +| dim75 | numeric | 180 | public | WideCube | false | NULL | false | false | +| dim76 | numeric | 181 | public | WideCube | false | NULL | false | false | +| dim77 | numeric | 182 | public | WideCube | false | NULL | false | false | +| dim78 | numeric | 183 | public | WideCube | false | NULL | false | false | +| dim79 | numeric | 184 | public | WideCube | false | NULL | false | false | +| dim80 | numeric | 185 | public | WideCube | false | NULL | false | false | +| dim81 | numeric | 186 | public | WideCube | false | NULL | false | false | +| dim82 | numeric | 187 | public | WideCube | false | NULL | false | false | +| dim83 | numeric | 188 | public | WideCube | false | NULL | false | false | +| dim84 | numeric | 189 | public | WideCube | false | NULL | false | false | +| dim85 | numeric | 190 | public | WideCube | false | NULL | false | false | +| dim86 | numeric | 191 | public | WideCube | false | NULL | false | false | +| dim87 | numeric | 192 | public | WideCube | false | NULL | false | false | +| dim88 | numeric | 193 | public | WideCube | false | NULL | false | false | +| dim89 | numeric | 194 | public | WideCube | false | NULL | false | false | +| dim90 | numeric | 195 | public | WideCube | false | NULL | false | false | +| dim91 | numeric | 196 | public | WideCube | false | NULL | false | false | +| dim92 | numeric | 197 | public | WideCube | false | NULL | false | false | +| dim93 | numeric | 198 | public | WideCube | false | NULL | false | false | +| dim94 | numeric | 199 | public | WideCube | false | NULL | false | false | +| dim95 | numeric | 200 | public | WideCube | false | NULL | false | false | +| dim96 | numeric | 201 | public | WideCube | false | NULL | false | false | +| dim97 | numeric | 202 | public | WideCube | false | NULL | false | false | +| dim98 | numeric | 203 | public | WideCube | false | NULL | false | false | +| dim99 | numeric | 204 | public | WideCube | false | NULL | false | false | +| __user | text | 205 | public | WideCube | false | NULL | false | false | +| __cubeJoinField | text | 206 | public | WideCube | false | NULL | false | false | ++--------------------+---------------+-------------------+--------------+---------------------------+-------+---------------+-------------------+----------------------------+ diff --git a/rust/cubesql/cubesql/src/compile/test/test_introspection.rs b/rust/cubesql/cubesql/src/compile/test/test_introspection.rs index e0e40723d2f4d..0a4f3b9fc0f24 100644 --- a/rust/cubesql/cubesql/src/compile/test/test_introspection.rs +++ b/rust/cubesql/cubesql/src/compile/test/test_introspection.rs @@ -3218,3 +3218,92 @@ async fn test_metabase_v0_51_2_introspection_field_indoption() -> Result<(), Cub ); Ok(()) } + +#[tokio::test] +async fn test_metabase_v0_51_8_introspection() -> Result<(), CubeError> { + init_testing_logger(); + + insta::assert_snapshot!( + "test_metabase_v0_51_8_introspection", + execute_query( + // language=PostgreSQL + r#" +select + "c"."column_name" as "name", + case + when "c"."udt_schema" in ('public', 'pg_catalog') + then format('%s', "c"."udt_name") + else format('"%s"."%s"', "c"."udt_schema", "c"."udt_name") + end as "database-type", + "c"."ordinal_position" - 1 as "database-position", + "c"."table_schema" as "table-schema", + "c"."table_name" as "table-name", + "pk"."column_name" is not null as "pk?", + col_description( + cast( + cast( + format( + '%I.%I', + cast("c"."table_schema" as text), + cast("c"."table_name" as text) + ) as regclass + ) as oid + ), + "c"."ordinal_position" + ) as "field-comment", + (("column_default" is null) or (lower("column_default") = 'null')) + and ("is_nullable" = 'NO') + and not ( + (("column_default" is not null) and ("column_default" like '%nextval(%')) + or ("is_identity" <> 'NO') + ) as "database-required", + (("column_default" is not null) and ("column_default" like '%nextval(%')) + or ("is_identity" <> 'NO') as "database-is-auto-increment" +from "information_schema"."columns" as "c" +left join + ( + select "tc"."table_schema", "tc"."table_name", "kc"."column_name" + from "information_schema"."table_constraints" as "tc" + inner join + "information_schema"."key_column_usage" as "kc" + on ("tc"."constraint_name" = "kc"."constraint_name") + and ("tc"."table_schema" = "kc"."table_schema") + and ("tc"."table_name" = "kc"."table_name") + where "tc"."constraint_type" = 'PRIMARY KEY' + ) as "pk" + on ("c"."table_schema" = "pk"."table_schema") + and ("c"."table_name" = "pk"."table_name") + and ("c"."column_name" = "pk"."column_name") +where + c.table_schema !~ '^information_schema|catalog_history|pg_' + and ("c"."table_schema" in ('public')) +union all +select + "pa"."attname" as "name", + case + when "ptn"."nspname" in ('public', 'pg_catalog') + then format('%s', "pt"."typname") + else format('"%s"."%s"', "ptn"."nspname", "pt"."typname") + end as "database-type", + "pa"."attnum" - 1 as "database-position", + "pn"."nspname" as "table-schema", + "pc"."relname" as "table-name", + false as "pk?", + null as "field-comment", + false as "database-required", + false as "database-is-auto-increment" +from "pg_catalog"."pg_class" as "pc" +inner join "pg_catalog"."pg_namespace" as "pn" on "pn"."oid" = "pc"."relnamespace" +inner join "pg_catalog"."pg_attribute" as "pa" on "pa"."attrelid" = "pc"."oid" +inner join "pg_catalog"."pg_type" as "pt" on "pt"."oid" = "pa"."atttypid" +inner join "pg_catalog"."pg_namespace" as "ptn" on "ptn"."oid" = "pt"."typnamespace" +where ("pc"."relkind" = 'm') and ("pa"."attnum" >= 1) and ("pn"."nspname" in ('public')) +order by "table-schema" asc, "table-name" asc, "database-position" asc + "# + .to_string(), + DatabaseProtocol::PostgreSQL + ) + .await? + ); + Ok(()) +}