diff --git a/Cargo.lock b/Cargo.lock index 223889581470d..b7473fdd9243f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6232,7 +6232,6 @@ dependencies = [ "openssl", "opentelemetry", "prometheus", - "proptest", "prost", "qcell", "rand 0.10.3", diff --git a/doc/user/content/sql/functions/cast.md b/doc/user/content/sql/functions/cast.md index 67bf61a6ad6ad..d92a29ceb4d63 100644 --- a/doc/user/content/sql/functions/cast.md +++ b/doc/user/content/sql/functions/cast.md @@ -1,14 +1,12 @@ --- -title: "CAST and TRY_CAST" +title: "CAST function and operator" description: "Returns the value converted to the specified type" menu: main: parent: 'sql-functions' --- -The `cast` function and operator return a value converted to the specified -[type](../../types/). The `try_cast` function does the same, but returns `NULL` -instead of an error when the value cannot be converted. +The `cast` function and operator return a value converted to the specified [type](../../types/). ## Signatures @@ -20,16 +18,10 @@ The following special syntax is permitted if _val_ is a string literal: {{% include-syntax file="examples/sql_functions/cast" example="syntax-literal" %}} -{{% include-syntax file="examples/sql_functions/cast" example="syntax-try-cast" %}} - ### Return value `cast` returns the value with the type specified by the _type_ parameter. -`try_cast` returns the value with the type specified by the _type_ parameter, -or `NULL` if the conversion fails. Its result is nullable whenever the -conversion can fail, even when the value being converted is not. - ## Details ### Valid casts @@ -177,67 +169,6 @@ Source type | Return type 2 Casting a [`float`](../../types/float/) to a [`numeric`](../../types/numeric/) can yield an imprecise result due to the floating point arithmetic involved in the conversion. -### `TRY_CAST` - -{{< public-preview />}} - -`TRY_CAST` accepts the same explicit casts as `CAST`, and returns `NULL` where -`CAST` would return an error: for example, text that does not parse as the -target type, a number that does not fit in the target type, or a JSON value of -the wrong kind. When the conversion succeeds, `TRY_CAST` and `CAST` return the -same value. - -`TRY_CAST` only suppresses errors from the conversion itself. Errors raised -while computing the value being converted still propagate: - -```mzsql -SELECT TRY_CAST(1 / 0 AS text); -``` -```nofmt -ERROR: division by zero -``` - -A parameter whose type is inferred from `TRY_CAST` is decoded as the target -type when the statement is bound, so a bound value that does not convert is a -bind error rather than `NULL`. To get `NULL` instead, give the parameter an -explicit type: - -```mzsql -SELECT TRY_CAST($1::text AS int); -``` - -Converting a value of a composite type ([`array`](../../types/array/), -[`list`](../../types/list/), or [`record`](../../types/record/)) converts each -element. If any element fails to convert, `TRY_CAST` returns `NULL` for the -whole value rather than a value with `NULL` elements. - -`TRY_CAST` accepts every explicit cast that `CAST` does, with these -differences, each of which is reported when the statement is planned: - -- `TRY_CAST` does not support the casts that look up objects in the catalog: - casts to or from `regclass`, `regproc`, `regtype`, `aclitem`, and - [`mz_aclitem`](../../types/mz_aclitem/), other than those between the `oid` - alias types and the integer types. - -- An `ARRAY`, `LIST`, or `ROW` constructor written directly inside `TRY_CAST` - is typed on its own before the conversion, where `CAST` would type it from - the target. Its elements must therefore already agree on a type, and an - empty constructor needs an explicit type: - - ```mzsql - -- With a text column s, CAST(ARRAY[s, 2] AS int[]) plans, but this does not: - -- "ARRAY types text and integer cannot be matched". - SELECT TRY_CAST(ARRAY[s, 2] AS int[]) FROM t; - -- Give the elements a common type instead. - SELECT TRY_CAST(ARRAY[s, 2::text] AS int[]) FROM t; - SELECT TRY_CAST(ARRAY[]::text[] AS int[]); - ``` - -- A `MAP` constructor written directly inside `TRY_CAST` is rejected. It would - be typed on its own like the other constructors, and there is no cast - between two [`map`](../../types/map/) types to convert it afterward. Convert - it with `CAST` or `::` instead. - ## Examples ```mzsql @@ -271,29 +202,5 @@ SELECT 100.21::numeric(10, 2)::float AS dec_to_float; 100.21 ``` -
- -```mzsql -SELECT TRY_CAST('42' AS int) AS ok, TRY_CAST('forty-two' AS int) AS bad; -``` -```nofmt - ok | bad -----+----- - 42 | -``` - -
- -Routing rows whose values do not convert, instead of failing the whole view: - -```mzsql -CREATE VIEW parsed AS - SELECT raw, TRY_CAST(raw AS timestamp) AS ts - FROM events; - -CREATE VIEW unparseable AS - SELECT raw FROM parsed WHERE ts IS NULL AND raw IS NOT NULL; -``` - ## Related topics * [Data Types](../../types/) diff --git a/doc/user/data/examples/sql_functions/cast.yml b/doc/user/data/examples/sql_functions/cast.yml index d73537fd51770..edaad0b222a60 100644 --- a/doc/user/data/examples/sql_functions/cast.yml +++ b/doc/user/data/examples/sql_functions/cast.yml @@ -20,17 +20,6 @@ description: | The return value's [type](/sql/types/). -- name: "syntax-try-cast" - code: | - TRY_CAST ( AS ) - syntax_elements: - - name: "``" - description: | - Any value you want to convert. - - name: "**AS** ``" - description: | - The return value's [type](/sql/types/). - - name: "syntax-literal" code: | '' diff --git a/doc/user/data/sql_funcs.yml b/doc/user/data/sql_funcs.yml index dd35ce712ed5b..3882e88901889 100644 --- a/doc/user/data/sql_funcs.yml +++ b/doc/user/data/sql_funcs.yml @@ -5,11 +5,6 @@ description: Value as type `T` url: /sql/functions/cast - - signature: TRY_CAST (cast_expr) -> T? - description: Value as type `T`, or _NULL_ if the conversion fails. - url: /sql/functions/cast#try_cast - version-added: v26.45 - - signature: 'coalesce(x: T...) -> T?' description: First non-_NULL_ arg, or _NULL_ if all are _NULL_. diff --git a/misc/python/materialize/mzcompose/__init__.py b/misc/python/materialize/mzcompose/__init__.py index 014a9a0af967e..ac33a4161dc18 100644 --- a/misc/python/materialize/mzcompose/__init__.py +++ b/misc/python/materialize/mzcompose/__init__.py @@ -134,7 +134,6 @@ def get_minimal_system_parameters( ), "enable_s3_tables_region_check": "false", "enable_statement_lifecycle_logging": "true", - "enable_try_cast": "true", # Introspection goldens depend on the replica topology, so tests need # one consistent value rather than a varying one. "enable_unified_cluster": ( diff --git a/src/adapter/Cargo.toml b/src/adapter/Cargo.toml index c296b52d51ef6..2ecb728a48c27 100644 --- a/src/adapter/Cargo.toml +++ b/src/adapter/Cargo.toml @@ -107,8 +107,6 @@ datadriven.workspace = true mz-alloc-default = { path = "../alloc-default" } mz-persist = { path = "../persist", features = ["turmoil"] } mz-persist-client = { path = "../persist-client", features = ["turmoil"] } -mz-repr = { path = "../repr", features = ["proptest"] } -proptest.workspace = true turmoil.workspace = true [[bench]] diff --git a/src/adapter/src/catalog.rs b/src/adapter/src/catalog.rs index c776e39f0c51f..402855e4565d3 100644 --- a/src/adapter/src/catalog.rs +++ b/src/adapter/src/catalog.rs @@ -3713,376 +3713,6 @@ mod tests { } } - /// `TRY_CAST` must plan as the strict cast with every unary stage wrapped - /// in `TryCast`, for every pair of types the strict cast supports, except - /// the SQL-implemented casts, which it must refuse with a dedicated error. - #[mz_ore::test(tokio::test)] - #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `TLS_client_method` on OS `linux` - async fn test_try_cast_wraps_every_stage_of_every_cast() { - use mz_expr::UnaryFunc; - use mz_expr::func::TryCast; - use mz_expr::visit::{Visit, VisitChildren}; - use mz_ore::collections::CollectionExt; - use mz_repr::SqlScalarBaseType as B; - use mz_repr::explain::ExprHumanizer; - use mz_sql::plan::{HirRelationExpr, Params, Plan, PlanError, valid_cast_base_type_pairs}; - - /// Calls `f` on every scalar expression node in `rel`, including those - /// inside subqueries. - fn for_each_scalar(rel: &HirRelationExpr, f: &mut impl FnMut(&HirScalarExpr)) { - rel.visit_pre(&mut |rel: &HirRelationExpr| { - rel.visit_children(|scalar: &HirScalarExpr| scalar.visit_pre(&mut *f)); - }); - } - - fn unary_calls(rel: &HirRelationExpr) -> Vec { - let mut funcs = vec![]; - for_each_scalar(rel, &mut |e| { - if let HirScalarExpr::CallUnary { func, .. } = e { - funcs.push(func.clone()); - } - }); - funcs - } - - fn has_subquery(rel: &HirRelationExpr) -> bool { - let mut found = false; - for_each_scalar(rel, &mut |e| { - found |= matches!(e, HirScalarExpr::Exists(..) | HirScalarExpr::Select(..)); - }); - found - } - - Catalog::with_debug(|mut catalog| async move { - catalog - .system_config_mut() - .set("enable_try_cast", VarInput::Flat("on")) - .expect("valid setting"); - // A record type can only be named in SQL through a user-defined - // type, so create one to reach the record-to-record cast. - let (id, gid) = catalog - .allocate_user_id_for_test() - .await - .expect("allocate id"); - let item = catalog - .state() - .deserialize_item( - gid, - "CREATE TYPE materialize.public.rec AS (a pg_catalog.int8, b pg_catalog.text)", - &BTreeMap::new(), - &mut LocalExpressionCache::Closed, - None, - ) - .expect("valid type"); - let commit_ts = catalog.current_upper().await; - catalog - .transact( - None, - commit_ts, - None, - vec![Op::CreateItem { - item, - name: QualifiedItemName { - qualifiers: ItemQualifiers { - database_spec: ResolvedDatabaseSpecifier::Id(DatabaseId::User(1)), - schema_spec: SchemaSpecifier::Id(SchemaId::User(3)), - }, - item: "rec".to_string(), - }, - id, - owner_id: MZ_SYSTEM_ROLE_ID, - }], - ) - .await - .expect("create type"); - let conn_catalog = catalog.for_system_session(); - let pcx = PlanContext::zero(); - let plan_select = |expr: std::string::String| -> Result { - let sql = format!("SELECT {expr}"); - let stmt = mz_sql_parser::parser::parse_statements(&sql) - .expect("parses") - .into_element() - .ast; - let (stmt, resolved_ids) = mz_sql::names::resolve(&conn_catalog, stmt)?; - let params = Params::empty(); - let (plan, _) = - mz_sql::plan::plan(Some(&pcx), &conn_catalog, stmt, ¶ms, &resolved_ids)?; - match plan { - Plan::Select(plan) => Ok(plan.source), - other => panic!("expected a SELECT plan, got {other:?}"), - } - }; - - // `SqlScalarType::enumerate` has no instance of the parameterized - // type families, nor of `name` and `aclitem`, so those come as - // hand-written SQL. A source is an expression of the type; a target - // is a type name. The anonymous record source has a field of a - // different type than `rec`, so casting it there converts a field. - let mut sources: Vec<(std::string::String, B)> = SqlScalarType::enumerate() - .iter() - .map(|ty| { - let name = conn_catalog.humanize_sql_scalar_type(ty, false); - (format!("NULL::{name}"), ty.into()) - }) - .collect(); - let mut targets: Vec<(std::string::String, B)> = SqlScalarType::enumerate() - .iter() - .map(|ty| (conn_catalog.humanize_sql_scalar_type(ty, false), ty.into())) - .collect(); - let extra_types = [ - ("name", B::PgLegacyName), - ("aclitem", B::AclItem), - ("int4 list", B::List), - ("int8 list", B::List), - ("int2[]", B::Array), - ("int4[]", B::Array), - ("int8[]", B::Array), - ("map[text=>int4]", B::Map), - ("int4range", B::Range), - ("materialize.public.rec", B::Record), - ]; - for (name, base) in extra_types { - sources.push((format!("NULL::{name}"), base)); - targets.push((name.to_string(), base)); - } - sources.push(("ROW(1, 'a')".to_string(), B::Record)); - - let mut exercised = BTreeSet::new(); - let mut unsupported = BTreeSet::new(); - let mut unplannable = BTreeSet::new(); - for (source, from) in &sources { - for (to_name, to) in &targets { - let strict = plan_select(format!("CAST({source} AS {to_name})")); - let lenient = plan_select(format!("TRY_CAST({source} AS {to_name})")); - let strict = match strict { - Ok(strict) => strict, - Err(PlanError::InvalidCast { .. }) => { - assert!( - matches!(lenient, Err(PlanError::InvalidCast { .. })), - "{source} to {to_name}: CAST is invalid, so TRY_CAST must be too, got {lenient:?}" - ); - continue; - } - Err(_) => { - unplannable.insert((source.clone(), to_name.clone())); - continue; - } - }; - let strict_funcs = unary_calls(&strict); - assert!( - !strict_funcs.iter().any(|f| matches!(f, UnaryFunc::TryCast(_))), - "{source} to {to_name}: CAST must not produce TryCast, got {strict_funcs:?}" - ); - exercised.insert((*from, *to)); - match lenient { - Ok(lenient) => { - // Spelled out rather than built with - // `UnaryFunc::try_cast`, which the planner itself - // uses: an oracle built from the code under test - // would pass an always-wrap mutant. - let wrapped: Vec = strict_funcs - .iter() - .map(|f| { - if f.could_error() { - UnaryFunc::TryCast(TryCast { - inner: Box::new(f.clone()), - }) - } else { - f.clone() - } - }) - .collect(); - assert_eq!( - unary_calls(&lenient), - wrapped, - "{source} to {to_name}: TRY_CAST must wrap every fallible stage of CAST" - ); - } - Err(PlanError::TryCastUnsupported { .. }) => { - assert!( - has_subquery(&strict), - "{source} to {to_name}: TRY_CAST refused a cast that is not SQL-implemented" - ); - unsupported.insert((*from, *to)); - } - Err(err) => panic!("{source} to {to_name}: unexpected error {err}"), - } - } - } - assert!(unplannable.is_empty(), "casts that did not plan: {unplannable:?}"); - - let table: BTreeSet<(B, B)> = valid_cast_base_type_pairs().collect(); - let unexercised: Vec<_> = table.difference(&exercised).collect(); - assert!( - unexercised.is_empty(), - "cast table entries the type lists above do not reach: {unexercised:?}" - ); - - // The string category routes through `text`, so each catalog-lookup - // cast is refused for every string-like type. - let string_like = [B::String, B::Char, B::VarChar, B::PgLegacyChar, B::PgLegacyName]; - let mut expected = BTreeSet::new(); - for s in string_like { - for reg in [B::RegClass, B::RegProc, B::RegType] { - expected.insert((s, reg)); - expected.insert((reg, s)); - } - for acl in [B::AclItem, B::MzAclItem] { - expected.insert((acl, s)); - } - } - expected.insert((B::AclItem, B::MzAclItem)); - expected.insert((B::MzAclItem, B::AclItem)); - assert_eq!(unsupported, expected); - }) - .await; - } - - /// For every type pair `TRY_CAST` supports and many values of the source - /// type, `TRY_CAST` agrees with `CAST` where `CAST` succeeds and is NULL - /// where `CAST` errors. It never errors itself. - #[mz_ore::test(tokio::test)] - #[cfg_attr(miri, ignore)] // unsupported operation: can't call foreign function `TLS_client_method` on OS `linux` - async fn proptest_try_cast_agrees_with_cast() { - use std::cell::Cell; - - use mz_expr::CastFailureMode; - use mz_repr::{ColumnName, arb_datum_for_scalar}; - use mz_sql::plan::{CastContext, plan_hypothetical_cast_with_failure_mode}; - use proptest::test_runner::{Config, TestRunner}; - - fn record(fields: &[(&str, SqlScalarType)]) -> SqlScalarType { - SqlScalarType::Record { - fields: fields - .iter() - .map(|(name, ty)| (ColumnName::from(*name), ty.clone().nullable(true))) - .collect(), - custom_id: None, - } - } - - Catalog::with_debug(|catalog| async move { - let conn_catalog = catalog.for_system_session(); - let pcx = PlanContext::zero(); - let scx = StatementContext::new(Some(&pcx), &conn_catalog); - let qcx = QueryContext::root(&scx, QueryLifetime::OneShot); - let ecx = ExprContext { - qcx: &qcx, - name: "proptest_try_cast", - scope: &Scope::empty(), - relation_type: &SqlRelationType::empty(), - allow_aggregates: false, - allow_subqueries: true, - allow_parameters: false, - allow_windows: false, - }; - let plan = |mode, from: &SqlScalarType, to: &SqlScalarType| { - let ccx = CastContext::Explicit; - plan_hypothetical_cast_with_failure_mode(&ecx, ccx, mode, from, to) - }; - - // `SqlScalarType::enumerate` has no instance of the parameterized - // type families, nor of `name` and `aclitem`. Two widths of each - // container so that the element casts are exercised. - let mut types = SqlScalarType::enumerate().to_vec(); - types.extend([ - SqlScalarType::PgLegacyName, - SqlScalarType::AclItem, - SqlScalarType::List { - element_type: Box::new(SqlScalarType::Int32), - custom_id: None, - }, - SqlScalarType::List { - element_type: Box::new(SqlScalarType::Int64), - custom_id: None, - }, - SqlScalarType::Array(Box::new(SqlScalarType::Int32)), - SqlScalarType::Array(Box::new(SqlScalarType::Int64)), - SqlScalarType::Map { - value_type: Box::new(SqlScalarType::Int32), - custom_id: None, - }, - SqlScalarType::Range { - element_type: Box::new(SqlScalarType::Int32), - }, - record(&[("a", SqlScalarType::Int32), ("b", SqlScalarType::String)]), - record(&[("a", SqlScalarType::Int64), ("b", SqlScalarType::String)]), - ]); - - let mut pairs = 0usize; - // Counted from inside `check`, which the proptest runner requires - // to be `Fn`, hence the `Cell`. - let values = Cell::new(0usize); - for from in &types { - for to in &types { - let Some(strict) = plan(CastFailureMode::Error, from, to) else { - continue; - }; - let Some(lenient) = plan(CastFailureMode::NullFallback, from, to) else { - // The SQL-implemented casts, refused by the planner. - continue; - }; - pairs += 1; - let arena = RowArena::new(); - let check = |datum: Datum| { - values.set(values.get() + 1); - let row = [datum]; - let strict_result = strict.eval(&row, &arena); - let lenient_result = lenient.eval(&row, &arena); - match (&strict_result, &lenient_result) { - (Ok(s), Ok(l)) => assert_eq!( - s, l, - "{from:?} to {to:?} on {datum}: TRY_CAST differs from CAST" - ), - (Err(_), Ok(Datum::Null)) => {} - (Err(err), Ok(l)) => panic!( - "{from:?} to {to:?} on {datum}: CAST errors ({err}) but TRY_CAST gives {l}" - ), - (_, Err(err)) => { - panic!("{from:?} to {to:?} on {datum}: TRY_CAST errored: {err}") - } - } - }; - - // Edge values, then NULL, then random values of the source - // type. - for datum in from.interesting_datums() { - check(datum); - } - check(Datum::Null); - let mut runner = TestRunner::new(Config { - cases: 64, - ..Config::default() - }); - runner - .run(&arb_datum_for_scalar(from.clone()), |pd| { - check(Datum::from(&pd)); - Ok(()) - }) - .expect("oracle holds"); - - // A random string almost never parses as the target type, - // so for string sources also feed the rendering of random - // target values, which exercises the success branch. - let render = plan(CastFailureMode::Error, to, &SqlScalarType::String); - if from.base_eq(&SqlScalarType::String) && let Some(render) = render { - runner - .run(&arb_datum_for_scalar(to.clone()), |pd| { - if let Ok(s) = render.eval(&[Datum::from(&pd)], &arena) { - check(s); - } - Ok(()) - }) - .expect("oracle holds"); - } - } - } - assert!(pairs > 200, "only {pairs} pairs exercised"); - assert!(values.get() > 10_000, "only {} values checked", values.get()); - }) - .await; - } - fn smoketest_fn( name: &&str, call_name: String, diff --git a/src/compute-types/tests/snapshots/func_registry.json b/src/compute-types/tests/snapshots/func_registry.json index 40d22170943a0..3011949cfcf27 100644 --- a/src/compute-types/tests/snapshots/func_registry.json +++ b/src/compute-types/tests/snapshots/func_registry.json @@ -578,11 +578,6 @@ "trunc_float32": {"variant":"TruncFloat32","propagates_nulls":true,"introduces_nulls":false,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["Float32:NotNull"],"output_type":"Float32:NotNull","sqlfunc_signature":"fn(f32) -> f32"}, "trunc_float64": {"variant":"TruncFloat64","propagates_nulls":true,"introduces_nulls":false,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["Float64:NotNull"],"output_type":"Float64:NotNull","sqlfunc_signature":"fn(f64) -> f64"}, "trunc_numeric": {"variant":"TruncNumeric","propagates_nulls":true,"introduces_nulls":false,"could_error":false,"preserves_uniqueness":false,"is_monotone":true,"is_eliminable_cast":false,"inverse":null,"input_types":["Numeric { max_scale: None }:NotNull"],"output_type":"Numeric { max_scale: None }:NotNull","sqlfunc_signature":"fn(Numeric) -> Numeric"}, - "try_cast": {"variant":"TryCast","propagates_nulls":true,"introduces_nulls":true,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["String:NotNull"],"output_type":"Int32:Null","sqlfunc_signature":null}, - "try_cast[infallible]": {"variant":"TryCast","propagates_nulls":true,"introduces_nulls":false,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["Int32:NotNull"],"output_type":"Int64:Null","sqlfunc_signature":null}, - "try_cast[jsonb]": {"variant":"TryCast","propagates_nulls":true,"introduces_nulls":true,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["Jsonb:NotNull"],"output_type":"Numeric { max_scale: None }:Null","sqlfunc_signature":null}, - "try_cast[list]": {"variant":"TryCast","propagates_nulls":true,"introduces_nulls":true,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["List { element_type: Int64, custom_id: None }:NotNull"],"output_type":"List { element_type: Int32, custom_id: None }:Null","sqlfunc_signature":null}, - "try_cast[narrowing]": {"variant":"TryCast","propagates_nulls":true,"introduces_nulls":true,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["Int64:NotNull"],"output_type":"Int32:Null","sqlfunc_signature":null}, "try_parse_monotonic_iso8601_timestamp": {"variant":"TryParseMonotonicIso8601Timestamp","propagates_nulls":true,"introduces_nulls":true,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["String:NotNull"],"output_type":"Timestamp { precision: None }:Null","sqlfunc_signature":"fn(&'a str) -> Option>"}, "upper": {"variant":"Upper","propagates_nulls":true,"introduces_nulls":false,"could_error":false,"preserves_uniqueness":false,"is_monotone":false,"is_eliminable_cast":false,"inverse":null,"input_types":["String:NotNull"],"output_type":"String:NotNull","sqlfunc_signature":"fn(&'a str) -> String"} }, diff --git a/src/compute-types/tests/snapshots/func_registry_digests.json b/src/compute-types/tests/snapshots/func_registry_digests.json index 5f8d4a2116229..83abe62f0005d 100644 --- a/src/compute-types/tests/snapshots/func_registry_digests.json +++ b/src/compute-types/tests/snapshots/func_registry_digests.json @@ -1,3 +1,3 @@ { - "1": "sha256:6464d6eed984478e19c76a27b6cd2f5b86318b8fffabd73d0aa70d7f41a5db4b" + "1": "sha256:628fcb08bde495ddd92d1f1d004c4abfa5b69b6456b4ccac1580a43aed8a601c" } diff --git a/src/compute-types/tests/snapshots/func_registry_source.json b/src/compute-types/tests/snapshots/func_registry_source.json index 413670921c869..f5aaf0b008968 100644 --- a/src/compute-types/tests/snapshots/func_registry_source.json +++ b/src/compute-types/tests/snapshots/func_registry_source.json @@ -578,11 +578,6 @@ "trunc_float32": {"display":"truncf32","sqlfunc_decl":"#[sqlfunc(sqlname = \"truncf32\")] fn trunc_float32(a: f32) -> f32","body_fingerprint":"b9a309bede7a3d9d"}, "trunc_float64": {"display":"truncf64","sqlfunc_decl":"#[sqlfunc(sqlname = \"truncf64\")] fn trunc_float64(a: f64) -> f64","body_fingerprint":"b9a309bede7a3d9d"}, "trunc_numeric": {"display":"truncnumeric","sqlfunc_decl":"#[sqlfunc(sqlname = \"truncnumeric\", is_monotone = true)] fn trunc_numeric(mut a: Numeric) -> Numeric","body_fingerprint":"325db9fb2ac6dff2"}, - "try_cast": {"display":"try_cast[text_to_integer]","sqlfunc_decl":null,"body_fingerprint":null}, - "try_cast[infallible]": {"display":"try_cast[integer_to_bigint]","sqlfunc_decl":null,"body_fingerprint":null}, - "try_cast[jsonb]": {"display":"try_cast[jsonb_to_numeric]","sqlfunc_decl":null,"body_fingerprint":null}, - "try_cast[list]": {"display":"try_cast[list1tolist2]","sqlfunc_decl":null,"body_fingerprint":null}, - "try_cast[narrowing]": {"display":"try_cast[bigint_to_integer]","sqlfunc_decl":null,"body_fingerprint":null}, "try_parse_monotonic_iso8601_timestamp": {"display":"try_parse_monotonic_iso8601_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"try_parse_monotonic_iso8601_timestamp\")] fn try_parse_monotonic_iso8601_timestamp<'a>(a: &'a str,) -> Option>","body_fingerprint":"248be614321cc3e0"}, "upper": {"display":"upper","sqlfunc_decl":"#[sqlfunc] fn upper<'a>(a: &'a str) -> String","body_fingerprint":"b2fbf9e0812208ab"} }, diff --git a/src/compute-types/tests/snapshots/lir_v1.json b/src/compute-types/tests/snapshots/lir_v1.json index 8abbd8dc80994..f303aa43faf80 100644 --- a/src/compute-types/tests/snapshots/lir_v1.json +++ b/src/compute-types/tests/snapshots/lir_v1.json @@ -3654,13 +3654,6 @@ "TruncFloat32": "UNITSTRUCT", "TruncFloat64": "UNITSTRUCT", "TruncNumeric": "UNITSTRUCT", - "TryCast": { - "STRUCT": { - "inner": { - "TYPENAME": "UnaryFunc" - } - } - }, "TryParseMonotonicIso8601Timestamp": "UNITSTRUCT", "UnaryFunc": { "ENUM": { @@ -5334,11 +5327,6 @@ "TYPENAME": "TruncNumeric" } }, - "TryCast": { - "NEWTYPE": { - "TYPENAME": "TryCast" - } - }, "TryParseMonotonicIso8601Timestamp": { "NEWTYPE": { "TYPENAME": "TryParseMonotonicIso8601Timestamp" diff --git a/src/expr-parser/src/parser.rs b/src/expr-parser/src/parser.rs index d25449ae5622e..d446a382903ea 100644 --- a/src/expr-parser/src/parser.rs +++ b/src/expr-parser/src/parser.rs @@ -1380,22 +1380,6 @@ mod scalar { exprs, )) } - // `try_cast[]()` wraps the named cast so - // that its errors become NULL. - "try_cast" => { - let name = params.parse::()?; - let Some(cast) = UnaryFunc::from_variant_name(&name.to_string()) else { - return Err(Error::new(name.span(), "unsupported function name")); - }; - let expr = Box::new(parse_expr(&inner)?); - Ok(MirScalarExpr::CallUnary { - func: func::TryCast { - inner: Box::new(cast), - } - .into(), - expr, - }) - } _ => Err(Error::new( ident.span(), "unsupported parameterized function", diff --git a/src/expr/fuzz/fuzz_targets/mfp_optimize.rs b/src/expr/fuzz/fuzz_targets/mfp_optimize.rs index 58fd0c959e566..8ae846c935ac8 100644 --- a/src/expr/fuzz/fuzz_targets/mfp_optimize.rs +++ b/src/expr/fuzz/fuzz_targets/mfp_optimize.rs @@ -71,7 +71,7 @@ use std::collections::{BTreeMap, BTreeSet}; use libfuzzer_sys::arbitrary::{self, Arbitrary, Unstructured}; use libfuzzer_sys::fuzz_target; use mz_expr::{ - Eval, EvalError, MapFilterProject, MirScalarExpr, SafeMfpPlan, UnaryFunc, UnmaterializableFunc, + Eval, EvalError, MapFilterProject, MirScalarExpr, SafeMfpPlan, UnmaterializableFunc, VariadicFunc, func, }; use mz_repr::{Datum, Diff, ReprScalarType, Row, RowArena, Timestamp}; @@ -165,22 +165,6 @@ fn gen_leaf(u: &mut Unstructured, want: Ty, cols: &[Ty]) -> arbitrary::Result, -) -> arbitrary::Result { - let cast = cast.into(); - Ok(if bool::arbitrary(u)? { - UnaryFunc::try_cast(cast) - } else { - cast - }) -} - fn gen_scalar( u: &mut Unstructured, want: Ty, @@ -208,8 +192,7 @@ fn gen_scalar( 4 => Ok(gen_scalar(u, Ty::Int, cols, d)? .call_binary(gen_scalar(u, Ty::Int, cols, d)?, func::ModInt32)), // int8 -> int4 (fallible: out-of-range overflows). - _ => Ok(gen_scalar(u, Ty::Long, cols, d)? - .call_unary(maybe_try_cast(u, func::CastInt64ToInt32)?)), + _ => Ok(gen_scalar(u, Ty::Long, cols, d)?.call_unary(func::CastInt64ToInt32)), }, Ty::Long => match u.int_in_range(0u8..=4)? { 0 => { @@ -225,8 +208,7 @@ fn gen_scalar( 3 => Ok(gen_scalar(u, Ty::Long, cols, d)? .call_binary(gen_scalar(u, Ty::Long, cols, d)?, func::MulInt64)), // int4 -> int8 (infallible widening). - _ => Ok(gen_scalar(u, Ty::Int, cols, d)? - .call_unary(maybe_try_cast(u, func::CastInt32ToInt64)?)), + _ => Ok(gen_scalar(u, Ty::Int, cols, d)?.call_unary(func::CastInt32ToInt64)), }, Ty::Bool => match u.int_in_range(0u8..=5)? { 0 => { diff --git a/src/expr/fuzz/fuzz_targets/mir_scalar_reduce.rs b/src/expr/fuzz/fuzz_targets/mir_scalar_reduce.rs index 8d0c073bd204e..975bd23ab40e6 100644 --- a/src/expr/fuzz/fuzz_targets/mir_scalar_reduce.rs +++ b/src/expr/fuzz/fuzz_targets/mir_scalar_reduce.rs @@ -66,7 +66,7 @@ use libfuzzer_sys::arbitrary::{self, Arbitrary, Unstructured}; use libfuzzer_sys::fuzz_target; use mz_expr::func::variadic::{And, Or}; -use mz_expr::{Eval, EvalError, MirScalarExpr, UnaryFunc, VariadicFunc, func}; +use mz_expr::{Eval, EvalError, MirScalarExpr, VariadicFunc, func}; use mz_repr::{Datum, ReprColumnType, ReprScalarType, Row, RowArena}; // Column layout: a contiguous block per type. Columns are nullable. @@ -273,22 +273,6 @@ fn gen_wide_and_or(u: &mut Unstructured, depth: u32) -> arbitrary::Result, -) -> arbitrary::Result { - let cast = cast.into(); - Ok(if bool::arbitrary(u)? { - UnaryFunc::try_cast(cast) - } else { - cast - }) -} - fn gen_expr(u: &mut Unstructured, ty: Ty, depth: u32) -> arbitrary::Result { if depth == 0 || u.ratio(2u8, 5u8)? { return gen_leaf(u, ty); @@ -313,10 +297,8 @@ fn gen_expr(u: &mut Unstructured, ty: Ty, depth: u32) -> arbitrary::Result Ok(gen_expr(u, Ty::Int, d)?.call_unary(func::AbsInt32)), // Casts that produce an int4. _ => match u.int_in_range(0u8..=2)? { - 0 => Ok(gen_expr(u, Ty::Long, d)? - .call_unary(maybe_try_cast(u, func::CastInt64ToInt32)?)), - 1 => Ok(gen_expr(u, Ty::Str, d)? - .call_unary(maybe_try_cast(u, func::CastStringToInt32)?)), + 0 => Ok(gen_expr(u, Ty::Long, d)?.call_unary(func::CastInt64ToInt32)), + 1 => Ok(gen_expr(u, Ty::Str, d)?.call_unary(func::CastStringToInt32)), _ => Ok(gen_expr(u, Ty::Str, d)?.call_unary(func::ByteLengthString)), }, }, @@ -342,10 +324,8 @@ fn gen_expr(u: &mut Unstructured, ty: Ty, depth: u32) -> arbitrary::Result Ok(gen_expr(u, Ty::Long, d)?.call_unary(func::AbsInt64)), // Casts that produce an int8. _ => match u.int_in_range(0u8..=1)? { - 0 => Ok(gen_expr(u, Ty::Int, d)? - .call_unary(maybe_try_cast(u, func::CastInt32ToInt64)?)), - _ => Ok(gen_expr(u, Ty::Str, d)? - .call_unary(maybe_try_cast(u, func::CastStringToInt64)?)), + 0 => Ok(gen_expr(u, Ty::Int, d)?.call_unary(func::CastInt32ToInt64)), + _ => Ok(gen_expr(u, Ty::Str, d)?.call_unary(func::CastStringToInt64)), }, } } @@ -381,24 +361,20 @@ fn gen_expr(u: &mut Unstructured, ty: Ty, depth: u32) -> arbitrary::Result bool cast. - _ => Ok(gen_expr(u, Ty::Int, d)?.call_unary(maybe_try_cast(u, func::CastInt32ToBool)?)), + _ => Ok(gen_expr(u, Ty::Int, d)?.call_unary(func::CastInt32ToBool)), }, - Ty::Str => { - match u.int_in_range(0u8..=3)? { - 0 => { - let cond = gen_expr(u, Ty::Bool, d)?; - let then = gen_expr(u, Ty::Str, d)?; - let els = gen_expr(u, Ty::Str, d)?; - Ok(cond.if_then_else(then, els)) - } - 1 => Ok(gen_expr(u, Ty::Str, d)? - .call_binary(gen_expr(u, Ty::Str, d)?, func::TextConcatBinary)), - 2 => Ok(gen_expr(u, Ty::Int, d)? - .call_unary(maybe_try_cast(u, func::CastInt32ToString)?)), - _ => Ok(gen_expr(u, Ty::Bool, d)? - .call_unary(maybe_try_cast(u, func::CastBoolToString)?)), + Ty::Str => match u.int_in_range(0u8..=3)? { + 0 => { + let cond = gen_expr(u, Ty::Bool, d)?; + let then = gen_expr(u, Ty::Str, d)?; + let els = gen_expr(u, Ty::Str, d)?; + Ok(cond.if_then_else(then, els)) } - } + 1 => Ok(gen_expr(u, Ty::Str, d)? + .call_binary(gen_expr(u, Ty::Str, d)?, func::TextConcatBinary)), + 2 => Ok(gen_expr(u, Ty::Int, d)?.call_unary(func::CastInt32ToString)), + _ => Ok(gen_expr(u, Ty::Bool, d)?.call_unary(func::CastBoolToString)), + }, } } diff --git a/src/expr/src/interpret.rs b/src/expr/src/interpret.rs index 6900762409af3..5bba046327695 100644 --- a/src/expr/src/interpret.rs +++ b/src/expr/src/interpret.rs @@ -1688,42 +1688,10 @@ mod tests { Not(_) => arg.scalar_type == ReprScalarType::Bool, IsNull(_) => true, TryParseMonotonicIso8601Timestamp(_) => arg.scalar_type == ReprScalarType::String, - TryCast(f) => unary_typecheck(&f.inner, arg), _ => false, } } - /// Fallible casts to wrap in `TryCast`, as the planner does for `TRY_CAST`: - /// narrowing integer casts, float to integer, numeric to integer, and the - /// jsonb casts that reject the wrong kind of value. - const TRY_CAST_INNER_FUNCS: &[UnaryFunc] = &[ - UnaryFunc::CastInt64ToInt32(CastInt64ToInt32), - UnaryFunc::CastInt32ToInt16(CastInt32ToInt16), - UnaryFunc::CastInt64ToUint64(CastInt64ToUint64), - UnaryFunc::CastFloat64ToInt64(CastFloat64ToInt64), - UnaryFunc::CastFloat64ToFloat32(CastFloat64ToFloat32), - UnaryFunc::CastNumericToInt32(CastNumericToInt32), - UnaryFunc::CastNumericToMzTimestamp(CastNumericToMzTimestamp), - UnaryFunc::CastJsonbToNumeric(CastJsonbToNumeric(None)), - UnaryFunc::CastJsonbToBool(CastJsonbToBool), - UnaryFunc::CastTimestampToMzTimestamp(CastTimestampToMzTimestamp), - ]; - - /// The declared unary functions, plus `TryCast` around each fallible cast - /// in `TRY_CAST_INNER_FUNCS`, so the interpreter's properties are checked - /// over the null-fallback wrapper as well. - fn interesting_unary_funcs() -> Vec { - let mut funcs = INTERESTING_UNARY_FUNCS.to_vec(); - funcs.extend(TRY_CAST_INNER_FUNCS.iter().map(|cast| { - assert!( - cast.could_error(), - "{cast} never errors, so wrapping it is pointless" - ); - UnaryFunc::try_cast(cast.clone()) - })); - funcs - } - fn interesting_binary_funcs() -> Vec { vec![ AddTimestampInterval.into(), @@ -1997,7 +1965,7 @@ mod tests { column_gen .prop_union(literal_gen) .prop_recursive(4, 64, 8, |self_gen| { - let unary_gen = (select(interesting_unary_funcs()), self_gen.clone()) + let unary_gen = (select(INTERESTING_UNARY_FUNCS), self_gen.clone()) .prop_filter_map("unary func", |(func, (expr_in, type_in))| { if !unary_typecheck(&func, &type_in) { return None; diff --git a/src/expr/src/lib.rs b/src/expr/src/lib.rs index ec3b4298d6bd5..75dd1eceb030a 100644 --- a/src/expr/src/lib.rs +++ b/src/expr/src/lib.rs @@ -46,9 +46,7 @@ pub use relation::{ RowSetFinishing, RowSetFinishingIncremental, WindowFrame, WindowFrameBound, WindowFrameUnits, canonicalize, compare_columns, non_nullable_columns, }; -pub use scalar::func::{ - self, BinaryFunc, CastFailureMode, UnaryFunc, UnmaterializableFunc, VariadicFunc, -}; +pub use scalar::func::{self, BinaryFunc, UnaryFunc, UnmaterializableFunc, VariadicFunc}; pub use scalar::{ Columns, DomainLimit, Eval, EvalError, FilterCharacteristics, MirScalarExpr, OptimizableExpr, ProtoDomainLimit, ProtoEvalError, StableEvalError, StableEvalErrorRef, like_pattern, diff --git a/src/expr/src/scalar/func.rs b/src/expr/src/scalar/func.rs index 9c3ae37aa3212..9c438a4ba0400 100644 --- a/src/expr/src/scalar/func.rs +++ b/src/expr/src/scalar/func.rs @@ -197,7 +197,6 @@ func_name! { TimezoneTimestampTz => "timezone_timestamp_tz", ToCharTimestamp => "to_char_timestamp", ToCharTimestampTz => "to_char_timestamp_tz", - TryCast => "try_cast", variadic::And => "and", variadic::Coalesce => "coalesce", variadic::Greatest => "greatest", diff --git a/src/expr/src/scalar/func/impls.rs b/src/expr/src/scalar/func/impls.rs index 3566471da08ba..ae00788120800 100644 --- a/src/expr/src/scalar/func/impls.rs +++ b/src/expr/src/scalar/func/impls.rs @@ -36,7 +36,6 @@ mod regproc; mod string; mod time; mod timestamp; -mod try_cast; mod uint16; mod uint32; mod uint64; @@ -72,7 +71,6 @@ pub use crate::scalar::func::impls::regproc::*; pub use crate::scalar::func::impls::string::*; pub use crate::scalar::func::impls::time::*; pub use crate::scalar::func::impls::timestamp::*; -pub use crate::scalar::func::impls::try_cast::*; pub use crate::scalar::func::impls::uint16::*; pub use crate::scalar::func::impls::uint32::*; pub use crate::scalar::func::impls::uint64::*; diff --git a/src/expr/src/scalar/func/impls/try_cast.rs b/src/expr/src/scalar/func/impls/try_cast.rs deleted file mode 100644 index f16ea671f517b..0000000000000 --- a/src/expr/src/scalar/func/impls/try_cast.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright Materialize, Inc. and contributors. All rights reserved. -// -// Use of this software is governed by the Business Source License -// included in the LICENSE file. -// -// As of the Change Date specified in that file, in accordance with -// the Business Source License, use of this software will be governed -// by the Apache License, Version 2.0. - -use std::fmt; - -use mz_repr::{Datum, RowArena, SqlColumnType}; -use serde::{Deserialize, Serialize}; - -use crate::scalar::func::{LazyUnaryFunc, UnaryFunc}; -use crate::{Eval, EvalError, MirScalarExpr}; - -/// What a cast does when the value cannot be converted to the target type. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum CastFailureMode { - /// The cast raises an error. - Error, - /// The cast evaluates to `NULL`. Each cast function is wrapped in - /// [`TryCast`], so errors raised while evaluating the cast's argument - /// still propagate. - NullFallback, -} - -/// Wraps a cast function so that its errors become `NULL`. -/// -/// The argument is evaluated first, and its errors propagate. Only errors -/// raised by `inner` itself are swallowed. Because the wrapper is strict in its -/// argument and never errors, it is safe for the optimizer to treat it like -/// any other infallible unary function. -#[derive( - Ord, - PartialOrd, - Clone, - Debug, - Eq, - PartialEq, - Serialize, - Deserialize, - Hash -)] -pub struct TryCast { - pub inner: Box>, -} - -/// Evaluates to the first column of the row it is given. -/// -/// `TryCast::eval` needs to hand an already evaluated datum to `inner`, but -/// `LazyUnaryFunc::eval` takes the argument as an `&'a impl Eval` and returns -/// a `Datum<'a>`, so a value constructed inside `eval` cannot serve as the -/// argument. Instead the datum becomes a one-column row and this `'static` -/// reader stands in for the argument, mirroring how the element casts of -/// `CastList1ToList2` evaluate against a one-column row. -struct FirstColumn; - -static FIRST_COLUMN: FirstColumn = FirstColumn; - -impl Eval for FirstColumn { - fn eval<'a>( - &'a self, - datums: &[Datum<'a>], - _temp_storage: &'a RowArena, - ) -> Result, EvalError> { - Ok(datums[0]) - } - - fn could_error(&self) -> bool { - false - } -} - -impl LazyUnaryFunc for TryCast { - fn eval<'a>( - &'a self, - datums: &[Datum<'a>], - temp_storage: &'a RowArena, - a: &'a impl Eval, - ) -> Result, EvalError> { - let a = a.eval(datums, temp_storage)?; - match self.inner.eval(&[a], temp_storage, &FIRST_COLUMN) { - Ok(datum) => Ok(datum), - // An internal error means the planner handed `inner` a datum of - // the wrong kind. No cast reports it for bad user data, so it is - // a planner bug, and swallowing it would hide that from CI. Soft - // assertions are off in production, where the row becomes NULL - // like any other failure and nothing is logged. - Err(EvalError::Internal(message)) => { - mz_ore::soft_panic_no_log!( - "TRY_CAST swallowed an internal error from {}: {message}", - self.inner - ); - Ok(Datum::Null) - } - Err(_) => Ok(Datum::Null), - } - } - - fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType { - self.inner.output_sql_type(input_type).nullable(true) - } - - fn propagates_nulls(&self) -> bool { - self.inner.propagates_nulls() - } - - fn introduces_nulls(&self) -> bool { - self.inner.could_error() || self.inner.introduces_nulls() - } - - fn could_error(&self) -> bool { - false - } - - fn preserves_uniqueness(&self) -> bool { - // Every failing input maps to the same NULL. - // - // If there was somehow a _unique_ failing input - // and no other value produce NULL, this could be true. - // It's not clear that situation ever arises. - false - } - - fn inverse(&self) -> Option { - None - } - - fn is_monotone(&self) -> bool { - // The failing inputs, which may sit anywhere in the domain, map to - // NULL, so the wrapper is not monotone even when `inner` is. - // - // If the domain of failures was somehow monotonic with respect to NULL, - // this could be true. - false - } - - fn is_eliminable_cast(&self) -> bool { - false - } -} - -impl UnaryFunc { - /// Wraps `inner` in [`TryCast`] if it could error, so that its errors - /// become NULL. A function that cannot error has nothing to fall back - /// from and is returned as is, keeping its nullability. - pub fn try_cast(inner: UnaryFunc) -> UnaryFunc { - if inner.could_error() { - UnaryFunc::TryCast(TryCast { - inner: Box::new(inner), - }) - } else { - inner - } - } -} - -impl TryCast { - /// Rebuilds this function with any expressions stored in `inner` converted - /// to `E2`. - pub fn try_map_expr<'a, E2: TryFrom<&'a E>>(&'a self) -> Result, E2::Error> { - Ok(TryCast { - inner: Box::new(self.inner.try_map_expr()?), - }) - } - - /// Rebuilds this function with any expressions stored in `inner` converted - /// to `E2`. - pub fn map_expr<'a, E2: From<&'a E>>(&'a self) -> TryCast { - TryCast { - inner: Box::new(self.inner.map_expr()), - } - } -} - -impl fmt::Display for TryCast { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // The brackets attach the guard to the function rather than to its - // argument: `try_cast[f](x)` catches errors from `f`, not from `x`. - write!(f, "try_cast[{}]", self.inner) - } -} - -#[cfg(test)] -mod tests { - use mz_repr::{Datum, ReprScalarType, RowArena}; - - use crate::scalar::func::{CastInt64ToInt32, CastStringToInt32, CastStringToInt64, TryCast}; - use crate::{Eval, EvalError, MirScalarExpr, UnaryFunc}; - - fn try_cast(inner: impl Into) -> UnaryFunc { - TryCast { - inner: Box::new(inner.into()), - } - .into() - } - - fn eval(expr: &MirScalarExpr) -> Result, EvalError> { - // The expressions under test are closed, so an empty row suffices. - let arena = Box::leak(Box::new(RowArena::new())); - expr.eval(&[], arena) - } - - fn text(s: &str) -> MirScalarExpr { - MirScalarExpr::literal_ok(Datum::String(s), ReprScalarType::String) - } - - #[mz_ore::test] - fn cast_error_becomes_null() { - let expr = text("abc").call_unary(try_cast(CastStringToInt32)); - assert_eq!(eval(&expr), Ok(Datum::Null)); - } - - #[mz_ore::test] - fn success_agrees_with_strict_cast() { - let lenient = text("42").call_unary(try_cast(CastStringToInt32)); - let strict = text("42").call_unary(CastStringToInt32); - assert_eq!(eval(&lenient), Ok(Datum::Int32(42))); - assert_eq!(eval(&lenient), eval(&strict)); - } - - #[mz_ore::test] - fn null_argument_stays_null() { - let expr = MirScalarExpr::literal_null(ReprScalarType::String) - .call_unary(try_cast(CastStringToInt32)); - assert_eq!(eval(&expr), Ok(Datum::Null)); - } - - #[mz_ore::test] - fn argument_error_propagates() { - let expr = MirScalarExpr::literal(Err(EvalError::DivisionByZero), ReprScalarType::String) - .call_unary(try_cast(CastStringToInt32)); - assert_eq!(eval(&expr), Err(EvalError::DivisionByZero)); - } - - /// A two-stage chain, text to int8 to int4, wrapped per stage as the - /// planner does. A failure at either stage yields NULL because the later - /// stage propagates the NULL the earlier one produced. - #[mz_ore::test] - fn chain_nulls_at_either_stage() { - let chain = |s: &str| { - text(s) - .call_unary(try_cast(CastStringToInt64)) - .call_unary(try_cast(CastInt64ToInt32)) - }; - assert_eq!(eval(&chain("abc")), Ok(Datum::Null), "fails at stage one"); - assert_eq!( - eval(&chain("3000000000")), - Ok(Datum::Null), - "fails at stage two" - ); - assert_eq!(eval(&chain("5")), Ok(Datum::Int32(5))); - } - - /// A datum of the wrong kind reaches the inner cast only through a - /// planner bug, which the wrapper reports under soft assertions and hides - /// as NULL otherwise. - #[mz_ore::test] - fn internal_error_is_a_soft_panic() { - let expr = MirScalarExpr::literal_ok(Datum::Int64(7), ReprScalarType::Int64) - .call_unary(try_cast(CastStringToInt32)); - let outcome = mz_ore::panic::catch_unwind(|| eval(&expr)); - if mz_ore::assert::soft_assertions_enabled() { - assert!(outcome.is_err(), "soft assertion should have fired"); - } else { - assert_eq!(outcome.expect("no panic"), Ok(Datum::Null)); - } - } - - #[mz_ore::test] - fn metadata() { - let func = try_cast(CastStringToInt32); - assert!(!func.could_error()); - assert!(func.introduces_nulls()); - assert!(func.propagates_nulls()); - assert!(!func.is_monotone()); - assert!(!func.preserves_uniqueness()); - assert_eq!(func.inverse(), None); - let input = mz_repr::SqlScalarType::String.nullable(false); - assert!(func.output_sql_type(input).nullable); - assert_eq!(func.to_string(), "try_cast[text_to_integer]"); - } -} diff --git a/src/expr/src/scalar/func/registry.rs b/src/expr/src/scalar/func/registry.rs index 9b1a02ea21aba..09a46269a711a 100644 --- a/src/expr/src/scalar/func/registry.rs +++ b/src/expr/src/scalar/func/registry.rs @@ -579,53 +579,6 @@ fn unary_samples() -> Vec> { record_type(), ), unary(RecordGet(1), record_type()), - // The null-fallback wrapper around a cast. Its properties derive from - // the wrapped function, so the samples cover the branches that differ: - // a fallible parse, a narrowing cast with an inverse (which the wrapper - // must not inherit), a nullable output the wrapper only forces nullable, - // a container cast whose element cast lives inside the wrapper, and a - // wrapper around a function that cannot error, which the planner never - // emits but the type allows. - unary( - TryCast { - inner: Box::new(CastStringToInt32.into()), - }, - SqlScalarType::String, - ), - unary( - TryCast { - inner: Box::new(CastInt64ToInt32.into()), - }, - SqlScalarType::Int64, - ) - .labeled("narrowing"), - unary( - TryCast { - inner: Box::new(CastJsonbToNumeric(None).into()), - }, - SqlScalarType::Jsonb, - ) - .labeled("jsonb"), - unary( - TryCast { - inner: Box::new( - CastList1ToList2 { - return_ty: list_type(SqlScalarType::Int32), - cast_expr: column(0), - } - .into(), - ), - }, - list_type(SqlScalarType::Int64), - ) - .labeled("list"), - unary( - TryCast { - inner: Box::new(CastInt32ToInt64.into()), - }, - SqlScalarType::Int32, - ) - .labeled("infallible"), unary( CastStringToArray { return_ty: array_type(SqlScalarType::Int32), diff --git a/src/expr/src/scalar/func/unary.rs b/src/expr/src/scalar/func/unary.rs index 60c7eb268da11..c53f9d8ec60ef 100644 --- a/src/expr/src/scalar/func/unary.rs +++ b/src/expr/src/scalar/func/unary.rs @@ -422,7 +422,6 @@ derive_unary!( CastMapToString, CastInt2VectorToString, CastRangeToString, - TryCast(E), CeilFloat32, CeilFloat64, CeilNumeric, @@ -573,14 +572,11 @@ mod test { #[mz_ore::test] fn test_could_error() { - let funcs: [UnaryFunc; 5] = [ + let funcs: [UnaryFunc; 4] = [ UnaryFunc::IsNull(IsNull), UnaryFunc::CastVarCharToString(CastVarCharToString), UnaryFunc::Not(Not), UnaryFunc::IsLikeMatch(IsLikeMatch(like_pattern::compile("%hi%", false).unwrap())), - UnaryFunc::TryCast(TryCast { - inner: Box::new(UnaryFunc::CastStringToInt32(CastStringToInt32)), - }), ]; for func in funcs { assert!(!func.could_error()) diff --git a/src/expr/tests/spec/interpret.spec b/src/expr/tests/spec/interpret.spec index defdbd0f14ae9..8f0d632d5c4af 100644 --- a/src/expr/tests/spec/interpret.spec +++ b/src/expr/tests/spec/interpret.spec @@ -159,22 +159,3 @@ expr (#0 = #1) test (true, false, null::boolean, 13) ---- may contain: [false null] - -# TryCast never errors and may yield NULL for any input, so the output spec -# admits NULL even though the input values all convert. -interpret -types (text?) -values ("12", "13") -expr try_cast[cast_string_to_int32](#0) -test (12, 13, 14, null::integer) ----- -may contain: [12 13 14 null] - -# The argument's error still propagates through the wrapper. -interpret -types (bigint?) -values (1::bigint, 2::bigint) -expr try_cast[cast_int64_to_int32](div_int64(#0, 0)) -test (1, null::integer) ----- -may contain: [1 null ] diff --git a/src/expr/tests/spec/reduce.spec b/src/expr/tests/spec/reduce.spec index 08c857d4c0d4d..08d20b0250741 100644 --- a/src/expr/tests/spec/reduce.spec +++ b/src/expr/tests/spec/reduce.spec @@ -703,44 +703,3 @@ types (integer?, integer?, integer?) (#0 = #1) ((#0 + 1) < 2147483647) ((#0 + 1) < #2) - -## TryCast wraps a cast function so that its errors become NULL. Errors -## from the argument still propagate. - -reduce -types (text?) -try_cast[cast_string_to_int32]("42") ----- -42 - -reduce -types (text?) -try_cast[cast_string_to_int32]("abc") ----- -null - -reduce -types (text?) -try_cast[cast_string_to_int32](null::text) ----- -null - -reduce -types (bigint?) -try_cast[cast_int64_to_int32](div_int64(1, 0)) ----- -error("division by zero") - -# The wrapper introduces NULLs, so IS NULL must not be pushed through it the -# way it is for a plain cast. -reduce -types (text?) -((try_cast[cast_string_to_int32](#0)) IS NULL) ----- -(try_cast[text_to_integer](#0)) IS NULL - -reduce -types (text?) -((cast_string_to_int32(#0)) IS NULL) ----- -(#0) IS NULL diff --git a/src/repr/src/scalar.rs b/src/repr/src/scalar.rs index 284d60e525426..8d095a632cd06 100644 --- a/src/repr/src/scalar.rs +++ b/src/repr/src/scalar.rs @@ -3592,28 +3592,6 @@ impl SqlScalarType { } } - /// Reports whether `predicate` holds for this type or for any type nested - /// within it: the element type of a list, array, map, or range, or a field - /// type of a record, at any depth. - pub fn contains(&self, predicate: &impl Fn(&SqlScalarType) -> bool) -> bool { - if predicate(self) { - return true; - } - match self { - SqlScalarType::List { element_type, .. } - | SqlScalarType::Array(element_type) - | SqlScalarType::Map { - value_type: element_type, - .. - } - | SqlScalarType::Range { element_type } => element_type.contains(predicate), - SqlScalarType::Record { fields, .. } => fields - .iter() - .any(|(_, field)| field.scalar_type.contains(predicate)), - _ => false, - } - } - /// Returns the [`SqlScalarType`] of elements in a [`SqlScalarType::Array`], /// [`SqlScalarType::Int2Vector`], or [`SqlScalarType::List`]. /// @@ -6226,31 +6204,4 @@ mod tests { } } } - - #[mz_ore::test] - fn scalar_type_contains_walks_nested_types() { - let is_legacy_char = |ty: &SqlScalarType| *ty == SqlScalarType::PgLegacyChar; - let nested = SqlScalarType::Record { - fields: [ - ("a".into(), SqlScalarType::Int32.nullable(true)), - ( - "b".into(), - SqlScalarType::List { - element_type: Box::new(SqlScalarType::Map { - value_type: Box::new(SqlScalarType::PgLegacyChar), - custom_id: None, - }), - custom_id: None, - } - .nullable(true), - ), - ] - .into(), - custom_id: None, - }; - assert!(nested.contains(&is_legacy_char)); - assert!(SqlScalarType::PgLegacyChar.contains(&is_legacy_char)); - assert!(!SqlScalarType::Array(Box::new(SqlScalarType::String)).contains(&is_legacy_char)); - assert!(!SqlScalarType::Int32.contains(&is_legacy_char)); - } } diff --git a/src/sql-lexer/build.rs b/src/sql-lexer/build.rs index 5c7d90b7eeb7f..223fe3ff64e48 100644 --- a/src/sql-lexer/build.rs +++ b/src/sql-lexer/build.rs @@ -44,9 +44,6 @@ fn main() -> Result<()> { let mut buf = CodegenBuf::new(); - // Multi-word keywords like `TRY_CAST` keep their underscore in the - // variant name so that `as_str` can uppercase it verbatim. - buf.writeln("#[allow(non_camel_case_types)]"); buf.writeln("#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]"); buf.write_block("pub enum Keyword", |buf| { for kw in &keywords { diff --git a/src/sql-lexer/src/keywords.txt b/src/sql-lexer/src/keywords.txt index 1e3d34cc8b060..18763dd47871e 100644 --- a/src/sql-lexer/src/keywords.txt +++ b/src/sql-lexer/src/keywords.txt @@ -509,7 +509,6 @@ Transactional Transform Trim True -Try_Cast Tunnel Type Types diff --git a/src/sql-parser/src/ast/defs/expr.rs b/src/sql-parser/src/ast/defs/expr.rs index 4a8c682a96b48..ecdb3b7c46fa7 100644 --- a/src/sql-parser/src/ast/defs/expr.rs +++ b/src/sql-parser/src/ast/defs/expr.rs @@ -106,12 +106,10 @@ pub enum Expr { expr1: Box>, expr2: Option>>, }, - /// CAST an expression to a different data type, e.g. `CAST(foo AS VARCHAR(123))` - /// or `TRY_CAST(foo AS int4)`. + /// CAST an expression to a different data type e.g. `CAST(foo AS VARCHAR(123))` Cast { expr: Box>, data_type: T::DataType, - failure_mode: CastFailureMode, }, /// `expr COLLATE collation` Collate { @@ -367,11 +365,7 @@ impl AstDisplay for Expr { } } } - Expr::Cast { - expr, - data_type, - failure_mode: CastFailureMode::Error, - } => { + Expr::Cast { expr, data_type } => { // `::` binds very tightly, so a non-self-delimiting operand must // be parenthesized or the cast re-associates into its spine. // `CAST(-0 AS int4)` (i.e. `Cast(- 0)`) would otherwise print as @@ -389,17 +383,6 @@ impl AstDisplay for Expr { f.write_str("::"); f.write_node(data_type); } - Expr::Cast { - expr, - data_type, - failure_mode: CastFailureMode::NullFallback, - } => { - f.write_str("TRY_CAST("); - f.write_node(&expr); - f.write_str(" AS "); - f.write_node(data_type); - f.write_str(")"); - } Expr::Collate { expr, collation } => { // `COLLATE` binds very tightly (`PostfixCollateAt`), so a // low-precedence operand must be parenthesized or the collation @@ -869,11 +852,7 @@ fn prints_self_delimiting(expr: &Expr) -> bool { | Expr::Map(_) | Expr::MapSubquery(_) | Expr::Case { .. } - | Expr::Row { .. } - | Expr::Cast { - failure_mode: CastFailureMode::NullFallback, - .. - } => true, + | Expr::Row { .. } => true, // The postfix `::` / `COLLATE` / `[…]` forms print as ``, // so they are safe only when their inner operand is. Expr::Cast { expr, .. } | Expr::Collate { expr, .. } | Expr::Subscript { expr, .. } => { @@ -913,12 +892,7 @@ fn bare_prefix_operand_needs_parens(operand: &Expr) -> bool { let mut saw_postfix = false; loop { match e { - Expr::Cast { - expr, - failure_mode: CastFailureMode::Error, - .. - } - | Expr::Subscript { expr, .. } => { + Expr::Cast { expr, .. } | Expr::Subscript { expr, .. } => { saw_postfix = true; e = expr.as_ref(); } @@ -974,12 +948,8 @@ fn write_subscript_receiver(f: &mut AstFormatter, | Expr::MapSubquery(_) | Expr::FieldAccess { .. } | Expr::WildcardAccess(_) - | Expr::Collate { .. } - | Expr::Cast { - failure_mode: CastFailureMode::NullFallback, - .. - } => false, - // `::` `Cast`: the type parser swallows a following `[…]` as an array suffix + | Expr::Collate { .. } => false, + // `Cast`: the type parser swallows a following `[…]` as an array suffix // (`a::int4[1]` is `a` cast to `int4[]`, not a subscript of `a::int4`). // `Subscript`: consecutive `[…]` flatten into one node (`a[1][2]` is a // single subscript), so a nested subscript receiver must be parenthesized @@ -1080,7 +1050,6 @@ impl Expr { Expr::Cast { expr: Box::new(self), data_type, - failure_mode: CastFailureMode::Error, } } @@ -1146,16 +1115,6 @@ impl Op { } } -/// What a cast does when the value cannot be converted to the target type. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum CastFailureMode { - /// The cast raises an error, as `CAST(expr AS type)` and `expr::type` do. - Error, - /// The cast evaluates to `NULL`, as `TRY_CAST(expr AS type)` does. Errors - /// raised while evaluating `expr` itself still propagate. - NullFallback, -} - #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum HomogenizingFunction { Coalesce, diff --git a/src/sql-parser/src/lib.rs b/src/sql-parser/src/lib.rs index 7e50da55f7b4b..9181737211a5a 100644 --- a/src/sql-parser/src/lib.rs +++ b/src/sql-parser/src/lib.rs @@ -43,7 +43,7 @@ mod ident; #[cfg(feature = "test")] pub fn datadriven_testcase(tc: &datadriven::TestCase) -> String { use crate::ast::display::AstDisplay; - use crate::ast::{CastFailureMode, Expr, Statement}; + use crate::ast::{Expr, Statement}; use datadriven::TestCase; use mz_ore::collections::CollectionExt; use mz_ore::fmt::FormatBuffer; @@ -125,20 +125,13 @@ pub fn datadriven_testcase(tc: &datadriven::TestCase) -> String { match parser::parse_expr(&printed) { Ok(parsed) => { // TODO: We always coerce the double colon operator into a Cast expr instead - // of keeping it as an Op (see parse_pg_cast). An erroring Expr::Cast - // always prints itself as double colon. We're thus unable to perfectly - // roundtrip `CAST(..)`. We could fix this by keeping "::" as a binary - // operator and teaching func.rs how to handle it, similar to how that - // file handles "~~" (without the parser converting that operator - // directly into an Expr::Like). `TRY_CAST(..)` has no operator form - // and so does roundtrip. - if !matches!( - parsed, - Expr::Cast { - failure_mode: CastFailureMode::Error, - .. - } - ) { + // of keeping it as an Op (see parse_pg_cast). Expr::Cast always prints + // itself as double colon. We're thus unable to perfectly roundtrip + // `CAST(..)`. We could fix this by keeping "::" as a binary operator and + // teaching func.rs how to handle it, similar to how that file handles "~~" + // (without the parser converting that operator directly into an + // Expr::Like). + if !matches!(parsed, Expr::Cast { .. }) { if parsed != s { panic!( "reparse comparison failed: {input} != {s}\n{:?}\n!=\n{:?}\n{printed}\n", diff --git a/src/sql-parser/src/parser.rs b/src/sql-parser/src/parser.rs index 5df0bf2c091dd..e0f679ba16260 100644 --- a/src/sql-parser/src/parser.rs +++ b/src/sql-parser/src/parser.rs @@ -656,7 +656,6 @@ impl<'a> Parser<'a> { Ok(Expr::Cast { expr: Box::new(Expr::Value(Value::String(parser.parse_literal_string()?))), data_type, - failure_mode: CastFailureMode::Error, }) } })); @@ -680,10 +679,7 @@ impl<'a> Parser<'a> { } (Token::Keyword(MAP), Some(Token::LBracket) | Some(Token::LParen)) => self.parse_map(), (Token::Keyword(CASE), _) => self.parse_case_expr(), - (Token::Keyword(CAST), _) => self.parse_cast_expr(CastFailureMode::Error), - (Token::Keyword(TRY_CAST), Some(Token::LParen)) => { - self.parse_cast_expr(CastFailureMode::NullFallback) - } + (Token::Keyword(CAST), _) => self.parse_cast_expr(), (Token::Keyword(COALESCE), Some(Token::LParen)) => { self.parse_homogenizing_function(HomogenizingFunction::Coalesce) } @@ -1008,9 +1004,8 @@ impl<'a> Parser<'a> { }) } - /// Parses the argument list of `CAST(...)` or `TRY_CAST(...)`, the keyword - /// having already been consumed. - fn parse_cast_expr(&mut self, failure_mode: CastFailureMode) -> Result, ParserError> { + /// Parse a SQL CAST function e.g. `CAST(expr AS FLOAT)` + fn parse_cast_expr(&mut self) -> Result, ParserError> { // Whether `expr` is safe to print directly to the left of a Postgres-style // `::` cast without wrapping it in parentheses. `Expr::Cast` / // `Expr::Collate` print as the postfix forms `::` / @@ -1028,11 +1023,7 @@ impl<'a> Parser<'a> { | Expr::HomogenizingFunction { .. } | Expr::NullIf { .. } | Expr::Subquery { .. } - | Expr::Parameter(..) - | Expr::Cast { - failure_mode: CastFailureMode::NullFallback, - .. - } => true, + | Expr::Parameter(..) => true, Expr::Cast { expr, .. } | Expr::Collate { expr, .. } => safe_before_pg_cast(expr), _ => false, } @@ -1047,23 +1038,24 @@ impl<'a> Parser<'a> { // CAST( OP AS ) // to // OP :: - // (because we print an erroring Expr::Cast always as a Postgres-style - // cast, i.e. `::`) which could incorrectly change the meaning of the - // expression as the `::` binds tightly. To be safe, we wrap the inner + // (because we print Expr::Cast always as a Postgres-style cast, i.e. `::`) + // which could incorrectly change the meaning of the expression + // as the `::` binds tightly. To be safe, we wrap the inner // expression in parentheses // ( OP ):: // unless the inner expression is of a kind that we know is - // safe to follow with a `::` without wrapping. `TRY_CAST(...)` prints - // in its function-like form, which delimits its operand itself. - let expr = match failure_mode { - CastFailureMode::Error if !safe_before_pg_cast(&expr) => Expr::Nested(Box::new(expr)), - CastFailureMode::Error | CastFailureMode::NullFallback => expr, - }; - Ok(Expr::Cast { - expr: Box::new(expr), - data_type, - failure_mode, - }) + // safe to follow with a `::` without wrapping. + if safe_before_pg_cast(&expr) { + Ok(Expr::Cast { + expr: Box::new(expr), + data_type, + }) + } else { + Ok(Expr::Cast { + expr: Box::new(Expr::Nested(Box::new(expr))), + data_type, + }) + } } /// Parse a SQL EXISTS expression e.g. `WHERE EXISTS(SELECT ...)`. @@ -1512,15 +1504,9 @@ impl<'a> Parser<'a> { // If the expression that is being cast can end with a type name, then let's parenthesize // it. Otherwise, the `[...]` would melt into the type name (making it an array type). - // Specifically, the only expressions whose printing can end with a type name are `::` - // casts, so check for that. - if matches!( - expr, - Expr::Cast { - failure_mode: CastFailureMode::Error, - .. - } - ) { + // Specifically, the only expressions whose printing can end with a type name are casts, so + // check for that. + if matches!(expr, Expr::Cast { .. }) { Ok(Expr::Subscript { expr: Box::new(Expr::Nested(Box::new(expr))), positions, @@ -1724,7 +1710,6 @@ impl<'a> Parser<'a> { Ok(Expr::Cast { expr: Box::new(expr), data_type: self.parse_data_type()?, - failure_mode: CastFailureMode::Error, }) } diff --git a/src/sql-parser/tests/testdata/literal b/src/sql-parser/tests/testdata/literal index edbef52cf6773..6859333e7f41e 100644 --- a/src/sql-parser/tests/testdata/literal +++ b/src/sql-parser/tests/testdata/literal @@ -113,122 +113,122 @@ Value(HexString("deadBEEF")) parse-scalar DATE '1999-01-01' ---- -Cast { expr: Value(String("1999-01-01")), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01")), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] } } parse-scalar DATE 'invalid date' ---- -Cast { expr: Value(String("invalid date")), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid date")), data_type: Other { name: Name(UnresolvedItemName([Ident("date")])), typ_mod: [] } } parse-scalar TIME '01:23:34' ---- -Cast { expr: Value(String("01:23:34")), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("01:23:34")), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] } } parse-scalar TIME 'invalid time' ---- -Cast { expr: Value(String("invalid time")), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid time")), data_type: Other { name: Name(UnresolvedItemName([Ident("time")])), typ_mod: [] } } parse-scalar TIMESTAMP '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] } } parse-scalar TIMESTAMPTZ '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] } } parse-scalar TIMESTAMP WITH TIME ZONE '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] } } parse-scalar TIMESTAMP WITHOUT TIME ZONE '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] } } parse-scalar TIMESTAMP 'invalid timestamptx' ---- -Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] } } parse-scalar TIMESTAMPTZ 'invalid timestamptx' ---- -Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] } } parse-scalar TIMESTAMP WITH TIME ZONE 'invalid timestamptx' ---- -Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [] } } parse-scalar TIMESTAMP WITHOUT TIME ZONE 'invalid timestamptx' ---- -Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] }, failure_mode: Error } +Cast { expr: Value(String("invalid timestamptx")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [] } } parse-scalar TIMESTAMP(-1) '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [-1] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [-1] } } parse-scalar TIMESTAMPTZ(-1) '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [-1] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [-1] } } parse-scalar TIMESTAMP(-1) WITH TIME ZONE '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [-1] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [-1] } } parse-scalar TIMESTAMP(-1) WITHOUT TIME ZONE '1999-01-01 01:23:34.555' ---- -Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [-1] }, failure_mode: Error } +Cast { expr: Value(String("1999-01-01 01:23:34.555")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [-1] } } parse-scalar TIMESTAMP(3) '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [3] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [3] } } parse-scalar TIMESTAMPTZ(3) '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [3] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [3] } } parse-scalar TIMESTAMP(3) WITH TIME ZONE '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [3] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [3] } } parse-scalar TIMESTAMP(3) WITHOUT TIME ZONE '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [3] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [3] } } parse-scalar TIMESTAMP(7) '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [7] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [7] } } parse-scalar TIMESTAMPTZ(7) '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [7] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [7] } } parse-scalar TIMESTAMP(7) WITH TIME ZONE '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [7] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamptz")])), typ_mod: [7] } } parse-scalar TIMESTAMP(7) WITHOUT TIME ZONE '1970-01-01T00:00:00.666666666' ---- -Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [7] }, failure_mode: Error } +Cast { expr: Value(String("1970-01-01T00:00:00.666666666")), data_type: Other { name: Name(UnresolvedItemName([Ident("timestamp")])), typ_mod: [7] } } parse-scalar TIMESTAMP(9223372036854775808) '1970-01-01T00:00:00.666666666' diff --git a/src/sql-parser/tests/testdata/scalar b/src/sql-parser/tests/testdata/scalar index 752a2c22788e4..73cb36de081f6 100644 --- a/src/sql-parser/tests/testdata/scalar +++ b/src/sql-parser/tests/testdata/scalar @@ -185,7 +185,7 @@ Op { op: Op { namespace: None, op: "!~*" }, expr1: Identifier([Ident("a")]), exp parse-scalar a !x 'foo' ---- -Op { op: Op { namespace: None, op: "!" }, expr1: Identifier([Ident("a")]), expr2: Some(Cast { expr: Value(String("foo")), data_type: Other { name: Name(UnresolvedItemName([Ident("x")])), typ_mod: [] }, failure_mode: Error }) } +Op { op: Op { namespace: None, op: "!" }, expr1: Identifier([Ident("a")]), expr2: Some(Cast { expr: Value(String("foo")), data_type: Other { name: Name(UnresolvedItemName([Ident("x")])), typ_mod: [] } }) } parse-scalar a !x @@ -209,66 +209,6 @@ CAST(id AS BIGINT) ---- id::int8 -# TRY_CAST has no operator spelling, so it prints in its function-like form and -# never needs the parenthesization that `::` casts need around their operand. -parse-scalar roundtrip -TRY_CAST(id AS BIGINT) ----- -TRY_CAST(id AS int8) - -parse-scalar roundtrip -try_cast(a + b AS int4) ----- -TRY_CAST(a + b AS int4) - -parse-scalar roundtrip --TRY_CAST(2 AS int4) ----- -- TRY_CAST(2 AS int4) - -parse-scalar roundtrip -TRY_CAST(x AS int4)::text ----- -TRY_CAST(x AS int4)::text - -parse-scalar roundtrip -TRY_CAST(x::int2 AS int4) ----- -TRY_CAST(x::int2 AS int4) - -parse-scalar roundtrip -TRY_CAST(CAST(x AS smallint) AS int) ----- -TRY_CAST(x::int2 AS int4) - -parse-scalar roundtrip -TRY_CAST(x AS int4 list)[1] ----- -TRY_CAST(x AS int4 list)[1] - -parse-scalar roundtrip -CAST(TRY_CAST(x AS int4 list) AS text) ----- -TRY_CAST(x AS int4 list)::text - -parse-scalar -TRY_CAST(id AS int4) ----- -Cast { expr: Identifier([Ident("id")]), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: NullFallback } - -parse-scalar -TRY_CAST(id, int4) ----- -error: Expected AS, found comma -TRY_CAST(id, int4) - ^ - -# TRY_CAST is not reserved, so it remains usable as a bare identifier. -parse-scalar roundtrip -try_cast + 1 ----- -try_cast + 1 - parse-scalar roundtrip id::double precision ---- @@ -901,17 +841,17 @@ address[1:list_length(address) - 1] parse-scalar ARRAY[]::int[] ---- -Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }), failure_mode: Error } +Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }) } parse-scalar ARRAY[]::int[][][][] ---- -Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }), failure_mode: Error } +Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }) } parse-scalar ARRAY[]::int[2][2] ---- -Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }), failure_mode: Error } +Cast { expr: Array([]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }) } parse-scalar roundtrip ARRAY[1, 2, 3][-1] diff --git a/src/sql-parser/tests/testdata/select b/src/sql-parser/tests/testdata/select index 14ef9fe5450c6..57a834a31760d 100644 --- a/src/sql-parser/tests/testdata/select +++ b/src/sql-parser/tests/testdata/select @@ -1617,7 +1617,7 @@ SELECT foo::uuid list[2] FROM fake_table; ---- SELECT foo::uuid list[] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Identifier([Ident("foo")]), data_type: Array(List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] })), failure_mode: Error }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Identifier([Ident("foo")]), data_type: Array(List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] })) }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # Subscripting after a cast. The danger here is that if the cast is to list or array, then the subscript could melt into # the type as if it were creating an array type if the parser and/or AST printer is not careful. @@ -1627,26 +1627,14 @@ SELECT CAST(my_json AS uuid list)[my_index] FROM fake_table; ---- SELECT (my_json::uuid list)[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }), failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) - -parse-statement -SELECT TRY_CAST(my_json AS uuid[])[my_index] FROM fake_table; ----- -SELECT TRY_CAST(my_json AS uuid[])[my_index] FROM fake_table -=> -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Cast { expr: Identifier([Ident("my_json")]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }), failure_mode: NullFallback }, positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) - -parse-statement roundtrip -SELECT try_cast, try_cast.x FROM try_cast ----- -SELECT try_cast, try_cast.x FROM try_cast +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }) }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement SELECT CAST(my_json AS uuid[])[my_index] FROM fake_table; ---- SELECT (my_json::uuid[])[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }), failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }) }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # This would not plan, because we are subscripting into a uuid, but it's still nice if it roundtrips through the parser. parse-statement @@ -1654,7 +1642,7 @@ SELECT CAST(my_json AS uuid)[my_index] FROM fake_table; ---- SELECT (my_json::uuid)[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }, failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] } }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # Same as above, but with Postgres-style casts. @@ -1663,14 +1651,14 @@ SELECT (my_json::uuid list)[my_index] FROM fake_table; ---- SELECT (my_json::uuid list)[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }), failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: List(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }) }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement SELECT (my_json::uuid[])[my_index] FROM fake_table; ---- SELECT (my_json::uuid[])[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }), failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Array(Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }) }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # This would not plan, because we are subscripting into a uuid, but it's still nice if it roundtrips through the parser. parse-statement @@ -1678,7 +1666,7 @@ SELECT (my_json::uuid)[my_index] FROM fake_table; ---- SELECT (my_json::uuid)[my_index] FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] }, failure_mode: Error }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Subscript { expr: Nested(Cast { expr: Identifier([Ident("my_json")]), data_type: Other { name: Name(UnresolvedItemName([Ident("uuid")])), typ_mod: [] } }), positions: [SubscriptPosition { start: Some(Identifier([Ident("my_index")])), end: None, explicit_slice: false }] }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # Casts are always printed as Postgres-style casts, that is, with `::`. When going from `CAST` to `::`, we have to avoid # the daner of `::` binding to just part of the expression that was originally being cast. E.g. rewriting @@ -1691,7 +1679,7 @@ SELECT CAST(a + b AS integer) FROM fake_table; ---- SELECT (a + b)::int4 FROM fake_table => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Identifier([Ident("a")]), expr2: Some(Identifier([Ident("b")])) }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Identifier([Ident("a")]), expr2: Some(Identifier([Ident("b")])) }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("fake_table")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # We should avoid inserting superfluous parenthesis, or it won't roundtrip after printing the cast in the Postgres # style, because some parser code paths eat superfluous parenthesis, including the code path that handles @@ -1701,7 +1689,7 @@ SELECT round(1.5678, CAST((SELECT n FROM nums) AS integer)); ---- SELECT round(1.5678, (SELECT n FROM nums)::int4) => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Function(Function { name: Name(UnresolvedItemName([Ident("round")])), args: Args { args: [Value(Number("1.5678")), Cast { expr: Subquery(Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Identifier([Ident("n")]), alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("nums")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }], order_by: [] }, filter: None, over: None, distinct: false }), alias: None }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Function(Function { name: Name(UnresolvedItemName([Ident("round")])), args: Args { args: [Value(Number("1.5678")), Cast { expr: Subquery(Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Identifier([Ident("n")]), alias: None }], from: [TableWithJoins { relation: Table { name: Name(UnresolvedItemName([Ident("nums")])), alias: None }, joins: [] }], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }], order_by: [] }, filter: None, over: None, distinct: false }), alias: None }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # Prepared statement parameter handling in casts. (Note: some extra wrapping parens here are currently not removed.) parse-statement @@ -1709,49 +1697,49 @@ select $2::int as col ---- SELECT $2::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select CAST($2 AS int) as col ---- SELECT $2::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select $1 + ($2)::int as col ---- SELECT $1 + ($2)::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Cast { expr: Nested(Parameter(2)), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }) }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Cast { expr: Nested(Parameter(2)), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }) }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select $1 + $2::int as col ---- SELECT $1 + $2::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }) }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Cast { expr: Parameter(2), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }) }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select ($1 + $2)::int as col ---- SELECT ($1 + $2)::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Parameter(2)) }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Parameter(2)) }), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select ((($1)))::int as col ---- SELECT ((($1)))::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Nested(Nested(Parameter(1)))), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Nested(Nested(Parameter(1)))), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) parse-statement select ((($1 + $2)))::int as col ---- SELECT ((($1 + $2)))::int4 AS col => -Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Nested(Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Parameter(2)) }))), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] }, failure_mode: Error }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) +Select(SelectStatement { query: Query { ctes: Simple([]), body: Select(Select { distinct: None, projection: [Expr { expr: Cast { expr: Nested(Nested(Nested(Op { op: Op { namespace: None, op: "+" }, expr1: Parameter(1), expr2: Some(Parameter(2)) }))), data_type: Other { name: Name(UnresolvedItemName([Ident("int4")])), typ_mod: [] } }, alias: Some(Ident("col")) }], from: [], selection: None, group_by: [], having: None, qualify: None, options: [] }), order_by: [], limit: None, offset: None }, as_of: None }) # Map subqueries parse-statement diff --git a/src/sql-pretty/src/doc.rs b/src/sql-pretty/src/doc.rs index ecc57a4b1b65a..a0f6b9553382a 100644 --- a/src/sql-pretty/src/doc.rs +++ b/src/sql-pretty/src/doc.rs @@ -1254,26 +1254,13 @@ impl Pretty { let doc = intersperse_line_nest(docs); bracket_doc(RcDoc::text("CASE"), doc, RcDoc::text("END"), RcDoc::line()) } - Expr::Cast { - expr, - data_type, - failure_mode: CastFailureMode::Error, - } => { + Expr::Cast { expr, data_type } => { let doc = self.doc_expr(expr); RcDoc::concat([ doc, RcDoc::text(format!("::{}", data_type.to_ast_string_simple())), ]) } - Expr::Cast { - expr, - data_type, - failure_mode: CastFailureMode::NullFallback, - } => bracket( - "TRY_CAST(", - self.doc_expr(expr), - format!(" AS {})", data_type.to_ast_string_simple()), - ), Expr::Nested(ast) => bracket("(", self.doc_expr(ast), ")"), Expr::Function(fun) => self.doc_function(fun), Expr::Subquery(ast) => bracket("(", self.doc_query(ast), ")"), diff --git a/src/sql/src/plan.rs b/src/sql/src/plan.rs index c23aace7f9814..66fcdd93c367e 100644 --- a/src/sql/src/plan.rs +++ b/src/sql/src/plan.rs @@ -99,9 +99,6 @@ pub(crate) mod statement; pub(crate) mod transform_ast; pub(crate) mod transform_hir; pub(crate) mod typeconv; -pub use typeconv::{ - CastContext, plan_hypothetical_cast_with_failure_mode, valid_cast_base_type_pairs, -}; pub(crate) mod with_options; use crate::plan; diff --git a/src/sql/src/plan/error.rs b/src/sql/src/plan/error.rs index 07867033a1a15..ec848436bf9dc 100644 --- a/src/sql/src/plan/error.rs +++ b/src/sql/src/plan/error.rs @@ -153,11 +153,6 @@ pub enum PlanError { UnsupportedRangeElementType { element_type_name: String, }, - /// The cast exists, but `TRY_CAST` cannot turn its failures into NULL. - TryCastUnsupported { - from: String, - to: String, - }, InvalidTable { name: String, }, @@ -665,12 +660,6 @@ impl fmt::Display for PlanError { Self::InvalidTemporarySchema => { write!(f, "cannot create temporary item in non-temporary schema") } - Self::TryCastUnsupported { from, to } => { - write!( - f, - "TRY_CAST cannot fall back to NULL for the cast from {from} to {to}; use CAST instead" - ) - } Self::InvalidCast { name, ccx, from, to } =>{ write!( f, diff --git a/src/sql/src/plan/query.rs b/src/sql/src/plan/query.rs index d2a296a0983eb..07c8749d41ba6 100644 --- a/src/sql/src/plan/query.rs +++ b/src/sql/src/plan/query.rs @@ -75,7 +75,7 @@ use mz_sql_parser::ast::display::AstDisplay; use mz_sql_parser::ast::visit::Visit; use mz_sql_parser::ast::visit_mut::{self, VisitMut}; use mz_sql_parser::ast::{ - AsOf, Assignment, AstInfo, CastFailureMode, CreateWebhookSourceBody, CreateWebhookSourceCheck, + AsOf, Assignment, AstInfo, CreateWebhookSourceBody, CreateWebhookSourceCheck, CreateWebhookSourceHeader, CreateWebhookSourceSecret, CteBlock, DeleteStatement, Distinct, Expr, Function, FunctionArgs, HomogenizingFunction, Ident, InsertSource, IsExprConstruct, Join, JoinConstraint, JoinOperator, Limit, MapEntry, MutRecBlock, MutRecBlockOption, @@ -3691,9 +3691,7 @@ fn invent_column_name( Expr::Array { .. } => Some(("array".into(), NameQuality::High)), Expr::List { .. } => Some(("list".into(), NameQuality::High)), Expr::Map { .. } | Expr::MapSubquery(_) => Some(("map".into(), NameQuality::High)), - Expr::Cast { - expr, data_type, .. - } => match invent(ecx, expr, table_func_names)? { + Expr::Cast { expr, data_type } => match invent(ecx, expr, table_func_names)? { Some((name, NameQuality::High)) => Some((name, NameQuality::High)), _ => Some((data_type.unqualified_item_name().into(), NameQuality::Low)), }, @@ -4192,11 +4190,7 @@ fn plan_expr_inner<'a>( Expr::Op { op, expr1, expr2 } => { Ok(plan_op(ecx, normalize::op(op)?, expr1, expr2.as_deref())?.into()) } - Expr::Cast { - expr, - data_type, - failure_mode, - } => plan_cast(ecx, expr, data_type, *failure_mode), + Expr::Cast { expr, data_type } => plan_cast(ecx, expr, data_type), Expr::Function(func) => Ok(plan_function(ecx, func)?.into()), // Special functions and operators. @@ -4304,23 +4298,13 @@ fn plan_row(ecx: &ExprContext, exprs: &[Expr]) -> Result, data_type: &ResolvedDataType, - failure_mode: CastFailureMode, ) -> Result { - let (name, failure_mode) = match failure_mode { - CastFailureMode::Error => ("CAST", mz_expr::CastFailureMode::Error), - CastFailureMode::NullFallback => { - ecx.qcx.scx.require_feature_flag(&vars::ENABLE_TRY_CAST)?; - ("TRY_CAST", mz_expr::CastFailureMode::NullFallback) - } - }; let to_scalar_type = scalar_type_from_sql(ecx.qcx.scx, data_type)?; - let expr = match (expr, failure_mode) { + let expr = match expr { // Special case a direct cast of an ARRAY, LIST, or MAP expression so // we can pass in the target type as a type hint. This is // a limited form of the coercion that we do for string literals @@ -4329,40 +4313,14 @@ fn plan_cast( // PostgreSQL compatibility trouble. // // See: https://github.com/postgres/postgres/blob/31f403e95/src/backend/parser/parse_expr.c#L2762-L2768 - // - // The hint makes the element casts part of the literal, where a - // failure mode cannot reach them. Under `NullFallback` an ARRAY or - // LIST literal is planned on its own instead, so the cast that gets - // wrapped is the whole-value one, and a bad element yields NULL for - // the whole value just as it does for a non-literal source. A MAP - // literal has no whole-value cast to fall back on, since there is no - // cast between map types, so under `NullFallback` it is rejected - // rather than converted with the hint's element-level strictness. - (Expr::Array(exprs), mz_expr::CastFailureMode::Error) => { - plan_array(ecx, exprs, Some(&to_scalar_type))? - } - (Expr::List(exprs), mz_expr::CastFailureMode::Error) => { - plan_list(ecx, exprs, Some(&to_scalar_type))? - } - (Expr::Map(exprs), mz_expr::CastFailureMode::Error) => { - plan_map(ecx, exprs, Some(&to_scalar_type))? - } - (Expr::Map(_), mz_expr::CastFailureMode::NullFallback) => { - bail_unsupported!("MAP constructor as the argument of TRY_CAST") - } - (expr, _) => plan_expr(ecx, expr)?, + Expr::Array(exprs) => plan_array(ecx, exprs, Some(&to_scalar_type))?, + Expr::List(exprs) => plan_list(ecx, exprs, Some(&to_scalar_type))?, + Expr::Map(exprs) => plan_map(ecx, exprs, Some(&to_scalar_type))?, + _ => plan_expr(ecx, expr)?, }; - let ecx = &ecx.with_name(name); - // A string literal reaches its cast through coercion, so the failure mode - // has to apply there too, or `TRY_CAST('abc' AS int4)` would still error. - let expr = typeconv::plan_coerce_with_failure_mode(ecx, expr, &to_scalar_type, failure_mode)?; - let expr = typeconv::plan_cast_with_failure_mode( - ecx, - CastContext::Explicit, - failure_mode, - expr, - &to_scalar_type, - )?; + let ecx = &ecx.with_name("CAST"); + let expr = typeconv::plan_coerce(ecx, expr, &to_scalar_type)?; + let expr = typeconv::plan_cast(ecx, CastContext::Explicit, expr, &to_scalar_type)?; Ok(expr.into()) } diff --git a/src/sql/src/plan/typeconv.rs b/src/sql/src/plan/typeconv.rs index 6de8dbfc56d32..fce74bc72fc3f 100644 --- a/src/sql/src/plan/typeconv.rs +++ b/src/sql/src/plan/typeconv.rs @@ -16,19 +16,17 @@ use std::sync::LazyLock; use dynfmt::{Format, SimpleCurlyFormat}; use itertools::Itertools; +use mz_expr::func; use mz_expr::func::variadic::{JsonbBuildObject, RecordCreate}; use mz_expr::func::{CastArrayToJsonb, CastListToJsonb}; -use mz_expr::{CastFailureMode, func}; -use mz_ore::soft_panic_or_log; use mz_repr::{ - ColumnName, Datum, Row, SqlColumnType, SqlRelationType, SqlScalarBaseType, SqlScalarType, + ColumnName, Datum, SqlColumnType, SqlRelationType, SqlScalarBaseType, SqlScalarType, }; use crate::catalog::TypeCategory; use crate::plan::error::PlanError; use crate::plan::hir::{ - AbstractColumnType, CoercibleScalarExpr, CoercibleScalarType, HirScalarExpr, NameMetadata, - UnaryFunc, + AbstractColumnType, CoercibleScalarExpr, CoercibleScalarType, HirScalarExpr, UnaryFunc, }; use crate::plan::query::{ExprContext, QueryContext}; use crate::plan::scope::Scope; @@ -36,7 +34,7 @@ use crate::plan::scope::Scope; /// Like func::sql_impl_func, but for casts. fn sql_impl_cast(expr: &'static str) -> CastTemplate { let invoke = crate::func::sql_impl(expr); - CastTemplate::strict_only(move |ecx, _ccx, from_type, _to_type| { + CastTemplate::new(move |ecx, _ccx, from_type, _to_type| { // Oddly, this needs to be able to gracefully fail so we can detect unmet dependencies. let mut out = invoke(ecx, vec![from_type.clone()]).ok()?; Some(move |e| { @@ -51,7 +49,7 @@ fn sql_impl_cast_per_context(casts: &[(CastContext, &'static str)]) -> CastTempl .iter() .map(|(ccx, expr)| (ccx.clone(), crate::func::sql_impl(expr))) .collect(); - CastTemplate::strict_only(move |ecx, ccx, from_type, _to_type| { + CastTemplate::new(move |ecx, ccx, from_type, _to_type| { let invoke = &casts[&ccx]; let r = invoke(ecx, vec![from_type.clone()]); let mut out = r.ok()?; @@ -66,9 +64,8 @@ fn sql_impl_cast_per_context(casts: &[(CastContext, &'static str)]) -> CastTempl type Cast = Box HirScalarExpr>; /// A cast template is a function that produces a `Cast` given a concrete input -/// and output type and a failure mode. A template can return `None` to -/// indicate that it is incapable of producing a cast for the specified types, -/// or for the specified failure mode. +/// and output type. A template can return `None` to indicate that it is +/// incapable of producing a cast for the specified types. /// /// Cast templates are used to share code for similar casts, where the input or /// output type is of one "category" of type. For example, a single cast @@ -77,29 +74,13 @@ type Cast = Box HirScalarExpr>; /// which is impractical. struct CastTemplate( Box< - dyn Fn( - &ExprContext, - CastContext, - CastFailureMode, - &SqlScalarType, - &SqlScalarType, - ) -> Option + dyn Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option + Send + Sync, >, ); impl CastTemplate { - /// Builds a template from a builder of the erroring cast. - /// - /// Under [`CastFailureMode::NullFallback`] the template emits the same - /// stages with each one that could error wrapped in `TryCast`, so a - /// failure at any stage yields NULL. A stage that cannot error is emitted as - /// is, so a cast that cannot fail plans exactly as `CAST` does and keeps its - /// nullability. That requires the builder to apply only unary functions to its - /// input, which [`extract_unary_stages_for_try_cast`] verifies; a builder of any other shape - /// yields `None` under `NullFallback` and must use - /// [`CastTemplate::strict_only`] instead. fn new(t: T) -> CastTemplate where T: Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option @@ -108,85 +89,12 @@ impl CastTemplate { + 'static, C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static, { - CastTemplate(Box::new(move |ecx, ccx, mode, from_ty, to_ty| { - let cast = t(ecx, ccx, from_ty, to_ty)?; - match mode { - CastFailureMode::Error => Some(Box::new(cast)), - CastFailureMode::NullFallback => { - let stages = extract_unary_stages_for_try_cast(from_ty, cast)?; - Some(Box::new(move |mut expr: HirScalarExpr| { - for stage in stages { - expr = expr.call_unary(UnaryFunc::try_cast(stage)); - } - expr - })) - } - } - })) - } - - /// Builds a template that supports only [`CastFailureMode::Error`]. - /// - /// For casts implemented in SQL: their failures come from `mz_error_if_null` - /// calls and from subquery cardinality, neither of which a scalar-level - /// wrapper can turn into NULL. - fn strict_only(t: T) -> CastTemplate - where - T: Fn(&ExprContext, CastContext, &SqlScalarType, &SqlScalarType) -> Option - + Send - + Sync - + 'static, - C: FnOnce(HirScalarExpr) -> HirScalarExpr + 'static, - { - CastTemplate(Box::new(move |ecx, ccx, mode, from_ty, to_ty| match mode { - CastFailureMode::Error => Some(Box::new(t(ecx, ccx, from_ty, to_ty)?)), - CastFailureMode::NullFallback => None, + CastTemplate(Box::new(move |ecx, ccx, from_ty, to_ty| { + Some(Box::new(t(ecx, ccx, from_ty, to_ty)?)) })) } } -/// Extracts a sequence of `UnaryFunc`s from a cast, returning `None` if the cast -/// is not a sequence of `UnaryFunc`s. -/// -/// These stages must propagate nulls: f(g(e)) == try[f](try[g](e)) only when -/// f and g return null on a null input. (As of 2026-09-15, this is true of all -/// casts that are sequences of `UnaryFunc` stages.) -fn extract_unary_stages_for_try_cast( - from: &SqlScalarType, - cast: impl FnOnce(HirScalarExpr) -> HirScalarExpr, -) -> Option> { - // dummy argument (built manually to avoid an assert) - let sentinel = HirScalarExpr::Literal( - Row::pack([Datum::Dummy]), - from.clone().nullable(false), - NameMetadata::default(), - ); - let mut expr = cast(sentinel.clone()); - let mut stages = vec![]; - loop { - match expr { - HirScalarExpr::CallUnary { - func, expr: inner, .. - } => { - stages.push(func); - expr = *inner; - } - other if other == sentinel => break, - _ => return None, - } - } - stages.reverse(); - if let Some(stage) = stages - .iter() - .skip(1) - .find(|stage| !stage.propagates_nulls()) - { - soft_panic_or_log!("cast stage {stage} does not propagate NULLs; TRY_CAST cannot wrap it"); - return None; - } - Some(stages) -} - impl From for CastTemplate { fn from(u: UnaryFunc) -> CastTemplate { CastTemplate::new(move |_ecx, _ccx, _from, _to| { @@ -1191,8 +1099,6 @@ pub enum CastError { }, /// Cast would apply but is disallowed (e.g. range over an unsupported element type). UnsupportedRangeElementType { element_type_name: String }, - /// The cast exists but has no null-fallback form, so `TRY_CAST` cannot use it. - NullFallbackUnsupported { from: String, to: String }, } impl CastError { @@ -1208,9 +1114,6 @@ impl CastError { CastError::UnsupportedRangeElementType { element_type_name } => { PlanError::UnsupportedRangeElementType { element_type_name } } - CastError::NullFallbackUnsupported { from, to } => { - PlanError::TryCastUnsupported { from, to } - } } } } @@ -1224,13 +1127,11 @@ impl CastError { fn get_cast( ecx: &ExprContext, ccx: CastContext, - mode: CastFailureMode, from: &SqlScalarType, to: &SqlScalarType, ) -> Result { use CastContext::*; - // The identity cast cannot fail, so it is the same in every failure mode. if from == to || (ccx == Implicit && from.base_eq(to)) { return Ok(Box::new(|expr| expr)); } @@ -1255,20 +1156,8 @@ fn get_cast( } else { None }; - let build = |mode| template.and_then(|template| (template.0)(ecx, ccx, mode, from, to)); - match build(mode) { + match template.and_then(|template| (template.0)(ecx, ccx, from, to)) { Some(cast) => Ok(cast), - // A template declines a failure mode by returning `None`, the same way - // it declines a type pair. Tell the two apart so that the error names - // the actual problem. - None if mode == CastFailureMode::NullFallback - && build(CastFailureMode::Error).is_some() => - { - Err(CastError::NullFallbackUnsupported { - from: ecx.humanize_sql_scalar_type(from, false), - to: ecx.humanize_sql_scalar_type(to, false), - }) - } None => Err(CastError::InvalidCast { ccx, from: ecx.humanize_sql_scalar_type(from, false), @@ -1277,13 +1166,6 @@ fn get_cast( } } -/// The base type pairs that have an entry in the cast table, for tests that -/// want to prove they exercised every one. -pub fn valid_cast_base_type_pairs() -> impl Iterator -{ - VALID_CASTS.keys().copied() -} - /// Converts an expression to `SqlScalarType::String`. /// /// Most types have an explicit cast to string, but a few do not: `mz_aclitem` @@ -1516,18 +1398,6 @@ pub fn plan_coerce<'a>( ecx: &'a ExprContext, e: CoercibleScalarExpr, coerce_to: &SqlScalarType, -) -> Result { - plan_coerce_with_failure_mode(ecx, e, coerce_to, CastFailureMode::Error) -} - -/// Like [`plan_coerce`], but the casts that coerce a literal to `coerce_to` -/// use the given failure mode. `TRY_CAST('abc' AS int4)` reaches its cast -/// through this coercion, so the mode has to apply here too. -pub fn plan_coerce_with_failure_mode<'a>( - ecx: &'a ExprContext, - e: CoercibleScalarExpr, - coerce_to: &SqlScalarType, - mode: CastFailureMode, ) -> Result { use CoercibleScalarExpr::*; @@ -1543,32 +1413,22 @@ pub fn plan_coerce_with_failure_mode<'a>( // (with either implicit or explicit semantics) via a separate call // to `plan_cast`. let coerce_to_base = &coerce_to.without_modifiers(); - plan_cast_with_failure_mode(ecx, CastContext::Coerced, mode, lit, coerce_to_base)? + plan_cast(ecx, CastContext::Coerced, lit, coerce_to_base)? } LiteralRecord(exprs) => { let arity = exprs.len(); - // Under `NullFallback` the target field types are not pushed into - // the literal. Fields are typed on their own, so every field - // conversion happens inside the record cast that follows, where - // the `TryCast` wrapper covers it, and a bad field yields NULL for - // the whole record rather than a record with a NULL field. This - // mirrors how `plan_cast` treats collection constructors. - let coercions = match (coerce_to, mode) { - (SqlScalarType::Record { fields, .. }, CastFailureMode::Error) - if fields.len() == arity => - { - fields - .iter() - .map(|(_name, ty)| &ty.scalar_type) - .cloned() - .collect() - } + let coercions = match coerce_to { + SqlScalarType::Record { fields, .. } if fields.len() == arity => fields + .iter() + .map(|(_name, ty)| &ty.scalar_type) + .cloned() + .collect(), _ => vec![SqlScalarType::String; exprs.len()], }; let mut out = vec![]; for (e, coerce_to) in exprs.into_iter().zip_eq(coercions) { - out.push(plan_coerce_with_failure_mode(ecx, e, &coerce_to, mode)?); + out.push(plan_coerce(ecx, e, &coerce_to)?); } HirScalarExpr::call_variadic( RecordCreate { @@ -1635,17 +1495,6 @@ pub fn plan_hypothetical_cast( ccx: CastContext, from: &SqlScalarType, to: &SqlScalarType, -) -> Option { - plan_hypothetical_cast_with_failure_mode(ecx, ccx, CastFailureMode::Error, from, to) -} - -/// Similar to `plan_hypothetical_cast`, but with a `CastFailureMode` to specify behavior. -pub fn plan_hypothetical_cast_with_failure_mode( - ecx: &ExprContext, - ccx: CastContext, - mode: CastFailureMode, - from: &SqlScalarType, - to: &SqlScalarType, ) -> Option { // Reconstruct an expression context where the expression is evaluated on // the "first column" of some imaginary row. @@ -1674,13 +1523,7 @@ pub fn plan_hypothetical_cast_with_failure_mode( // Determine the `ScalarExpr` required to cast our column to the target // component type. - // - // NOTE: the templates plan element casts through `plan_hypothetical_cast`, - // which is always strict, whatever the enclosing cast's failure mode. Under - // `TRY_CAST` the enclosing function is what gets wrapped, so a bad element - // makes the whole value NULL rather than producing a value with NULL holes - // in it. - plan_cast_with_failure_mode(&ecx, ccx, mode, col_expr, to) + plan_cast(&ecx, ccx, col_expr, to) .ok()? // TODO(jkosh44) Support casts that have correlated implementations. .lower_uncorrelated(ecx.catalog().system_vars()) @@ -1701,27 +1544,13 @@ pub fn plan_cast( ccx: CastContext, expr: HirScalarExpr, to: &SqlScalarType, -) -> Result { - plan_cast_with_failure_mode(ecx, ccx, CastFailureMode::Error, expr, to) -} - -/// Like [`plan_cast`], but with the given failure mode. Under -/// [`CastFailureMode::NullFallback`] every stage of the cast, including the -/// intermediate string cast that string-like types route through, is wrapped so -/// that a failure anywhere yields NULL. -pub fn plan_cast_with_failure_mode( - ecx: &ExprContext, - ccx: CastContext, - mode: CastFailureMode, - expr: HirScalarExpr, - to: &SqlScalarType, ) -> Result { let from = ecx.scalar_type(&expr); - // Close over `ccx`, `mode`, `from`, and `to` to simplify error messages in - // the face of intermediate expressions. + // Close over `ccx`, `from`, and `to` to simplify error messages in the + // face of intermediate expressions. let cast_inner = |from, to, expr| { - get_cast(ecx, ccx, mode, from, to) + get_cast(ecx, ccx, from, to) .map(|cast| cast(expr)) .map_err(|e| e.into_plan_error(ecx.name.into())) }; @@ -1740,7 +1569,7 @@ pub fn plan_cast_with_failure_mode( // registered cast when one exists (`int4`, by byte value) and // otherwise render it to text first. let expr = match from { - SqlScalarType::PgLegacyChar => match get_cast(ecx, ccx, mode, &from, to) { + SqlScalarType::PgLegacyChar => match get_cast(ecx, ccx, &from, to) { Ok(cast) => return Ok(cast(expr)), Err(_) => cast_inner(&from, &SqlScalarType::String, expr)?, }, @@ -1765,70 +1594,5 @@ pub fn can_cast( cast_from: &SqlScalarType, cast_to: &SqlScalarType, ) -> bool { - get_cast(ecx, ccx, CastFailureMode::Error, cast_from, cast_to).is_ok() -} - -#[cfg(test)] -mod tests { - use mz_expr::func::{CastInt64ToInt32, CastStringToInt64, Eq as EqFunc, IsNull}; - use mz_repr::SqlScalarType; - - use super::extract_unary_stages_for_try_cast; - use crate::plan::hir::HirScalarExpr; - - #[mz_ore::test] - fn cast_stages_of_chain_are_innermost_first() { - let stages = - extract_unary_stages_for_try_cast(&SqlScalarType::String, |e: HirScalarExpr| { - e.call_unary(CastStringToInt64.into()) - .call_unary(CastInt64ToInt32.into()) - }); - assert_eq!( - stages, - Some(vec![CastStringToInt64.into(), CastInt64ToInt32.into()]) - ); - } - - #[mz_ore::test] - fn cast_stages_of_identity_is_empty() { - assert_eq!( - extract_unary_stages_for_try_cast(&SqlScalarType::Int32, |e: HirScalarExpr| e), - Some(vec![]) - ); - } - - #[mz_ore::test] - fn cast_stages_rejects_non_unary_shapes() { - let compares = |e: HirScalarExpr| { - let zero = HirScalarExpr::literal_null(SqlScalarType::Int32); - e.call_binary(zero, EqFunc) - }; - assert_eq!( - extract_unary_stages_for_try_cast(&SqlScalarType::Int32, compares), - None - ); - let ignores_input = |_: HirScalarExpr| HirScalarExpr::literal_null(SqlScalarType::Int32); - assert_eq!( - extract_unary_stages_for_try_cast(&SqlScalarType::Int32, ignores_input), - None - ); - } - - /// A stage after the first that does not propagate NULLs would turn the - /// NULL of an earlier failure into a value, so wrapping must refuse it. - #[mz_ore::test] - fn cast_stages_rejects_non_null_propagating_later_stage() { - let chain = |e: HirScalarExpr| { - e.call_unary(CastInt64ToInt32.into()) - .call_unary(IsNull.into()) - }; - let outcome = mz_ore::panic::catch_unwind(|| { - extract_unary_stages_for_try_cast(&SqlScalarType::Int64, chain) - }); - if mz_ore::assert::soft_assertions_enabled() { - assert!(outcome.is_err(), "soft assertion should have fired"); - } else { - assert_eq!(outcome.expect("no panic"), None); - } - } + get_cast(ecx, ccx, cast_from, cast_to).is_ok() } diff --git a/src/sql/src/pure.rs b/src/sql/src/pure.rs index c64437da0051f..842132e00e6c1 100644 --- a/src/sql/src/pure.rs +++ b/src/sql/src/pure.rs @@ -38,18 +38,18 @@ use mz_sql_parser::ast::visit::{Visit, visit_function}; use mz_sql_parser::ast::visit_mut::{VisitMut, visit_expr_mut}; use mz_sql_parser::ast::{ AlterSourceAction, AlterSourceAddSubsourceOptionName, AlterSourceStatement, AvroDocOn, - CastFailureMode, ColumnName, CreateMaterializedViewStatement, CreateSinkConnection, - CreateSinkOptionName, CreateSinkStatement, CreateSourceOptionName, CreateSubsourceOption, - CreateSubsourceOptionName, CreateTableFromSourceStatement, CsrConfigOption, - CsrConfigOptionName, CsrConnection, CsrSeedAvro, CsrSeedProtobuf, CsrSeedProtobufSchema, - DeferredItemName, DocOnIdentifier, DocOnSchema, Expr, Function, FunctionArgs, GlueAvroOption, - GlueAvroSeed, Ident, KafkaSourceConfigOption, KafkaSourceConfigOptionName, LoadGenerator, - LoadGeneratorOption, LoadGeneratorOptionName, MaterializedViewOption, - MaterializedViewOptionName, MySqlConfigOption, MySqlConfigOptionName, PgConfigOption, - PgConfigOptionName, RawItemName, ReaderSchemaSelectionStrategy, RefreshAtOptionValue, - RefreshEveryOptionValue, RefreshOptionValue, SourceEnvelope, SqlServerConfigOption, - SqlServerConfigOptionName, Statement, TableFromSourceColumns, TableFromSourceOption, - TableFromSourceOptionName, UnresolvedItemName, + ColumnName, CreateMaterializedViewStatement, CreateSinkConnection, CreateSinkOptionName, + CreateSinkStatement, CreateSourceOptionName, CreateSubsourceOption, CreateSubsourceOptionName, + CreateTableFromSourceStatement, CsrConfigOption, CsrConfigOptionName, CsrConnection, + CsrSeedAvro, CsrSeedProtobuf, CsrSeedProtobufSchema, DeferredItemName, DocOnIdentifier, + DocOnSchema, Expr, Function, FunctionArgs, GlueAvroOption, GlueAvroSeed, Ident, + KafkaSourceConfigOption, KafkaSourceConfigOptionName, LoadGenerator, LoadGeneratorOption, + LoadGeneratorOptionName, MaterializedViewOption, MaterializedViewOptionName, MySqlConfigOption, + MySqlConfigOptionName, PgConfigOption, PgConfigOptionName, RawItemName, + ReaderSchemaSelectionStrategy, RefreshAtOptionValue, RefreshEveryOptionValue, + RefreshOptionValue, SourceEnvelope, SqlServerConfigOption, SqlServerConfigOptionName, + Statement, TableFromSourceColumns, TableFromSourceOption, TableFromSourceOptionName, + UnresolvedItemName, }; use mz_sql_server_util::desc::SqlServerTableDesc; use mz_storage_types::configuration::StorageConfiguration; @@ -3174,7 +3174,6 @@ impl VisitMut<'_, Aug> for MzNowPurifierVisitor { *expr = Expr::Cast { expr: Box::new(Expr::Value(Value::Number(mz_now.to_string()))), data_type: self.mz_timestamp_type.clone(), - failure_mode: CastFailureMode::Error, }; self.introduced_mz_timestamp = true; } diff --git a/src/sql/src/session/vars/definitions.rs b/src/sql/src/session/vars/definitions.rs index fabe52224b73b..87c976a1ff7f1 100644 --- a/src/sql/src/session/vars/definitions.rs +++ b/src/sql/src/session/vars/definitions.rs @@ -2152,12 +2152,6 @@ feature_flags!( default: true, enable_for_item_parsing: true, }, - { - name: enable_try_cast, - desc: "TRY_CAST( AS ) syntax", - default: true, - enable_for_item_parsing: true, - }, { name: enable_mz_notices, desc: "Populate the contents of `mz_internal.mz_notices`", diff --git a/src/transform/fuzz/src/lib.rs b/src/transform/fuzz/src/lib.rs index ddfa9df08c1d4..cb09a96ee3d3e 100644 --- a/src/transform/fuzz/src/lib.rs +++ b/src/transform/fuzz/src/lib.rs @@ -30,8 +30,7 @@ use std::collections::BTreeMap; use libfuzzer_sys::arbitrary::{self, Arbitrary, Unstructured}; use mz_expr::{ - AggregateExpr, AggregateFunc, ColumnOrder, EvalError, MirRelationExpr, MirScalarExpr, - UnaryFunc, func, + AggregateExpr, AggregateFunc, ColumnOrder, EvalError, MirRelationExpr, MirScalarExpr, func, }; use mz_repr::optimize::OptimizerFeatures; use mz_repr::{Datum, Diff, GlobalId, ReprColumnType, ReprRelationType, ReprScalarType, Row}; @@ -97,23 +96,6 @@ fn cols_of(schema: &[Ty], ty: Ty) -> Vec { /// exercise error-propagation paths). Includes `Add`/`Sub`/`Mul`/`Mod` per /// integer width, the boolean connectives, `Eq` across a random type, `If`, and /// the `int4`<->`int8`/`int4`<->`bool` casts so neither integer width is a leaf. - -/// A cast as the planner emits it: strict, or, half the time, as `TRY_CAST` -/// would emit it, wrapped in `TryCast` when it could error. The wrapper is -/// strict in its argument and never errors, so it needs no special handling in -/// the oracle; generating it checks that `reduce` treats it that way. -fn maybe_try_cast( - u: &mut Unstructured, - cast: impl Into, -) -> arbitrary::Result { - let cast = cast.into(); - Ok(if bool::arbitrary(u)? { - UnaryFunc::try_cast(cast) - } else { - cast - }) -} - pub fn gen_scalar( u: &mut Unstructured, ty: Ty, @@ -152,8 +134,7 @@ pub fn gen_scalar( 3 => gen_scalar(u, Ty::Int32, schema, d)? .call_binary(gen_scalar(u, Ty::Int32, schema, d)?, func::ModInt32), // Narrowing cast from int8 (may error on overflow, folds to an error). - 4 => gen_scalar(u, Ty::Int64, schema, d)? - .call_unary(maybe_try_cast(u, func::CastInt64ToInt32)?), + 4 => gen_scalar(u, Ty::Int64, schema, d)?.call_unary(func::CastInt64ToInt32), _ => gen_if(u)?, }, Ty::Int64 => match u.int_in_range(0u8..=5)? { @@ -166,8 +147,7 @@ pub fn gen_scalar( 3 => gen_scalar(u, Ty::Int64, schema, d)? .call_binary(gen_scalar(u, Ty::Int64, schema, d)?, func::ModInt64), // Widening cast from int4. - 4 => gen_scalar(u, Ty::Int32, schema, d)? - .call_unary(maybe_try_cast(u, func::CastInt32ToInt64)?), + 4 => gen_scalar(u, Ty::Int32, schema, d)?.call_unary(func::CastInt32ToInt64), _ => gen_if(u)?, }, Ty::Bool => match u.int_in_range(0u8..=5)? { @@ -181,8 +161,7 @@ pub fn gen_scalar( a.call_binary(b, func::Eq) } // Cast int4 -> bool (nonzero is true). - 4 => gen_scalar(u, Ty::Int32, schema, d)? - .call_unary(maybe_try_cast(u, func::CastInt32ToBool)?), + 4 => gen_scalar(u, Ty::Int32, schema, d)?.call_unary(func::CastInt32ToBool), _ => gen_if(u)?, }, }) diff --git a/test/sqllogictest/mzcompose.py b/test/sqllogictest/mzcompose.py index 78b172f87e35a..f9e143e3467e2 100644 --- a/test/sqllogictest/mzcompose.py +++ b/test/sqllogictest/mzcompose.py @@ -588,9 +588,6 @@ def compileFastSltConfig() -> SltRunConfig: "test/sqllogictest/cast.slt", "test/sqllogictest/char.slt", "test/sqllogictest/chbench.slt", - # A constant-folded numeric(5,2) literal loses its scale in a view; - # see the file's header. - "test/sqllogictest/try_cast_noindex.slt", "test/sqllogictest/chr.slt", "test/sqllogictest/cluster.slt", "test/sqllogictest/coercion.slt", @@ -1094,9 +1091,6 @@ def compileSlowSltConfig() -> SltRunConfig: # pg_typeof contains public schema name in views "test/sqllogictest/cast.slt", "test/sqllogictest/map.slt", - # A constant-folded numeric(5,2) literal loses its scale in a view; - # see the file's header. - "test/sqllogictest/try_cast_noindex.slt", # pg_typeof contains public schema name in views "test/sqllogictest/typeof.slt", # https://github.com/MaterializeInc/database-issues/issues/9513#issuecomment-3128051157 diff --git a/test/sqllogictest/try_cast.slt b/test/sqllogictest/try_cast.slt deleted file mode 100644 index f0bc51fa17913..0000000000000 --- a/test/sqllogictest/try_cast.slt +++ /dev/null @@ -1,4471 +0,0 @@ -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. - -# TRY_CAST(expr AS type) is CAST(expr AS type) that yields NULL where the cast -# would error. Errors raised by `expr` itself still propagate. -# -# The matrix below has one block per entry of the planner's cast table. For -# each source value that converts, the block shows the TRY_CAST result and -# checks that it agrees with CAST (the second column, always true, comparing -# the text renderings so that every type is comparable; the third -# column checks that a NULL source yields NULL). For each source value that -# does not convert, the block checks that CAST errors and TRY_CAST is NULL. -# The values were chosen so that every cast with a failure mode has at least -# one failing value; a block with no failing value is a cast that cannot fail. -# -# The numeric(5,2) target's blocks are in try_cast_noindex.slt, see there. - -mode cockroach - -# The feature flag gates the syntax. -simple conn=mz_system,user=mz_system -ALTER SYSTEM SET enable_try_cast = false ----- -COMPLETE 0 - -statement error enable_try_cast -SELECT TRY_CAST('1' AS int4) - -simple conn=mz_system,user=mz_system -ALTER SYSTEM SET enable_try_cast = true ----- -COMPLETE 0 - -# Record types can only be named through user-defined types. -statement ok -CREATE TYPE rec8 AS (a int8, b text) - -statement ok -CREATE TYPE rec2 AS (a int2, b text) - -# Basics. -query ITT -SELECT TRY_CAST('42' AS int4), TRY_CAST('abc' AS int4)::text, TRY_CAST(NULL AS int4)::text ----- -42 NULL NULL - -query T -SELECT pg_typeof(TRY_CAST(1 AS int8)) ----- -bigint - -# The argument is evaluated first, and its errors propagate. -statement error division by zero -SELECT TRY_CAST(1 / 0 AS text) - -statement error number too large to fit in target type -SELECT TRY_CAST(CAST('99999' AS smallint) AS integer) - -statement error division by zero -SELECT TRY_CAST(TRY_CAST(1 / 0 AS int8) AS int4) - -# Every stage of a multi-stage cast is protected. -query TT -SELECT TRY_CAST((-1)::int8 AS regclass)::text, TRY_CAST(5::int8 AS regclass)::text ----- -NULL 5 - -# String-like targets route through text, and an explicit cast to char(n) -# truncates rather than failing, as CAST does. -query T -SELECT TRY_CAST(12345 AS char(3)) ----- -123 - -# A bad element yields NULL for the whole value, never a value with NULL -# holes in it. Collection literals are typed on their own, so the same holds -# for them. -query TTTT -SELECT TRY_CAST('{1,2,x}' AS int4 list)::text, TRY_CAST('{1,2,3}' AS int4 list)::text, TRY_CAST(ARRAY['1', 'x'] AS int4[])::text, TRY_CAST(LIST[100000] AS int2 list)::text ----- -NULL {1,2,3} NULL NULL - -query TT -SELECT TRY_CAST(ROW(100000, 'a') AS rec2)::text, TRY_CAST(ROW(1, 'a') AS rec8)::text ----- -NULL (1,a) - -# The same holds when the fields are untyped literals, which are typed on -# their own rather than coerced to the record's field types. -query TT -SELECT TRY_CAST(ROW('100000', 'a') AS rec2)::text, TRY_CAST(ROW('1', 'a') AS rec8)::text ----- -NULL (1,a) - -# Because the literal is typed on its own, its elements must agree on a type -# (CAST would coerce them all to the target's element type), and an empty -# literal needs its own type annotation. -statement error ARRAY types text and integer cannot be matched -SELECT TRY_CAST(ARRAY[s, 2] AS int4[]) FROM (VALUES ('1')) AS v(s) - -query T -SELECT TRY_CAST(ARRAY[s, 2::text] AS int4[])::text FROM (VALUES ('1')) AS v(s) ----- -{1,2} - -statement error cannot determine type of empty array -SELECT TRY_CAST(ARRAY[] AS int4[]) - -# There is no cast between map types, so a MAP constructor has no whole-value -# cast to fall back on and is rejected rather than converted element by element. -statement error MAP constructor as the argument of TRY_CAST not yet supported -SELECT TRY_CAST(MAP['k' => '1'] AS map[text=>int4]) - -query T -SELECT TRY_CAST(MAP['k' => '1']::map[text=>int4] AS map[text=>int4])::text ----- -{k=>1} - -query T -SELECT TRY_CAST(ARRAY[]::int4[] AS int8[])::text ----- -{} - -# Pairs CAST rejects are rejected the same way. -statement error CAST does not support casting from integer to jsonb -SELECT TRY_CAST(1 AS jsonb) - -# The catalog-lookup casts are implemented in SQL and have no null-fallback -# form. String-like types route through text, so all of them are refused. - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regclass -SELECT TRY_CAST(NULL::text AS regclass) - -statement error TRY_CAST cannot fall back to NULL for the cast from regclass to text -SELECT TRY_CAST(NULL::regclass AS text) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regproc -SELECT TRY_CAST(NULL::text AS regproc) - -statement error TRY_CAST cannot fall back to NULL for the cast from regproc to text -SELECT TRY_CAST(NULL::regproc AS text) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regtype -SELECT TRY_CAST(NULL::text AS regtype) - -statement error TRY_CAST cannot fall back to NULL for the cast from regtype to text -SELECT TRY_CAST(NULL::regtype AS text) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regclass -SELECT TRY_CAST(NULL::char(3) AS regclass) - -statement error TRY_CAST cannot fall back to NULL for the cast from regclass to text -SELECT TRY_CAST(NULL::regclass AS char(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regproc -SELECT TRY_CAST(NULL::char(3) AS regproc) - -statement error TRY_CAST cannot fall back to NULL for the cast from regproc to text -SELECT TRY_CAST(NULL::regproc AS char(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regtype -SELECT TRY_CAST(NULL::char(3) AS regtype) - -statement error TRY_CAST cannot fall back to NULL for the cast from regtype to text -SELECT TRY_CAST(NULL::regtype AS char(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regclass -SELECT TRY_CAST(NULL::varchar(3) AS regclass) - -statement error TRY_CAST cannot fall back to NULL for the cast from regclass to text -SELECT TRY_CAST(NULL::regclass AS varchar(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regproc -SELECT TRY_CAST(NULL::varchar(3) AS regproc) - -statement error TRY_CAST cannot fall back to NULL for the cast from regproc to text -SELECT TRY_CAST(NULL::regproc AS varchar(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regtype -SELECT TRY_CAST(NULL::varchar(3) AS regtype) - -statement error TRY_CAST cannot fall back to NULL for the cast from regtype to text -SELECT TRY_CAST(NULL::regtype AS varchar(3)) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regclass -SELECT TRY_CAST(NULL::name AS regclass) - -statement error TRY_CAST cannot fall back to NULL for the cast from regclass to text -SELECT TRY_CAST(NULL::regclass AS name) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regproc -SELECT TRY_CAST(NULL::name AS regproc) - -statement error TRY_CAST cannot fall back to NULL for the cast from regproc to text -SELECT TRY_CAST(NULL::regproc AS name) - -statement error TRY_CAST cannot fall back to NULL for the cast from text to regtype -SELECT TRY_CAST(NULL::name AS regtype) - -statement error TRY_CAST cannot fall back to NULL for the cast from regtype to text -SELECT TRY_CAST(NULL::regtype AS name) - -statement error TRY_CAST cannot fall back to NULL for the cast from aclitem to text -SELECT TRY_CAST(NULL::aclitem AS text) - -statement error TRY_CAST cannot fall back to NULL for the cast from mz_aclitem to text -SELECT TRY_CAST(NULL::mz_aclitem AS text) - -statement error TRY_CAST cannot fall back to NULL for the cast from aclitem to mz_aclitem -SELECT TRY_CAST(NULL::aclitem AS mz_aclitem) - -statement error TRY_CAST cannot fall back to NULL for the cast from mz_aclitem to aclitem -SELECT TRY_CAST(NULL::mz_aclitem AS aclitem) - -# TRY_CAST over a NOT NULL column is nullable, and NULL fallbacks flow through -# dataflows without error. A TRY_CAST that cannot fail, here bool to int4 over -# a NOT NULL column, is planned as the plain cast and stays NOT NULL. -statement ok -CREATE TABLE t (s text NOT NULL) - -statement ok -INSERT INTO t VALUES ('1'), ('2'), ('x'), ('3000000000'), ('2020-01-01') - -statement ok -CREATE MATERIALIZED VIEW mv AS SELECT s, TRY_CAST(s AS int4) AS i, TRY_CAST(s AS date) AS d, TRY_CAST(s AS int4 list) AS l, TRY_CAST(s = '' AS int4) AS b FROM t - -statement ok -CREATE INDEX mv_i ON mv (i) - -query TTTTI rowsort -SELECT s, i::text, d::text, l::text, b FROM mv ----- -1 1 NULL NULL 0 -2 2 NULL NULL 0 -2020-01-01 NULL 2020-01-01 NULL 0 -3000000000 NULL NULL NULL 0 -x NULL NULL NULL 0 - -query TT rowsort -SELECT name, nullable::text FROM mz_columns WHERE id = (SELECT id FROM mz_materialized_views WHERE name = 'mv') AND name IN ('s', 'i', 'b') ----- -b false -i true -s false - -# A view definition plans as a TryCast. -statement ok -CREATE VIEW v AS SELECT TRY_CAST(s AS int4) AS i FROM t - -query T multiline -EXPLAIN RAW PLAN FOR VIEW v ----- -Project (#1) - Map (try_cast[text_to_integer](#0{s})) - Get materialize.public.t - -EOF - -query T multiline -EXPLAIN RAW PLAN FOR SELECT TRY_CAST(s AS int4) FROM t ----- -Project (#1) - Map (try_cast[text_to_integer](#0{s})) - Get materialize.public.t - -Target cluster: quickstart - -EOF - -query T multiline -EXPLAIN OPTIMIZED PLAN AS VERBOSE TEXT FOR SELECT TRY_CAST('x' AS int4), TRY_CAST('7' AS int4) ----- -Explained Query (fast path): - Constant - - (null, 7) - -Target cluster: quickstart - -EOF - -# The matrix. - -## boolean sources - -# boolean -> int4 -query TBB -SELECT TRY_CAST(true AS int4)::text, TRY_CAST(true AS int4)::text IS NOT DISTINCT FROM CAST(true AS int4)::text, TRY_CAST(NULL::bool AS int4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(false AS int4)::text, TRY_CAST(false AS int4)::text IS NOT DISTINCT FROM CAST(false AS int4)::text, TRY_CAST(NULL::bool AS int4) IS NULL ----- -0 true true - -# boolean -> int8 -query TBB -SELECT TRY_CAST(true AS int8)::text, TRY_CAST(true AS int8)::text IS NOT DISTINCT FROM CAST(true AS int8)::text, TRY_CAST(NULL::bool AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(false AS int8)::text, TRY_CAST(false AS int8)::text IS NOT DISTINCT FROM CAST(false AS int8)::text, TRY_CAST(NULL::bool AS int8) IS NULL ----- -0 true true - -# boolean -> text -query TBB -SELECT TRY_CAST(true AS text)::text, TRY_CAST(true AS text)::text IS NOT DISTINCT FROM CAST(true AS text)::text, TRY_CAST(NULL::bool AS text) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(false AS text)::text, TRY_CAST(false AS text)::text IS NOT DISTINCT FROM CAST(false AS text)::text, TRY_CAST(NULL::bool AS text) IS NULL ----- -false true true - -## smallint sources - -# smallint -> int4 -query TBB -SELECT TRY_CAST(1::int2 AS int4)::text, TRY_CAST(1::int2 AS int4)::text IS NOT DISTINCT FROM CAST(1::int2 AS int4)::text, TRY_CAST(NULL::int2 AS int4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS int4)::text, TRY_CAST(32767::int2 AS int4)::text IS NOT DISTINCT FROM CAST(32767::int2 AS int4)::text, TRY_CAST(NULL::int2 AS int4) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS int4)::text, TRY_CAST((-1)::int2 AS int4)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS int4)::text, TRY_CAST(NULL::int2 AS int4) IS NULL ----- --1 true true - -# smallint -> int8 -query TBB -SELECT TRY_CAST(1::int2 AS int8)::text, TRY_CAST(1::int2 AS int8)::text IS NOT DISTINCT FROM CAST(1::int2 AS int8)::text, TRY_CAST(NULL::int2 AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS int8)::text, TRY_CAST(32767::int2 AS int8)::text IS NOT DISTINCT FROM CAST(32767::int2 AS int8)::text, TRY_CAST(NULL::int2 AS int8) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS int8)::text, TRY_CAST((-1)::int2 AS int8)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS int8)::text, TRY_CAST(NULL::int2 AS int8) IS NULL ----- --1 true true - -# smallint -> uint2 -query TBB -SELECT TRY_CAST(1::int2 AS uint2)::text, TRY_CAST(1::int2 AS uint2)::text IS NOT DISTINCT FROM CAST(1::int2 AS uint2)::text, TRY_CAST(NULL::int2 AS uint2) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS uint2)::text, TRY_CAST(32767::int2 AS uint2)::text IS NOT DISTINCT FROM CAST(32767::int2 AS uint2)::text, TRY_CAST(NULL::int2 AS uint2) IS NULL ----- -32767 true true - -statement error -SELECT CAST((-1)::int2 AS uint2) - -query T -SELECT TRY_CAST((-1)::int2 AS uint2)::text ----- -NULL - -# smallint -> uint4 -query TBB -SELECT TRY_CAST(1::int2 AS uint4)::text, TRY_CAST(1::int2 AS uint4)::text IS NOT DISTINCT FROM CAST(1::int2 AS uint4)::text, TRY_CAST(NULL::int2 AS uint4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS uint4)::text, TRY_CAST(32767::int2 AS uint4)::text IS NOT DISTINCT FROM CAST(32767::int2 AS uint4)::text, TRY_CAST(NULL::int2 AS uint4) IS NULL ----- -32767 true true - -statement error -SELECT CAST((-1)::int2 AS uint4) - -query T -SELECT TRY_CAST((-1)::int2 AS uint4)::text ----- -NULL - -# smallint -> uint8 -query TBB -SELECT TRY_CAST(1::int2 AS uint8)::text, TRY_CAST(1::int2 AS uint8)::text IS NOT DISTINCT FROM CAST(1::int2 AS uint8)::text, TRY_CAST(NULL::int2 AS uint8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS uint8)::text, TRY_CAST(32767::int2 AS uint8)::text IS NOT DISTINCT FROM CAST(32767::int2 AS uint8)::text, TRY_CAST(NULL::int2 AS uint8) IS NULL ----- -32767 true true - -statement error -SELECT CAST((-1)::int2 AS uint8) - -query T -SELECT TRY_CAST((-1)::int2 AS uint8)::text ----- -NULL - -# smallint -> float4 -query TBB -SELECT TRY_CAST(1::int2 AS float4)::text, TRY_CAST(1::int2 AS float4)::text IS NOT DISTINCT FROM CAST(1::int2 AS float4)::text, TRY_CAST(NULL::int2 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS float4)::text, TRY_CAST(32767::int2 AS float4)::text IS NOT DISTINCT FROM CAST(32767::int2 AS float4)::text, TRY_CAST(NULL::int2 AS float4) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS float4)::text, TRY_CAST((-1)::int2 AS float4)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS float4)::text, TRY_CAST(NULL::int2 AS float4) IS NULL ----- --1 true true - -# smallint -> float8 -query TBB -SELECT TRY_CAST(1::int2 AS float8)::text, TRY_CAST(1::int2 AS float8)::text IS NOT DISTINCT FROM CAST(1::int2 AS float8)::text, TRY_CAST(NULL::int2 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS float8)::text, TRY_CAST(32767::int2 AS float8)::text IS NOT DISTINCT FROM CAST(32767::int2 AS float8)::text, TRY_CAST(NULL::int2 AS float8) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS float8)::text, TRY_CAST((-1)::int2 AS float8)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS float8)::text, TRY_CAST(NULL::int2 AS float8) IS NULL ----- --1 true true - -# smallint -> numeric -query TBB -SELECT TRY_CAST(1::int2 AS numeric)::text, TRY_CAST(1::int2 AS numeric)::text IS NOT DISTINCT FROM CAST(1::int2 AS numeric)::text, TRY_CAST(NULL::int2 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS numeric)::text, TRY_CAST(32767::int2 AS numeric)::text IS NOT DISTINCT FROM CAST(32767::int2 AS numeric)::text, TRY_CAST(NULL::int2 AS numeric) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS numeric)::text, TRY_CAST((-1)::int2 AS numeric)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS numeric)::text, TRY_CAST(NULL::int2 AS numeric) IS NULL ----- --1 true true - -# smallint -> oid -query TBB -SELECT TRY_CAST(1::int2 AS oid)::text, TRY_CAST(1::int2 AS oid)::text IS NOT DISTINCT FROM CAST(1::int2 AS oid)::text, TRY_CAST(NULL::int2 AS oid) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS oid)::text, TRY_CAST(32767::int2 AS oid)::text IS NOT DISTINCT FROM CAST(32767::int2 AS oid)::text, TRY_CAST(NULL::int2 AS oid) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS oid)::text, TRY_CAST((-1)::int2 AS oid)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS oid)::text, TRY_CAST(NULL::int2 AS oid) IS NULL ----- -4294967295 true true - -# smallint -> regclass -query TBB -SELECT TRY_CAST(1::int2 AS regclass)::text, TRY_CAST(1::int2 AS regclass)::text IS NOT DISTINCT FROM CAST(1::int2 AS regclass)::text, TRY_CAST(NULL::int2 AS regclass) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS regclass)::text, TRY_CAST(32767::int2 AS regclass)::text IS NOT DISTINCT FROM CAST(32767::int2 AS regclass)::text, TRY_CAST(NULL::int2 AS regclass) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS regclass)::text, TRY_CAST((-1)::int2 AS regclass)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS regclass)::text, TRY_CAST(NULL::int2 AS regclass) IS NULL ----- -4294967295 true true - -# smallint -> regproc -query TBB -SELECT TRY_CAST(1::int2 AS regproc)::text, TRY_CAST(1::int2 AS regproc)::text IS NOT DISTINCT FROM CAST(1::int2 AS regproc)::text, TRY_CAST(NULL::int2 AS regproc) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS regproc)::text, TRY_CAST(32767::int2 AS regproc)::text IS NOT DISTINCT FROM CAST(32767::int2 AS regproc)::text, TRY_CAST(NULL::int2 AS regproc) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS regproc)::text, TRY_CAST((-1)::int2 AS regproc)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS regproc)::text, TRY_CAST(NULL::int2 AS regproc) IS NULL ----- -4294967295 true true - -# smallint -> regtype -query TBB -SELECT TRY_CAST(1::int2 AS regtype)::text, TRY_CAST(1::int2 AS regtype)::text IS NOT DISTINCT FROM CAST(1::int2 AS regtype)::text, TRY_CAST(NULL::int2 AS regtype) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS regtype)::text, TRY_CAST(32767::int2 AS regtype)::text IS NOT DISTINCT FROM CAST(32767::int2 AS regtype)::text, TRY_CAST(NULL::int2 AS regtype) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS regtype)::text, TRY_CAST((-1)::int2 AS regtype)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS regtype)::text, TRY_CAST(NULL::int2 AS regtype) IS NULL ----- -4294967295 true true - -# smallint -> text -query TBB -SELECT TRY_CAST(1::int2 AS text)::text, TRY_CAST(1::int2 AS text)::text IS NOT DISTINCT FROM CAST(1::int2 AS text)::text, TRY_CAST(NULL::int2 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS text)::text, TRY_CAST(32767::int2 AS text)::text IS NOT DISTINCT FROM CAST(32767::int2 AS text)::text, TRY_CAST(NULL::int2 AS text) IS NULL ----- -32767 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS text)::text, TRY_CAST((-1)::int2 AS text)::text IS NOT DISTINCT FROM CAST((-1)::int2 AS text)::text, TRY_CAST(NULL::int2 AS text) IS NULL ----- --1 true true - -## integer sources - -# integer -> bool -query TBB -SELECT TRY_CAST(1 AS bool)::text, TRY_CAST(1 AS bool)::text IS NOT DISTINCT FROM CAST(1 AS bool)::text, TRY_CAST(NULL::int4 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(2147483647 AS bool)::text, TRY_CAST(2147483647 AS bool)::text IS NOT DISTINCT FROM CAST(2147483647 AS bool)::text, TRY_CAST(NULL::int4 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(-1 AS bool)::text, TRY_CAST(-1 AS bool)::text IS NOT DISTINCT FROM CAST(-1 AS bool)::text, TRY_CAST(NULL::int4 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(100000 AS bool)::text, TRY_CAST(100000 AS bool)::text IS NOT DISTINCT FROM CAST(100000 AS bool)::text, TRY_CAST(NULL::int4 AS bool) IS NULL ----- -true true true - -# integer -> oid -query TBB -SELECT TRY_CAST(1 AS oid)::text, TRY_CAST(1 AS oid)::text IS NOT DISTINCT FROM CAST(1 AS oid)::text, TRY_CAST(NULL::int4 AS oid) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS oid)::text, TRY_CAST(2147483647 AS oid)::text IS NOT DISTINCT FROM CAST(2147483647 AS oid)::text, TRY_CAST(NULL::int4 AS oid) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS oid)::text, TRY_CAST(-1 AS oid)::text IS NOT DISTINCT FROM CAST(-1 AS oid)::text, TRY_CAST(NULL::int4 AS oid) IS NULL ----- -4294967295 true true - -query TBB -SELECT TRY_CAST(100000 AS oid)::text, TRY_CAST(100000 AS oid)::text IS NOT DISTINCT FROM CAST(100000 AS oid)::text, TRY_CAST(NULL::int4 AS oid) IS NULL ----- -100000 true true - -# integer -> regclass -query TBB -SELECT TRY_CAST(1 AS regclass)::text, TRY_CAST(1 AS regclass)::text IS NOT DISTINCT FROM CAST(1 AS regclass)::text, TRY_CAST(NULL::int4 AS regclass) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS regclass)::text, TRY_CAST(2147483647 AS regclass)::text IS NOT DISTINCT FROM CAST(2147483647 AS regclass)::text, TRY_CAST(NULL::int4 AS regclass) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS regclass)::text, TRY_CAST(-1 AS regclass)::text IS NOT DISTINCT FROM CAST(-1 AS regclass)::text, TRY_CAST(NULL::int4 AS regclass) IS NULL ----- -4294967295 true true - -query TBB -SELECT TRY_CAST(100000 AS regclass)::text, TRY_CAST(100000 AS regclass)::text IS NOT DISTINCT FROM CAST(100000 AS regclass)::text, TRY_CAST(NULL::int4 AS regclass) IS NULL ----- -100000 true true - -# integer -> regproc -query TBB -SELECT TRY_CAST(1 AS regproc)::text, TRY_CAST(1 AS regproc)::text IS NOT DISTINCT FROM CAST(1 AS regproc)::text, TRY_CAST(NULL::int4 AS regproc) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS regproc)::text, TRY_CAST(2147483647 AS regproc)::text IS NOT DISTINCT FROM CAST(2147483647 AS regproc)::text, TRY_CAST(NULL::int4 AS regproc) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS regproc)::text, TRY_CAST(-1 AS regproc)::text IS NOT DISTINCT FROM CAST(-1 AS regproc)::text, TRY_CAST(NULL::int4 AS regproc) IS NULL ----- -4294967295 true true - -query TBB -SELECT TRY_CAST(100000 AS regproc)::text, TRY_CAST(100000 AS regproc)::text IS NOT DISTINCT FROM CAST(100000 AS regproc)::text, TRY_CAST(NULL::int4 AS regproc) IS NULL ----- -100000 true true - -# integer -> regtype -query TBB -SELECT TRY_CAST(1 AS regtype)::text, TRY_CAST(1 AS regtype)::text IS NOT DISTINCT FROM CAST(1 AS regtype)::text, TRY_CAST(NULL::int4 AS regtype) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS regtype)::text, TRY_CAST(2147483647 AS regtype)::text IS NOT DISTINCT FROM CAST(2147483647 AS regtype)::text, TRY_CAST(NULL::int4 AS regtype) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS regtype)::text, TRY_CAST(-1 AS regtype)::text IS NOT DISTINCT FROM CAST(-1 AS regtype)::text, TRY_CAST(NULL::int4 AS regtype) IS NULL ----- -4294967295 true true - -query TBB -SELECT TRY_CAST(100000 AS regtype)::text, TRY_CAST(100000 AS regtype)::text IS NOT DISTINCT FROM CAST(100000 AS regtype)::text, TRY_CAST(NULL::int4 AS regtype) IS NULL ----- -100000 true true - -# integer -> "char" -query TBB -SELECT TRY_CAST(1 AS "char")::text, TRY_CAST(1 AS "char")::text IS NOT DISTINCT FROM CAST(1 AS "char")::text, TRY_CAST(NULL::int4 AS "char") IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS "char")::text, TRY_CAST(2147483647 AS "char")::text IS NOT DISTINCT FROM CAST(2147483647 AS "char")::text, TRY_CAST(NULL::int4 AS "char") IS NULL ----- -2 true true - -query TBB -SELECT TRY_CAST(-1 AS "char")::text, TRY_CAST(-1 AS "char")::text IS NOT DISTINCT FROM CAST(-1 AS "char")::text, TRY_CAST(NULL::int4 AS "char") IS NULL ----- -- true true - -query TBB -SELECT TRY_CAST(100000 AS "char")::text, TRY_CAST(100000 AS "char")::text IS NOT DISTINCT FROM CAST(100000 AS "char")::text, TRY_CAST(NULL::int4 AS "char") IS NULL ----- -1 true true - -# integer -> int2 -query TBB -SELECT TRY_CAST(1 AS int2)::text, TRY_CAST(1 AS int2)::text IS NOT DISTINCT FROM CAST(1 AS int2)::text, TRY_CAST(NULL::int4 AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST(2147483647 AS int2) - -query T -SELECT TRY_CAST(2147483647 AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST(-1 AS int2)::text, TRY_CAST(-1 AS int2)::text IS NOT DISTINCT FROM CAST(-1 AS int2)::text, TRY_CAST(NULL::int4 AS int2) IS NULL ----- --1 true true - -statement error -SELECT CAST(100000 AS int2) - -query T -SELECT TRY_CAST(100000 AS int2)::text ----- -NULL - -# integer -> int8 -query TBB -SELECT TRY_CAST(1 AS int8)::text, TRY_CAST(1 AS int8)::text IS NOT DISTINCT FROM CAST(1 AS int8)::text, TRY_CAST(NULL::int4 AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS int8)::text, TRY_CAST(2147483647 AS int8)::text IS NOT DISTINCT FROM CAST(2147483647 AS int8)::text, TRY_CAST(NULL::int4 AS int8) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS int8)::text, TRY_CAST(-1 AS int8)::text IS NOT DISTINCT FROM CAST(-1 AS int8)::text, TRY_CAST(NULL::int4 AS int8) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(100000 AS int8)::text, TRY_CAST(100000 AS int8)::text IS NOT DISTINCT FROM CAST(100000 AS int8)::text, TRY_CAST(NULL::int4 AS int8) IS NULL ----- -100000 true true - -# integer -> uint2 -query TBB -SELECT TRY_CAST(1 AS uint2)::text, TRY_CAST(1 AS uint2)::text IS NOT DISTINCT FROM CAST(1 AS uint2)::text, TRY_CAST(NULL::int4 AS uint2) IS NULL ----- -1 true true - -statement error -SELECT CAST(2147483647 AS uint2) - -query T -SELECT TRY_CAST(2147483647 AS uint2)::text ----- -NULL - -statement error -SELECT CAST(-1 AS uint2) - -query T -SELECT TRY_CAST(-1 AS uint2)::text ----- -NULL - -statement error -SELECT CAST(100000 AS uint2) - -query T -SELECT TRY_CAST(100000 AS uint2)::text ----- -NULL - -# integer -> uint4 -query TBB -SELECT TRY_CAST(1 AS uint4)::text, TRY_CAST(1 AS uint4)::text IS NOT DISTINCT FROM CAST(1 AS uint4)::text, TRY_CAST(NULL::int4 AS uint4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS uint4)::text, TRY_CAST(2147483647 AS uint4)::text IS NOT DISTINCT FROM CAST(2147483647 AS uint4)::text, TRY_CAST(NULL::int4 AS uint4) IS NULL ----- -2147483647 true true - -statement error -SELECT CAST(-1 AS uint4) - -query T -SELECT TRY_CAST(-1 AS uint4)::text ----- -NULL - -query TBB -SELECT TRY_CAST(100000 AS uint4)::text, TRY_CAST(100000 AS uint4)::text IS NOT DISTINCT FROM CAST(100000 AS uint4)::text, TRY_CAST(NULL::int4 AS uint4) IS NULL ----- -100000 true true - -# integer -> uint8 -query TBB -SELECT TRY_CAST(1 AS uint8)::text, TRY_CAST(1 AS uint8)::text IS NOT DISTINCT FROM CAST(1 AS uint8)::text, TRY_CAST(NULL::int4 AS uint8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS uint8)::text, TRY_CAST(2147483647 AS uint8)::text IS NOT DISTINCT FROM CAST(2147483647 AS uint8)::text, TRY_CAST(NULL::int4 AS uint8) IS NULL ----- -2147483647 true true - -statement error -SELECT CAST(-1 AS uint8) - -query T -SELECT TRY_CAST(-1 AS uint8)::text ----- -NULL - -query TBB -SELECT TRY_CAST(100000 AS uint8)::text, TRY_CAST(100000 AS uint8)::text IS NOT DISTINCT FROM CAST(100000 AS uint8)::text, TRY_CAST(NULL::int4 AS uint8) IS NULL ----- -100000 true true - -# integer -> float4 -query TBB -SELECT TRY_CAST(1 AS float4)::text, TRY_CAST(1 AS float4)::text IS NOT DISTINCT FROM CAST(1 AS float4)::text, TRY_CAST(NULL::int4 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS float4)::text, TRY_CAST(2147483647 AS float4)::text IS NOT DISTINCT FROM CAST(2147483647 AS float4)::text, TRY_CAST(NULL::int4 AS float4) IS NULL ----- -2147483600 true true - -query TBB -SELECT TRY_CAST(-1 AS float4)::text, TRY_CAST(-1 AS float4)::text IS NOT DISTINCT FROM CAST(-1 AS float4)::text, TRY_CAST(NULL::int4 AS float4) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(100000 AS float4)::text, TRY_CAST(100000 AS float4)::text IS NOT DISTINCT FROM CAST(100000 AS float4)::text, TRY_CAST(NULL::int4 AS float4) IS NULL ----- -100000 true true - -# integer -> float8 -query TBB -SELECT TRY_CAST(1 AS float8)::text, TRY_CAST(1 AS float8)::text IS NOT DISTINCT FROM CAST(1 AS float8)::text, TRY_CAST(NULL::int4 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS float8)::text, TRY_CAST(2147483647 AS float8)::text IS NOT DISTINCT FROM CAST(2147483647 AS float8)::text, TRY_CAST(NULL::int4 AS float8) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS float8)::text, TRY_CAST(-1 AS float8)::text IS NOT DISTINCT FROM CAST(-1 AS float8)::text, TRY_CAST(NULL::int4 AS float8) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(100000 AS float8)::text, TRY_CAST(100000 AS float8)::text IS NOT DISTINCT FROM CAST(100000 AS float8)::text, TRY_CAST(NULL::int4 AS float8) IS NULL ----- -100000 true true - -# integer -> numeric -query TBB -SELECT TRY_CAST(1 AS numeric)::text, TRY_CAST(1 AS numeric)::text IS NOT DISTINCT FROM CAST(1 AS numeric)::text, TRY_CAST(NULL::int4 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS numeric)::text, TRY_CAST(2147483647 AS numeric)::text IS NOT DISTINCT FROM CAST(2147483647 AS numeric)::text, TRY_CAST(NULL::int4 AS numeric) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS numeric)::text, TRY_CAST(-1 AS numeric)::text IS NOT DISTINCT FROM CAST(-1 AS numeric)::text, TRY_CAST(NULL::int4 AS numeric) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(100000 AS numeric)::text, TRY_CAST(100000 AS numeric)::text IS NOT DISTINCT FROM CAST(100000 AS numeric)::text, TRY_CAST(NULL::int4 AS numeric) IS NULL ----- -100000 true true - -# integer -> text -query TBB -SELECT TRY_CAST(1 AS text)::text, TRY_CAST(1 AS text)::text IS NOT DISTINCT FROM CAST(1 AS text)::text, TRY_CAST(NULL::int4 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS text)::text, TRY_CAST(2147483647 AS text)::text IS NOT DISTINCT FROM CAST(2147483647 AS text)::text, TRY_CAST(NULL::int4 AS text) IS NULL ----- -2147483647 true true - -query TBB -SELECT TRY_CAST(-1 AS text)::text, TRY_CAST(-1 AS text)::text IS NOT DISTINCT FROM CAST(-1 AS text)::text, TRY_CAST(NULL::int4 AS text) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(100000 AS text)::text, TRY_CAST(100000 AS text)::text IS NOT DISTINCT FROM CAST(100000 AS text)::text, TRY_CAST(NULL::int4 AS text) IS NULL ----- -100000 true true - -# integer -> mz_timestamp -query TBB -SELECT TRY_CAST(1 AS mz_timestamp)::text, TRY_CAST(1 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(1 AS mz_timestamp)::text, TRY_CAST(NULL::int4 AS mz_timestamp) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(2147483647 AS mz_timestamp)::text, TRY_CAST(2147483647 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(2147483647 AS mz_timestamp)::text, TRY_CAST(NULL::int4 AS mz_timestamp) IS NULL ----- -2147483647 true true - -statement error -SELECT CAST(-1 AS mz_timestamp) - -query T -SELECT TRY_CAST(-1 AS mz_timestamp)::text ----- -NULL - -query TBB -SELECT TRY_CAST(100000 AS mz_timestamp)::text, TRY_CAST(100000 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(100000 AS mz_timestamp)::text, TRY_CAST(NULL::int4 AS mz_timestamp) IS NULL ----- -100000 true true - -## bigint sources - -# bigint -> bool -query TBB -SELECT TRY_CAST(1::int8 AS bool)::text, TRY_CAST(1::int8 AS bool)::text IS NOT DISTINCT FROM CAST(1::int8 AS bool)::text, TRY_CAST(NULL::int8 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS bool)::text, TRY_CAST(9223372036854775807::int8 AS bool)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS bool)::text, TRY_CAST(NULL::int8 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS bool)::text, TRY_CAST((-1)::int8 AS bool)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS bool)::text, TRY_CAST(NULL::int8 AS bool) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS bool)::text, TRY_CAST(3000000000::int8 AS bool)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS bool)::text, TRY_CAST(NULL::int8 AS bool) IS NULL ----- -true true true - -# bigint -> int2 -query TBB -SELECT TRY_CAST(1::int8 AS int2)::text, TRY_CAST(1::int8 AS int2)::text IS NOT DISTINCT FROM CAST(1::int8 AS int2)::text, TRY_CAST(NULL::int8 AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS int2) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1)::int8 AS int2)::text, TRY_CAST((-1)::int8 AS int2)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS int2)::text, TRY_CAST(NULL::int8 AS int2) IS NULL ----- --1 true true - -statement error -SELECT CAST(3000000000::int8 AS int2) - -query T -SELECT TRY_CAST(3000000000::int8 AS int2)::text ----- -NULL - -# bigint -> int4 -query TBB -SELECT TRY_CAST(1::int8 AS int4)::text, TRY_CAST(1::int8 AS int4)::text IS NOT DISTINCT FROM CAST(1::int8 AS int4)::text, TRY_CAST(NULL::int8 AS int4) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS int4) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS int4)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1)::int8 AS int4)::text, TRY_CAST((-1)::int8 AS int4)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS int4)::text, TRY_CAST(NULL::int8 AS int4) IS NULL ----- --1 true true - -statement error -SELECT CAST(3000000000::int8 AS int4) - -query T -SELECT TRY_CAST(3000000000::int8 AS int4)::text ----- -NULL - -# bigint -> uint2 -query TBB -SELECT TRY_CAST(1::int8 AS uint2)::text, TRY_CAST(1::int8 AS uint2)::text IS NOT DISTINCT FROM CAST(1::int8 AS uint2)::text, TRY_CAST(NULL::int8 AS uint2) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS uint2) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS uint2)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS uint2) - -query T -SELECT TRY_CAST((-1)::int8 AS uint2)::text ----- -NULL - -statement error -SELECT CAST(3000000000::int8 AS uint2) - -query T -SELECT TRY_CAST(3000000000::int8 AS uint2)::text ----- -NULL - -# bigint -> uint4 -query TBB -SELECT TRY_CAST(1::int8 AS uint4)::text, TRY_CAST(1::int8 AS uint4)::text IS NOT DISTINCT FROM CAST(1::int8 AS uint4)::text, TRY_CAST(NULL::int8 AS uint4) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS uint4) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS uint4)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS uint4) - -query T -SELECT TRY_CAST((-1)::int8 AS uint4)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS uint4)::text, TRY_CAST(3000000000::int8 AS uint4)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS uint4)::text, TRY_CAST(NULL::int8 AS uint4) IS NULL ----- -3000000000 true true - -# bigint -> uint8 -query TBB -SELECT TRY_CAST(1::int8 AS uint8)::text, TRY_CAST(1::int8 AS uint8)::text IS NOT DISTINCT FROM CAST(1::int8 AS uint8)::text, TRY_CAST(NULL::int8 AS uint8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS uint8)::text, TRY_CAST(9223372036854775807::int8 AS uint8)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS uint8)::text, TRY_CAST(NULL::int8 AS uint8) IS NULL ----- -9223372036854775807 true true - -statement error -SELECT CAST((-1)::int8 AS uint8) - -query T -SELECT TRY_CAST((-1)::int8 AS uint8)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS uint8)::text, TRY_CAST(3000000000::int8 AS uint8)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS uint8)::text, TRY_CAST(NULL::int8 AS uint8) IS NULL ----- -3000000000 true true - -# bigint -> numeric -query TBB -SELECT TRY_CAST(1::int8 AS numeric)::text, TRY_CAST(1::int8 AS numeric)::text IS NOT DISTINCT FROM CAST(1::int8 AS numeric)::text, TRY_CAST(NULL::int8 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS numeric)::text, TRY_CAST(9223372036854775807::int8 AS numeric)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS numeric)::text, TRY_CAST(NULL::int8 AS numeric) IS NULL ----- -9223372036854775807 true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS numeric)::text, TRY_CAST((-1)::int8 AS numeric)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS numeric)::text, TRY_CAST(NULL::int8 AS numeric) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS numeric)::text, TRY_CAST(3000000000::int8 AS numeric)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS numeric)::text, TRY_CAST(NULL::int8 AS numeric) IS NULL ----- -3000000000 true true - -# bigint -> float4 -query TBB -SELECT TRY_CAST(1::int8 AS float4)::text, TRY_CAST(1::int8 AS float4)::text IS NOT DISTINCT FROM CAST(1::int8 AS float4)::text, TRY_CAST(NULL::int8 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS float4)::text, TRY_CAST(9223372036854775807::int8 AS float4)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS float4)::text, TRY_CAST(NULL::int8 AS float4) IS NULL ----- -9.223372e+18 true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS float4)::text, TRY_CAST((-1)::int8 AS float4)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS float4)::text, TRY_CAST(NULL::int8 AS float4) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS float4)::text, TRY_CAST(3000000000::int8 AS float4)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS float4)::text, TRY_CAST(NULL::int8 AS float4) IS NULL ----- -3000000000 true true - -# bigint -> float8 -query TBB -SELECT TRY_CAST(1::int8 AS float8)::text, TRY_CAST(1::int8 AS float8)::text IS NOT DISTINCT FROM CAST(1::int8 AS float8)::text, TRY_CAST(NULL::int8 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS float8)::text, TRY_CAST(9223372036854775807::int8 AS float8)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS float8)::text, TRY_CAST(NULL::int8 AS float8) IS NULL ----- -9.223372036854776e+18 true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS float8)::text, TRY_CAST((-1)::int8 AS float8)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS float8)::text, TRY_CAST(NULL::int8 AS float8) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS float8)::text, TRY_CAST(3000000000::int8 AS float8)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS float8)::text, TRY_CAST(NULL::int8 AS float8) IS NULL ----- -3000000000 true true - -# bigint -> oid -query TBB -SELECT TRY_CAST(1::int8 AS oid)::text, TRY_CAST(1::int8 AS oid)::text IS NOT DISTINCT FROM CAST(1::int8 AS oid)::text, TRY_CAST(NULL::int8 AS oid) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS oid) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS oid)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS oid) - -query T -SELECT TRY_CAST((-1)::int8 AS oid)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS oid)::text, TRY_CAST(3000000000::int8 AS oid)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS oid)::text, TRY_CAST(NULL::int8 AS oid) IS NULL ----- -3000000000 true true - -# bigint -> regclass -query TBB -SELECT TRY_CAST(1::int8 AS regclass)::text, TRY_CAST(1::int8 AS regclass)::text IS NOT DISTINCT FROM CAST(1::int8 AS regclass)::text, TRY_CAST(NULL::int8 AS regclass) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS regclass) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS regclass)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS regclass) - -query T -SELECT TRY_CAST((-1)::int8 AS regclass)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS regclass)::text, TRY_CAST(3000000000::int8 AS regclass)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS regclass)::text, TRY_CAST(NULL::int8 AS regclass) IS NULL ----- -3000000000 true true - -# bigint -> regproc -query TBB -SELECT TRY_CAST(1::int8 AS regproc)::text, TRY_CAST(1::int8 AS regproc)::text IS NOT DISTINCT FROM CAST(1::int8 AS regproc)::text, TRY_CAST(NULL::int8 AS regproc) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS regproc) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS regproc)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS regproc) - -query T -SELECT TRY_CAST((-1)::int8 AS regproc)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS regproc)::text, TRY_CAST(3000000000::int8 AS regproc)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS regproc)::text, TRY_CAST(NULL::int8 AS regproc) IS NULL ----- -3000000000 true true - -# bigint -> regtype -query TBB -SELECT TRY_CAST(1::int8 AS regtype)::text, TRY_CAST(1::int8 AS regtype)::text IS NOT DISTINCT FROM CAST(1::int8 AS regtype)::text, TRY_CAST(NULL::int8 AS regtype) IS NULL ----- -1 true true - -statement error -SELECT CAST(9223372036854775807::int8 AS regtype) - -query T -SELECT TRY_CAST(9223372036854775807::int8 AS regtype)::text ----- -NULL - -statement error -SELECT CAST((-1)::int8 AS regtype) - -query T -SELECT TRY_CAST((-1)::int8 AS regtype)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS regtype)::text, TRY_CAST(3000000000::int8 AS regtype)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS regtype)::text, TRY_CAST(NULL::int8 AS regtype) IS NULL ----- -3000000000 true true - -# bigint -> text -query TBB -SELECT TRY_CAST(1::int8 AS text)::text, TRY_CAST(1::int8 AS text)::text IS NOT DISTINCT FROM CAST(1::int8 AS text)::text, TRY_CAST(NULL::int8 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS text)::text, TRY_CAST(9223372036854775807::int8 AS text)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS text)::text, TRY_CAST(NULL::int8 AS text) IS NULL ----- -9223372036854775807 true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS text)::text, TRY_CAST((-1)::int8 AS text)::text IS NOT DISTINCT FROM CAST((-1)::int8 AS text)::text, TRY_CAST(NULL::int8 AS text) IS NULL ----- --1 true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS text)::text, TRY_CAST(3000000000::int8 AS text)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS text)::text, TRY_CAST(NULL::int8 AS text) IS NULL ----- -3000000000 true true - -# bigint -> mz_timestamp -query TBB -SELECT TRY_CAST(1::int8 AS mz_timestamp)::text, TRY_CAST(1::int8 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(1::int8 AS mz_timestamp)::text, TRY_CAST(NULL::int8 AS mz_timestamp) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS mz_timestamp)::text, TRY_CAST(9223372036854775807::int8 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS mz_timestamp)::text, TRY_CAST(NULL::int8 AS mz_timestamp) IS NULL ----- -9223372036854775807 true true - -statement error -SELECT CAST((-1)::int8 AS mz_timestamp) - -query T -SELECT TRY_CAST((-1)::int8 AS mz_timestamp)::text ----- -NULL - -query TBB -SELECT TRY_CAST(3000000000::int8 AS mz_timestamp)::text, TRY_CAST(3000000000::int8 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS mz_timestamp)::text, TRY_CAST(NULL::int8 AS mz_timestamp) IS NULL ----- -3000000000 true true - -## uint2 sources - -# uint2 -> uint4 -query TBB -SELECT TRY_CAST(1::uint2 AS uint4)::text, TRY_CAST(1::uint2 AS uint4)::text IS NOT DISTINCT FROM CAST(1::uint2 AS uint4)::text, TRY_CAST(NULL::uint2 AS uint4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS uint4)::text, TRY_CAST(65535::uint2 AS uint4)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS uint4)::text, TRY_CAST(NULL::uint2 AS uint4) IS NULL ----- -65535 true true - -# uint2 -> uint8 -query TBB -SELECT TRY_CAST(1::uint2 AS uint8)::text, TRY_CAST(1::uint2 AS uint8)::text IS NOT DISTINCT FROM CAST(1::uint2 AS uint8)::text, TRY_CAST(NULL::uint2 AS uint8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS uint8)::text, TRY_CAST(65535::uint2 AS uint8)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS uint8)::text, TRY_CAST(NULL::uint2 AS uint8) IS NULL ----- -65535 true true - -# uint2 -> int2 -query TBB -SELECT TRY_CAST(1::uint2 AS int2)::text, TRY_CAST(1::uint2 AS int2)::text IS NOT DISTINCT FROM CAST(1::uint2 AS int2)::text, TRY_CAST(NULL::uint2 AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST(65535::uint2 AS int2) - -query T -SELECT TRY_CAST(65535::uint2 AS int2)::text ----- -NULL - -# uint2 -> int4 -query TBB -SELECT TRY_CAST(1::uint2 AS int4)::text, TRY_CAST(1::uint2 AS int4)::text IS NOT DISTINCT FROM CAST(1::uint2 AS int4)::text, TRY_CAST(NULL::uint2 AS int4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS int4)::text, TRY_CAST(65535::uint2 AS int4)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS int4)::text, TRY_CAST(NULL::uint2 AS int4) IS NULL ----- -65535 true true - -# uint2 -> int8 -query TBB -SELECT TRY_CAST(1::uint2 AS int8)::text, TRY_CAST(1::uint2 AS int8)::text IS NOT DISTINCT FROM CAST(1::uint2 AS int8)::text, TRY_CAST(NULL::uint2 AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS int8)::text, TRY_CAST(65535::uint2 AS int8)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS int8)::text, TRY_CAST(NULL::uint2 AS int8) IS NULL ----- -65535 true true - -# uint2 -> numeric -query TBB -SELECT TRY_CAST(1::uint2 AS numeric)::text, TRY_CAST(1::uint2 AS numeric)::text IS NOT DISTINCT FROM CAST(1::uint2 AS numeric)::text, TRY_CAST(NULL::uint2 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS numeric)::text, TRY_CAST(65535::uint2 AS numeric)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS numeric)::text, TRY_CAST(NULL::uint2 AS numeric) IS NULL ----- -65535 true true - -# uint2 -> float4 -query TBB -SELECT TRY_CAST(1::uint2 AS float4)::text, TRY_CAST(1::uint2 AS float4)::text IS NOT DISTINCT FROM CAST(1::uint2 AS float4)::text, TRY_CAST(NULL::uint2 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS float4)::text, TRY_CAST(65535::uint2 AS float4)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS float4)::text, TRY_CAST(NULL::uint2 AS float4) IS NULL ----- -65535 true true - -# uint2 -> float8 -query TBB -SELECT TRY_CAST(1::uint2 AS float8)::text, TRY_CAST(1::uint2 AS float8)::text IS NOT DISTINCT FROM CAST(1::uint2 AS float8)::text, TRY_CAST(NULL::uint2 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS float8)::text, TRY_CAST(65535::uint2 AS float8)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS float8)::text, TRY_CAST(NULL::uint2 AS float8) IS NULL ----- -65535 true true - -# uint2 -> text -query TBB -SELECT TRY_CAST(1::uint2 AS text)::text, TRY_CAST(1::uint2 AS text)::text IS NOT DISTINCT FROM CAST(1::uint2 AS text)::text, TRY_CAST(NULL::uint2 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS text)::text, TRY_CAST(65535::uint2 AS text)::text IS NOT DISTINCT FROM CAST(65535::uint2 AS text)::text, TRY_CAST(NULL::uint2 AS text) IS NULL ----- -65535 true true - -## uint4 sources - -# uint4 -> uint2 -query TBB -SELECT TRY_CAST(1::uint4 AS uint2)::text, TRY_CAST(1::uint4 AS uint2)::text IS NOT DISTINCT FROM CAST(1::uint4 AS uint2)::text, TRY_CAST(NULL::uint4 AS uint2) IS NULL ----- -1 true true - -statement error -SELECT CAST(4294967295::uint4 AS uint2) - -query T -SELECT TRY_CAST(4294967295::uint4 AS uint2)::text ----- -NULL - -# uint4 -> uint8 -query TBB -SELECT TRY_CAST(1::uint4 AS uint8)::text, TRY_CAST(1::uint4 AS uint8)::text IS NOT DISTINCT FROM CAST(1::uint4 AS uint8)::text, TRY_CAST(NULL::uint4 AS uint8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS uint8)::text, TRY_CAST(4294967295::uint4 AS uint8)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS uint8)::text, TRY_CAST(NULL::uint4 AS uint8) IS NULL ----- -4294967295 true true - -# uint4 -> int2 -query TBB -SELECT TRY_CAST(1::uint4 AS int2)::text, TRY_CAST(1::uint4 AS int2)::text IS NOT DISTINCT FROM CAST(1::uint4 AS int2)::text, TRY_CAST(NULL::uint4 AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST(4294967295::uint4 AS int2) - -query T -SELECT TRY_CAST(4294967295::uint4 AS int2)::text ----- -NULL - -# uint4 -> int4 -query TBB -SELECT TRY_CAST(1::uint4 AS int4)::text, TRY_CAST(1::uint4 AS int4)::text IS NOT DISTINCT FROM CAST(1::uint4 AS int4)::text, TRY_CAST(NULL::uint4 AS int4) IS NULL ----- -1 true true - -statement error -SELECT CAST(4294967295::uint4 AS int4) - -query T -SELECT TRY_CAST(4294967295::uint4 AS int4)::text ----- -NULL - -# uint4 -> int8 -query TBB -SELECT TRY_CAST(1::uint4 AS int8)::text, TRY_CAST(1::uint4 AS int8)::text IS NOT DISTINCT FROM CAST(1::uint4 AS int8)::text, TRY_CAST(NULL::uint4 AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS int8)::text, TRY_CAST(4294967295::uint4 AS int8)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS int8)::text, TRY_CAST(NULL::uint4 AS int8) IS NULL ----- -4294967295 true true - -# uint4 -> numeric -query TBB -SELECT TRY_CAST(1::uint4 AS numeric)::text, TRY_CAST(1::uint4 AS numeric)::text IS NOT DISTINCT FROM CAST(1::uint4 AS numeric)::text, TRY_CAST(NULL::uint4 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS numeric)::text, TRY_CAST(4294967295::uint4 AS numeric)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS numeric)::text, TRY_CAST(NULL::uint4 AS numeric) IS NULL ----- -4294967295 true true - -# uint4 -> float4 -query TBB -SELECT TRY_CAST(1::uint4 AS float4)::text, TRY_CAST(1::uint4 AS float4)::text IS NOT DISTINCT FROM CAST(1::uint4 AS float4)::text, TRY_CAST(NULL::uint4 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS float4)::text, TRY_CAST(4294967295::uint4 AS float4)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS float4)::text, TRY_CAST(NULL::uint4 AS float4) IS NULL ----- -4294967300 true true - -# uint4 -> float8 -query TBB -SELECT TRY_CAST(1::uint4 AS float8)::text, TRY_CAST(1::uint4 AS float8)::text IS NOT DISTINCT FROM CAST(1::uint4 AS float8)::text, TRY_CAST(NULL::uint4 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS float8)::text, TRY_CAST(4294967295::uint4 AS float8)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS float8)::text, TRY_CAST(NULL::uint4 AS float8) IS NULL ----- -4294967295 true true - -# uint4 -> text -query TBB -SELECT TRY_CAST(1::uint4 AS text)::text, TRY_CAST(1::uint4 AS text)::text IS NOT DISTINCT FROM CAST(1::uint4 AS text)::text, TRY_CAST(NULL::uint4 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS text)::text, TRY_CAST(4294967295::uint4 AS text)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS text)::text, TRY_CAST(NULL::uint4 AS text) IS NULL ----- -4294967295 true true - -# uint4 -> mz_timestamp -query TBB -SELECT TRY_CAST(1::uint4 AS mz_timestamp)::text, TRY_CAST(1::uint4 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(1::uint4 AS mz_timestamp)::text, TRY_CAST(NULL::uint4 AS mz_timestamp) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS mz_timestamp)::text, TRY_CAST(4294967295::uint4 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS mz_timestamp)::text, TRY_CAST(NULL::uint4 AS mz_timestamp) IS NULL ----- -4294967295 true true - -## uint8 sources - -# uint8 -> uint2 -query TBB -SELECT TRY_CAST(1::uint8 AS uint2)::text, TRY_CAST(1::uint8 AS uint2)::text IS NOT DISTINCT FROM CAST(1::uint8 AS uint2)::text, TRY_CAST(NULL::uint8 AS uint2) IS NULL ----- -1 true true - -statement error -SELECT CAST(18446744073709551615::uint8 AS uint2) - -query T -SELECT TRY_CAST(18446744073709551615::uint8 AS uint2)::text ----- -NULL - -# uint8 -> uint4 -query TBB -SELECT TRY_CAST(1::uint8 AS uint4)::text, TRY_CAST(1::uint8 AS uint4)::text IS NOT DISTINCT FROM CAST(1::uint8 AS uint4)::text, TRY_CAST(NULL::uint8 AS uint4) IS NULL ----- -1 true true - -statement error -SELECT CAST(18446744073709551615::uint8 AS uint4) - -query T -SELECT TRY_CAST(18446744073709551615::uint8 AS uint4)::text ----- -NULL - -# uint8 -> int2 -query TBB -SELECT TRY_CAST(1::uint8 AS int2)::text, TRY_CAST(1::uint8 AS int2)::text IS NOT DISTINCT FROM CAST(1::uint8 AS int2)::text, TRY_CAST(NULL::uint8 AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST(18446744073709551615::uint8 AS int2) - -query T -SELECT TRY_CAST(18446744073709551615::uint8 AS int2)::text ----- -NULL - -# uint8 -> int4 -query TBB -SELECT TRY_CAST(1::uint8 AS int4)::text, TRY_CAST(1::uint8 AS int4)::text IS NOT DISTINCT FROM CAST(1::uint8 AS int4)::text, TRY_CAST(NULL::uint8 AS int4) IS NULL ----- -1 true true - -statement error -SELECT CAST(18446744073709551615::uint8 AS int4) - -query T -SELECT TRY_CAST(18446744073709551615::uint8 AS int4)::text ----- -NULL - -# uint8 -> int8 -query TBB -SELECT TRY_CAST(1::uint8 AS int8)::text, TRY_CAST(1::uint8 AS int8)::text IS NOT DISTINCT FROM CAST(1::uint8 AS int8)::text, TRY_CAST(NULL::uint8 AS int8) IS NULL ----- -1 true true - -statement error -SELECT CAST(18446744073709551615::uint8 AS int8) - -query T -SELECT TRY_CAST(18446744073709551615::uint8 AS int8)::text ----- -NULL - -# uint8 -> numeric -query TBB -SELECT TRY_CAST(1::uint8 AS numeric)::text, TRY_CAST(1::uint8 AS numeric)::text IS NOT DISTINCT FROM CAST(1::uint8 AS numeric)::text, TRY_CAST(NULL::uint8 AS numeric) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS numeric)::text, TRY_CAST(18446744073709551615::uint8 AS numeric)::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS numeric)::text, TRY_CAST(NULL::uint8 AS numeric) IS NULL ----- -18446744073709551615 true true - -# uint8 -> float4 -query TBB -SELECT TRY_CAST(1::uint8 AS float4)::text, TRY_CAST(1::uint8 AS float4)::text IS NOT DISTINCT FROM CAST(1::uint8 AS float4)::text, TRY_CAST(NULL::uint8 AS float4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS float4)::text, TRY_CAST(18446744073709551615::uint8 AS float4)::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS float4)::text, TRY_CAST(NULL::uint8 AS float4) IS NULL ----- -1.8446744e+19 true true - -# uint8 -> float8 -query TBB -SELECT TRY_CAST(1::uint8 AS float8)::text, TRY_CAST(1::uint8 AS float8)::text IS NOT DISTINCT FROM CAST(1::uint8 AS float8)::text, TRY_CAST(NULL::uint8 AS float8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS float8)::text, TRY_CAST(18446744073709551615::uint8 AS float8)::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS float8)::text, TRY_CAST(NULL::uint8 AS float8) IS NULL ----- -1.8446744073709552e+19 true true - -# uint8 -> text -query TBB -SELECT TRY_CAST(1::uint8 AS text)::text, TRY_CAST(1::uint8 AS text)::text IS NOT DISTINCT FROM CAST(1::uint8 AS text)::text, TRY_CAST(NULL::uint8 AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS text)::text, TRY_CAST(18446744073709551615::uint8 AS text)::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS text)::text, TRY_CAST(NULL::uint8 AS text) IS NULL ----- -18446744073709551615 true true - -# uint8 -> mz_timestamp -query TBB -SELECT TRY_CAST(1::uint8 AS mz_timestamp)::text, TRY_CAST(1::uint8 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(1::uint8 AS mz_timestamp)::text, TRY_CAST(NULL::uint8 AS mz_timestamp) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS mz_timestamp)::text, TRY_CAST(18446744073709551615::uint8 AS mz_timestamp)::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS mz_timestamp)::text, TRY_CAST(NULL::uint8 AS mz_timestamp) IS NULL ----- -18446744073709551615 true true - -## mz_timestamp sources - -# mz_timestamp -> text -query TBB -SELECT TRY_CAST(1::mz_timestamp AS text)::text, TRY_CAST(1::mz_timestamp AS text)::text IS NOT DISTINCT FROM CAST(1::mz_timestamp AS text)::text, TRY_CAST(NULL::mz_timestamp AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::mz_timestamp AS text)::text, TRY_CAST(18446744073709551615::mz_timestamp AS text)::text IS NOT DISTINCT FROM CAST(18446744073709551615::mz_timestamp AS text)::text, TRY_CAST(NULL::mz_timestamp AS text) IS NULL ----- -18446744073709551615 true true - -# mz_timestamp -> timestamp -query TBB -SELECT TRY_CAST(1::mz_timestamp AS timestamp)::text, TRY_CAST(1::mz_timestamp AS timestamp)::text IS NOT DISTINCT FROM CAST(1::mz_timestamp AS timestamp)::text, TRY_CAST(NULL::mz_timestamp AS timestamp) IS NULL ----- -1970-01-01␠00:00:00.001 true true - -statement error -SELECT CAST(18446744073709551615::mz_timestamp AS timestamp) - -query T -SELECT TRY_CAST(18446744073709551615::mz_timestamp AS timestamp)::text ----- -NULL - -# mz_timestamp -> timestamp(3) -query TBB -SELECT TRY_CAST(1::mz_timestamp AS timestamp(3))::text, TRY_CAST(1::mz_timestamp AS timestamp(3))::text IS NOT DISTINCT FROM CAST(1::mz_timestamp AS timestamp(3))::text, TRY_CAST(NULL::mz_timestamp AS timestamp(3)) IS NULL ----- -1970-01-01␠00:00:00.001 true true - -statement error -SELECT CAST(18446744073709551615::mz_timestamp AS timestamp(3)) - -query T -SELECT TRY_CAST(18446744073709551615::mz_timestamp AS timestamp(3))::text ----- -NULL - -# mz_timestamp -> timestamptz -query TBB -SELECT TRY_CAST(1::mz_timestamp AS timestamptz)::text, TRY_CAST(1::mz_timestamp AS timestamptz)::text IS NOT DISTINCT FROM CAST(1::mz_timestamp AS timestamptz)::text, TRY_CAST(NULL::mz_timestamp AS timestamptz) IS NULL ----- -1970-01-01␠00:00:00.001+00 true true - -statement error -SELECT CAST(18446744073709551615::mz_timestamp AS timestamptz) - -query T -SELECT TRY_CAST(18446744073709551615::mz_timestamp AS timestamptz)::text ----- -NULL - -## text sources - -# text -> mz_timestamp -query TBB -SELECT TRY_CAST('1' AS mz_timestamp)::text, TRY_CAST('1' AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('1' AS mz_timestamp)::text, TRY_CAST(NULL AS mz_timestamp) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS mz_timestamp) - -query T -SELECT TRY_CAST('abc' AS mz_timestamp)::text ----- -NULL - -statement error -SELECT CAST('' AS mz_timestamp) - -query T -SELECT TRY_CAST('' AS mz_timestamp)::text ----- -NULL - -# text -> bool -query TBB -SELECT TRY_CAST('true' AS bool)::text, TRY_CAST('true' AS bool)::text IS NOT DISTINCT FROM CAST('true' AS bool)::text, TRY_CAST(NULL AS bool) IS NULL ----- -true true true - -statement error -SELECT CAST('abc' AS bool) - -query T -SELECT TRY_CAST('abc' AS bool)::text ----- -NULL - -statement error -SELECT CAST('' AS bool) - -query T -SELECT TRY_CAST('' AS bool)::text ----- -NULL - -# text -> int2 -query TBB -SELECT TRY_CAST('1' AS int2)::text, TRY_CAST('1' AS int2)::text IS NOT DISTINCT FROM CAST('1' AS int2)::text, TRY_CAST(NULL AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS int2) - -query T -SELECT TRY_CAST('abc' AS int2)::text ----- -NULL - -statement error -SELECT CAST('' AS int2) - -query T -SELECT TRY_CAST('' AS int2)::text ----- -NULL - -# text -> int4 -query TBB -SELECT TRY_CAST('1' AS int4)::text, TRY_CAST('1' AS int4)::text IS NOT DISTINCT FROM CAST('1' AS int4)::text, TRY_CAST(NULL AS int4) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS int4) - -query T -SELECT TRY_CAST('abc' AS int4)::text ----- -NULL - -statement error -SELECT CAST('' AS int4) - -query T -SELECT TRY_CAST('' AS int4)::text ----- -NULL - -# text -> int8 -query TBB -SELECT TRY_CAST('1' AS int8)::text, TRY_CAST('1' AS int8)::text IS NOT DISTINCT FROM CAST('1' AS int8)::text, TRY_CAST(NULL AS int8) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS int8) - -query T -SELECT TRY_CAST('abc' AS int8)::text ----- -NULL - -statement error -SELECT CAST('' AS int8) - -query T -SELECT TRY_CAST('' AS int8)::text ----- -NULL - -# text -> uint2 -query TBB -SELECT TRY_CAST('1' AS uint2)::text, TRY_CAST('1' AS uint2)::text IS NOT DISTINCT FROM CAST('1' AS uint2)::text, TRY_CAST(NULL AS uint2) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS uint2) - -query T -SELECT TRY_CAST('abc' AS uint2)::text ----- -NULL - -statement error -SELECT CAST('' AS uint2) - -query T -SELECT TRY_CAST('' AS uint2)::text ----- -NULL - -# text -> uint4 -query TBB -SELECT TRY_CAST('1' AS uint4)::text, TRY_CAST('1' AS uint4)::text IS NOT DISTINCT FROM CAST('1' AS uint4)::text, TRY_CAST(NULL AS uint4) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS uint4) - -query T -SELECT TRY_CAST('abc' AS uint4)::text ----- -NULL - -statement error -SELECT CAST('' AS uint4) - -query T -SELECT TRY_CAST('' AS uint4)::text ----- -NULL - -# text -> uint8 -query TBB -SELECT TRY_CAST('1' AS uint8)::text, TRY_CAST('1' AS uint8)::text IS NOT DISTINCT FROM CAST('1' AS uint8)::text, TRY_CAST(NULL AS uint8) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS uint8) - -query T -SELECT TRY_CAST('abc' AS uint8)::text ----- -NULL - -statement error -SELECT CAST('' AS uint8) - -query T -SELECT TRY_CAST('' AS uint8)::text ----- -NULL - -# text -> oid -query TBB -SELECT TRY_CAST('1' AS oid)::text, TRY_CAST('1' AS oid)::text IS NOT DISTINCT FROM CAST('1' AS oid)::text, TRY_CAST(NULL AS oid) IS NULL ----- -1 true true - -statement error -SELECT CAST('abc' AS oid) - -query T -SELECT TRY_CAST('abc' AS oid)::text ----- -NULL - -statement error -SELECT CAST('' AS oid) - -query T -SELECT TRY_CAST('' AS oid)::text ----- -NULL - -# text -> float4 -query TBB -SELECT TRY_CAST('1.5' AS float4)::text, TRY_CAST('1.5' AS float4)::text IS NOT DISTINCT FROM CAST('1.5' AS float4)::text, TRY_CAST(NULL AS float4) IS NULL ----- -1.5 true true - -statement error -SELECT CAST('abc' AS float4) - -query T -SELECT TRY_CAST('abc' AS float4)::text ----- -NULL - -statement error -SELECT CAST('' AS float4) - -query T -SELECT TRY_CAST('' AS float4)::text ----- -NULL - -# text -> float8 -query TBB -SELECT TRY_CAST('1.5' AS float8)::text, TRY_CAST('1.5' AS float8)::text IS NOT DISTINCT FROM CAST('1.5' AS float8)::text, TRY_CAST(NULL AS float8) IS NULL ----- -1.5 true true - -statement error -SELECT CAST('abc' AS float8) - -query T -SELECT TRY_CAST('abc' AS float8)::text ----- -NULL - -statement error -SELECT CAST('' AS float8) - -query T -SELECT TRY_CAST('' AS float8)::text ----- -NULL - -# text -> numeric -query TBB -SELECT TRY_CAST('1.5' AS numeric)::text, TRY_CAST('1.5' AS numeric)::text IS NOT DISTINCT FROM CAST('1.5' AS numeric)::text, TRY_CAST(NULL AS numeric) IS NULL ----- -1.5 true true - -statement error -SELECT CAST('abc' AS numeric) - -query T -SELECT TRY_CAST('abc' AS numeric)::text ----- -NULL - -statement error -SELECT CAST('' AS numeric) - -query T -SELECT TRY_CAST('' AS numeric)::text ----- -NULL - -# text -> date -query TBB -SELECT TRY_CAST('2020-01-01' AS date)::text, TRY_CAST('2020-01-01' AS date)::text IS NOT DISTINCT FROM CAST('2020-01-01' AS date)::text, TRY_CAST(NULL AS date) IS NULL ----- -2020-01-01 true true - -statement error -SELECT CAST('abc' AS date) - -query T -SELECT TRY_CAST('abc' AS date)::text ----- -NULL - -statement error -SELECT CAST('' AS date) - -query T -SELECT TRY_CAST('' AS date)::text ----- -NULL - -# text -> time -query TBB -SELECT TRY_CAST('12:34:56' AS time)::text, TRY_CAST('12:34:56' AS time)::text IS NOT DISTINCT FROM CAST('12:34:56' AS time)::text, TRY_CAST(NULL AS time) IS NULL ----- -12:34:56 true true - -statement error -SELECT CAST('abc' AS time) - -query T -SELECT TRY_CAST('abc' AS time)::text ----- -NULL - -statement error -SELECT CAST('' AS time) - -query T -SELECT TRY_CAST('' AS time)::text ----- -NULL - -# text -> timestamp -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789' AS timestamp)::text, TRY_CAST('2020-01-01 12:34:56.789' AS timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789' AS timestamp)::text, TRY_CAST(NULL AS timestamp) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -statement error -SELECT CAST('abc' AS timestamp) - -query T -SELECT TRY_CAST('abc' AS timestamp)::text ----- -NULL - -statement error -SELECT CAST('' AS timestamp) - -query T -SELECT TRY_CAST('' AS timestamp)::text ----- -NULL - -# text -> timestamp(3) -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789' AS timestamp(3))::text, TRY_CAST('2020-01-01 12:34:56.789' AS timestamp(3))::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789' AS timestamp(3))::text, TRY_CAST(NULL AS timestamp(3)) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -statement error -SELECT CAST('abc' AS timestamp(3)) - -query T -SELECT TRY_CAST('abc' AS timestamp(3))::text ----- -NULL - -statement error -SELECT CAST('' AS timestamp(3)) - -query T -SELECT TRY_CAST('' AS timestamp(3))::text ----- -NULL - -# text -> timestamptz -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00' AS timestamptz)::text, TRY_CAST('2020-01-01 12:34:56.789+00' AS timestamptz)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00' AS timestamptz)::text, TRY_CAST(NULL AS timestamptz) IS NULL ----- -2020-01-01␠12:34:56.789+00 true true - -statement error -SELECT CAST('abc' AS timestamptz) - -query T -SELECT TRY_CAST('abc' AS timestamptz)::text ----- -NULL - -statement error -SELECT CAST('' AS timestamptz) - -query T -SELECT TRY_CAST('' AS timestamptz)::text ----- -NULL - -# text -> interval -query TBB -SELECT TRY_CAST('1 day' AS interval)::text, TRY_CAST('1 day' AS interval)::text IS NOT DISTINCT FROM CAST('1 day' AS interval)::text, TRY_CAST(NULL AS interval) IS NULL ----- -1␠day true true - -statement error -SELECT CAST('abc' AS interval) - -query T -SELECT TRY_CAST('abc' AS interval)::text ----- -NULL - -query TBB -SELECT TRY_CAST('' AS interval)::text, TRY_CAST('' AS interval)::text IS NOT DISTINCT FROM CAST('' AS interval)::text, TRY_CAST(NULL AS interval) IS NULL ----- -00:00:00 true true - -# text -> bytea -query TBB -SELECT TRY_CAST('\x0102' AS bytea)::text, TRY_CAST('\x0102' AS bytea)::text IS NOT DISTINCT FROM CAST('\x0102' AS bytea)::text, TRY_CAST(NULL AS bytea) IS NULL ----- -\x0102 true true - -query TBB -SELECT TRY_CAST('abc' AS bytea)::text, TRY_CAST('abc' AS bytea)::text IS NOT DISTINCT FROM CAST('abc' AS bytea)::text, TRY_CAST(NULL AS bytea) IS NULL ----- -\x616263 true true - -query TBB -SELECT TRY_CAST('' AS bytea)::text, TRY_CAST('' AS bytea)::text IS NOT DISTINCT FROM CAST('' AS bytea)::text, TRY_CAST(NULL AS bytea) IS NULL ----- -\x true true - -# text -> jsonb -query TBB -SELECT TRY_CAST('{"a":1}' AS jsonb)::text, TRY_CAST('{"a":1}' AS jsonb)::text IS NOT DISTINCT FROM CAST('{"a":1}' AS jsonb)::text, TRY_CAST(NULL AS jsonb) IS NULL ----- -{"a":1} true true - -statement error -SELECT CAST('abc' AS jsonb) - -query T -SELECT TRY_CAST('abc' AS jsonb)::text ----- -NULL - -statement error -SELECT CAST('' AS jsonb) - -query T -SELECT TRY_CAST('' AS jsonb)::text ----- -NULL - -# text -> uuid -query TBB -SELECT TRY_CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' AS uuid)::text, TRY_CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' AS uuid)::text IS NOT DISTINCT FROM CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' AS uuid)::text, TRY_CAST(NULL AS uuid) IS NULL ----- -a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 true true - -statement error -SELECT CAST('abc' AS uuid) - -query T -SELECT TRY_CAST('abc' AS uuid)::text ----- -NULL - -statement error -SELECT CAST('' AS uuid) - -query T -SELECT TRY_CAST('' AS uuid)::text ----- -NULL - -# text -> int8[] -query TBB -SELECT TRY_CAST('{1,2}' AS int8[])::text, TRY_CAST('{1,2}' AS int8[])::text IS NOT DISTINCT FROM CAST('{1,2}' AS int8[])::text, TRY_CAST(NULL AS int8[]) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST('abc' AS int8[]) - -query T -SELECT TRY_CAST('abc' AS int8[])::text ----- -NULL - -statement error -SELECT CAST('' AS int8[]) - -query T -SELECT TRY_CAST('' AS int8[])::text ----- -NULL - -# text -> int2[] -query TBB -SELECT TRY_CAST('{1,2}' AS int2[])::text, TRY_CAST('{1,2}' AS int2[])::text IS NOT DISTINCT FROM CAST('{1,2}' AS int2[])::text, TRY_CAST(NULL AS int2[]) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST('abc' AS int2[]) - -query T -SELECT TRY_CAST('abc' AS int2[])::text ----- -NULL - -statement error -SELECT CAST('' AS int2[]) - -query T -SELECT TRY_CAST('' AS int2[])::text ----- -NULL - -# text -> int8 list -query TBB -SELECT TRY_CAST('{1,2}' AS int8 list)::text, TRY_CAST('{1,2}' AS int8 list)::text IS NOT DISTINCT FROM CAST('{1,2}' AS int8 list)::text, TRY_CAST(NULL AS int8 list) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST('abc' AS int8 list) - -query T -SELECT TRY_CAST('abc' AS int8 list)::text ----- -NULL - -statement error -SELECT CAST('' AS int8 list) - -query T -SELECT TRY_CAST('' AS int8 list)::text ----- -NULL - -# text -> int2 list -query TBB -SELECT TRY_CAST('{1,2}' AS int2 list)::text, TRY_CAST('{1,2}' AS int2 list)::text IS NOT DISTINCT FROM CAST('{1,2}' AS int2 list)::text, TRY_CAST(NULL AS int2 list) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST('abc' AS int2 list) - -query T -SELECT TRY_CAST('abc' AS int2 list)::text ----- -NULL - -statement error -SELECT CAST('' AS int2 list) - -query T -SELECT TRY_CAST('' AS int2 list)::text ----- -NULL - -# text -> map[text=>int8] -query TBB -SELECT TRY_CAST('{a=>1}' AS map[text=>int8])::text, TRY_CAST('{a=>1}' AS map[text=>int8])::text IS NOT DISTINCT FROM CAST('{a=>1}' AS map[text=>int8])::text, TRY_CAST(NULL AS map[text=>int8]) IS NULL ----- -{a=>1} true true - -statement error -SELECT CAST('abc' AS map[text=>int8]) - -query T -SELECT TRY_CAST('abc' AS map[text=>int8])::text ----- -NULL - -statement error -SELECT CAST('' AS map[text=>int8]) - -query T -SELECT TRY_CAST('' AS map[text=>int8])::text ----- -NULL - -# text -> int4range -query TBB -SELECT TRY_CAST('[1,3)' AS int4range)::text, TRY_CAST('[1,3)' AS int4range)::text IS NOT DISTINCT FROM CAST('[1,3)' AS int4range)::text, TRY_CAST(NULL AS int4range) IS NULL ----- -[1,3) true true - -statement error -SELECT CAST('abc' AS int4range) - -query T -SELECT TRY_CAST('abc' AS int4range)::text ----- -NULL - -statement error -SELECT CAST('' AS int4range) - -query T -SELECT TRY_CAST('' AS int4range)::text ----- -NULL - -# text -> int2vector -query TBB -SELECT TRY_CAST('1 2' AS int2vector)::text, TRY_CAST('1 2' AS int2vector)::text IS NOT DISTINCT FROM CAST('1 2' AS int2vector)::text, TRY_CAST(NULL AS int2vector) IS NULL ----- -1␠2 true true - -statement error -SELECT CAST('abc' AS int2vector) - -query T -SELECT TRY_CAST('abc' AS int2vector)::text ----- -NULL - -query TBB -SELECT TRY_CAST('' AS int2vector)::text, TRY_CAST('' AS int2vector)::text IS NOT DISTINCT FROM CAST('' AS int2vector)::text, TRY_CAST(NULL AS int2vector) IS NULL ----- -(empty) true true - -# text -> char(3) -query TBB -SELECT TRY_CAST('abc' AS char(3))::text, TRY_CAST('abc' AS char(3))::text IS NOT DISTINCT FROM CAST('abc' AS char(3))::text, TRY_CAST(NULL AS char(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc' AS char(3))::text, TRY_CAST('abc' AS char(3))::text IS NOT DISTINCT FROM CAST('abc' AS char(3))::text, TRY_CAST(NULL AS char(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('' AS char(3))::text, TRY_CAST('' AS char(3))::text IS NOT DISTINCT FROM CAST('' AS char(3))::text, TRY_CAST(NULL AS char(3)) IS NULL ----- -(empty) true true - -# text -> varchar(3) -query TBB -SELECT TRY_CAST('abc' AS varchar(3))::text, TRY_CAST('abc' AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc' AS varchar(3))::text, TRY_CAST(NULL AS varchar(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc' AS varchar(3))::text, TRY_CAST('abc' AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc' AS varchar(3))::text, TRY_CAST(NULL AS varchar(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('' AS varchar(3))::text, TRY_CAST('' AS varchar(3))::text IS NOT DISTINCT FROM CAST('' AS varchar(3))::text, TRY_CAST(NULL AS varchar(3)) IS NULL ----- -(empty) true true - -# text -> "char" -query TBB -SELECT TRY_CAST('a' AS "char")::text, TRY_CAST('a' AS "char")::text IS NOT DISTINCT FROM CAST('a' AS "char")::text, TRY_CAST(NULL AS "char") IS NULL ----- -a true true - -query TBB -SELECT TRY_CAST('abc' AS "char")::text, TRY_CAST('abc' AS "char")::text IS NOT DISTINCT FROM CAST('abc' AS "char")::text, TRY_CAST(NULL AS "char") IS NULL ----- -a true true - -query TBB -SELECT TRY_CAST('' AS "char")::text, TRY_CAST('' AS "char")::text IS NOT DISTINCT FROM CAST('' AS "char")::text, TRY_CAST(NULL AS "char") IS NULL ----- - true true - -# text -> name -query TBB -SELECT TRY_CAST('abc' AS name)::text, TRY_CAST('abc' AS name)::text IS NOT DISTINCT FROM CAST('abc' AS name)::text, TRY_CAST(NULL AS name) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc' AS name)::text, TRY_CAST('abc' AS name)::text IS NOT DISTINCT FROM CAST('abc' AS name)::text, TRY_CAST(NULL AS name) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('' AS name)::text, TRY_CAST('' AS name)::text IS NOT DISTINCT FROM CAST('' AS name)::text, TRY_CAST(NULL AS name) IS NULL ----- -(empty) true true - -## numeric sources - -# numeric -> mz_timestamp -statement error -SELECT CAST(1.5::numeric AS mz_timestamp) - -query T -SELECT TRY_CAST(1.5::numeric AS mz_timestamp)::text ----- -NULL - -statement error -SELECT CAST(1e38::numeric AS mz_timestamp) - -query T -SELECT TRY_CAST(1e38::numeric AS mz_timestamp)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::numeric AS mz_timestamp) - -query T -SELECT TRY_CAST((-1.5)::numeric AS mz_timestamp)::text ----- -NULL - -statement error -SELECT CAST('NaN'::numeric AS mz_timestamp) - -query T -SELECT TRY_CAST('NaN'::numeric AS mz_timestamp)::text ----- -NULL - -statement error -SELECT CAST(123.456::numeric AS mz_timestamp) - -query T -SELECT TRY_CAST(123.456::numeric AS mz_timestamp)::text ----- -NULL - -# numeric -> numeric -query TBB -SELECT TRY_CAST(1.5::numeric AS numeric)::text, TRY_CAST(1.5::numeric AS numeric)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS numeric)::text, TRY_CAST(NULL::numeric AS numeric) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST(1e38::numeric AS numeric)::text, TRY_CAST(1e38::numeric AS numeric)::text IS NOT DISTINCT FROM CAST(1e38::numeric AS numeric)::text, TRY_CAST(NULL::numeric AS numeric) IS NULL ----- -100000000000000000000000000000000000000 true true - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS numeric)::text, TRY_CAST((-1.5)::numeric AS numeric)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS numeric)::text, TRY_CAST(NULL::numeric AS numeric) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::numeric AS numeric)::text, TRY_CAST('NaN'::numeric AS numeric)::text IS NOT DISTINCT FROM CAST('NaN'::numeric AS numeric)::text, TRY_CAST(NULL::numeric AS numeric) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST(123.456::numeric AS numeric)::text, TRY_CAST(123.456::numeric AS numeric)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS numeric)::text, TRY_CAST(NULL::numeric AS numeric) IS NULL ----- -123.456 true true - -# numeric -> float4 -query TBB -SELECT TRY_CAST(1.5::numeric AS float4)::text, TRY_CAST(1.5::numeric AS float4)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS float4)::text, TRY_CAST(NULL::numeric AS float4) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST(1e38::numeric AS float4)::text, TRY_CAST(1e38::numeric AS float4)::text IS NOT DISTINCT FROM CAST(1e38::numeric AS float4)::text, TRY_CAST(NULL::numeric AS float4) IS NULL ----- -1e+38 true true - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS float4)::text, TRY_CAST((-1.5)::numeric AS float4)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS float4)::text, TRY_CAST(NULL::numeric AS float4) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::numeric AS float4)::text, TRY_CAST('NaN'::numeric AS float4)::text IS NOT DISTINCT FROM CAST('NaN'::numeric AS float4)::text, TRY_CAST(NULL::numeric AS float4) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST(123.456::numeric AS float4)::text, TRY_CAST(123.456::numeric AS float4)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS float4)::text, TRY_CAST(NULL::numeric AS float4) IS NULL ----- -123.456 true true - -# numeric -> float8 -query TBB -SELECT TRY_CAST(1.5::numeric AS float8)::text, TRY_CAST(1.5::numeric AS float8)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS float8)::text, TRY_CAST(NULL::numeric AS float8) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST(1e38::numeric AS float8)::text, TRY_CAST(1e38::numeric AS float8)::text IS NOT DISTINCT FROM CAST(1e38::numeric AS float8)::text, TRY_CAST(NULL::numeric AS float8) IS NULL ----- -1e+38 true true - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS float8)::text, TRY_CAST((-1.5)::numeric AS float8)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS float8)::text, TRY_CAST(NULL::numeric AS float8) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::numeric AS float8)::text, TRY_CAST('NaN'::numeric AS float8)::text IS NOT DISTINCT FROM CAST('NaN'::numeric AS float8)::text, TRY_CAST(NULL::numeric AS float8) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST(123.456::numeric AS float8)::text, TRY_CAST(123.456::numeric AS float8)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS float8)::text, TRY_CAST(NULL::numeric AS float8) IS NULL ----- -123.456 true true - -# numeric -> int2 -query TBB -SELECT TRY_CAST(1.5::numeric AS int2)::text, TRY_CAST(1.5::numeric AS int2)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS int2)::text, TRY_CAST(NULL::numeric AS int2) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS int2) - -query T -SELECT TRY_CAST(1e38::numeric AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS int2)::text, TRY_CAST((-1.5)::numeric AS int2)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS int2)::text, TRY_CAST(NULL::numeric AS int2) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::numeric AS int2) - -query T -SELECT TRY_CAST('NaN'::numeric AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS int2)::text, TRY_CAST(123.456::numeric AS int2)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS int2)::text, TRY_CAST(NULL::numeric AS int2) IS NULL ----- -123 true true - -# numeric -> int4 -query TBB -SELECT TRY_CAST(1.5::numeric AS int4)::text, TRY_CAST(1.5::numeric AS int4)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS int4)::text, TRY_CAST(NULL::numeric AS int4) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS int4) - -query T -SELECT TRY_CAST(1e38::numeric AS int4)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS int4)::text, TRY_CAST((-1.5)::numeric AS int4)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS int4)::text, TRY_CAST(NULL::numeric AS int4) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::numeric AS int4) - -query T -SELECT TRY_CAST('NaN'::numeric AS int4)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS int4)::text, TRY_CAST(123.456::numeric AS int4)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS int4)::text, TRY_CAST(NULL::numeric AS int4) IS NULL ----- -123 true true - -# numeric -> int8 -query TBB -SELECT TRY_CAST(1.5::numeric AS int8)::text, TRY_CAST(1.5::numeric AS int8)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS int8)::text, TRY_CAST(NULL::numeric AS int8) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS int8) - -query T -SELECT TRY_CAST(1e38::numeric AS int8)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS int8)::text, TRY_CAST((-1.5)::numeric AS int8)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS int8)::text, TRY_CAST(NULL::numeric AS int8) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::numeric AS int8) - -query T -SELECT TRY_CAST('NaN'::numeric AS int8)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS int8)::text, TRY_CAST(123.456::numeric AS int8)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS int8)::text, TRY_CAST(NULL::numeric AS int8) IS NULL ----- -123 true true - -# numeric -> uint2 -query TBB -SELECT TRY_CAST(1.5::numeric AS uint2)::text, TRY_CAST(1.5::numeric AS uint2)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS uint2)::text, TRY_CAST(NULL::numeric AS uint2) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS uint2) - -query T -SELECT TRY_CAST(1e38::numeric AS uint2)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::numeric AS uint2) - -query T -SELECT TRY_CAST((-1.5)::numeric AS uint2)::text ----- -NULL - -statement error -SELECT CAST('NaN'::numeric AS uint2) - -query T -SELECT TRY_CAST('NaN'::numeric AS uint2)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS uint2)::text, TRY_CAST(123.456::numeric AS uint2)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS uint2)::text, TRY_CAST(NULL::numeric AS uint2) IS NULL ----- -123 true true - -# numeric -> uint4 -query TBB -SELECT TRY_CAST(1.5::numeric AS uint4)::text, TRY_CAST(1.5::numeric AS uint4)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS uint4)::text, TRY_CAST(NULL::numeric AS uint4) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS uint4) - -query T -SELECT TRY_CAST(1e38::numeric AS uint4)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::numeric AS uint4) - -query T -SELECT TRY_CAST((-1.5)::numeric AS uint4)::text ----- -NULL - -statement error -SELECT CAST('NaN'::numeric AS uint4) - -query T -SELECT TRY_CAST('NaN'::numeric AS uint4)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS uint4)::text, TRY_CAST(123.456::numeric AS uint4)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS uint4)::text, TRY_CAST(NULL::numeric AS uint4) IS NULL ----- -123 true true - -# numeric -> uint8 -query TBB -SELECT TRY_CAST(1.5::numeric AS uint8)::text, TRY_CAST(1.5::numeric AS uint8)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS uint8)::text, TRY_CAST(NULL::numeric AS uint8) IS NULL ----- -2 true true - -statement error -SELECT CAST(1e38::numeric AS uint8) - -query T -SELECT TRY_CAST(1e38::numeric AS uint8)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::numeric AS uint8) - -query T -SELECT TRY_CAST((-1.5)::numeric AS uint8)::text ----- -NULL - -statement error -SELECT CAST('NaN'::numeric AS uint8) - -query T -SELECT TRY_CAST('NaN'::numeric AS uint8)::text ----- -NULL - -query TBB -SELECT TRY_CAST(123.456::numeric AS uint8)::text, TRY_CAST(123.456::numeric AS uint8)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS uint8)::text, TRY_CAST(NULL::numeric AS uint8) IS NULL ----- -123 true true - -# numeric -> text -query TBB -SELECT TRY_CAST(1.5::numeric AS text)::text, TRY_CAST(1.5::numeric AS text)::text IS NOT DISTINCT FROM CAST(1.5::numeric AS text)::text, TRY_CAST(NULL::numeric AS text) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST(1e38::numeric AS text)::text, TRY_CAST(1e38::numeric AS text)::text IS NOT DISTINCT FROM CAST(1e38::numeric AS text)::text, TRY_CAST(NULL::numeric AS text) IS NULL ----- -100000000000000000000000000000000000000 true true - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS text)::text, TRY_CAST((-1.5)::numeric AS text)::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS text)::text, TRY_CAST(NULL::numeric AS text) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::numeric AS text)::text, TRY_CAST('NaN'::numeric AS text)::text IS NOT DISTINCT FROM CAST('NaN'::numeric AS text)::text, TRY_CAST(NULL::numeric AS text) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST(123.456::numeric AS text)::text, TRY_CAST(123.456::numeric AS text)::text IS NOT DISTINCT FROM CAST(123.456::numeric AS text)::text, TRY_CAST(NULL::numeric AS text) IS NULL ----- -123.456 true true - -## timestamp sources - -# timestamp -> mz_timestamp -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS mz_timestamp)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS mz_timestamp)::text, TRY_CAST(NULL::timestamp AS mz_timestamp) IS NULL ----- -1577882096789 true true - -statement error -SELECT CAST('1969-01-01'::timestamp AS mz_timestamp) - -query T -SELECT TRY_CAST('1969-01-01'::timestamp AS mz_timestamp)::text ----- -NULL - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS mz_timestamp)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS mz_timestamp)::text, TRY_CAST(NULL::timestamp AS mz_timestamp) IS NULL ----- -8210266876799000 true true - -# timestamp -> date -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS date)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS date)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS date)::text, TRY_CAST(NULL::timestamp AS date) IS NULL ----- -2020-01-01 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS date)::text, TRY_CAST('1969-01-01'::timestamp AS date)::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS date)::text, TRY_CAST(NULL::timestamp AS date) IS NULL ----- -1969-01-01 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS date)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS date)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS date)::text, TRY_CAST(NULL::timestamp AS date) IS NULL ----- -262142-12-31 true true - -# timestamp -> timestamptz -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamptz)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamptz)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS timestamptz)::text, TRY_CAST(NULL::timestamp AS timestamptz) IS NULL ----- -2020-01-01␠12:34:56.789+00 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS timestamptz)::text, TRY_CAST('1969-01-01'::timestamp AS timestamptz)::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS timestamptz)::text, TRY_CAST(NULL::timestamp AS timestamptz) IS NULL ----- -1969-01-01␠00:00:00+00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamptz)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamptz)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS timestamptz)::text, TRY_CAST(NULL::timestamp AS timestamptz) IS NULL ----- -262142-12-31␠23:59:59+00 true true - -# timestamp -> timestamp -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp)::text, TRY_CAST(NULL::timestamp AS timestamp) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS timestamp)::text, TRY_CAST('1969-01-01'::timestamp AS timestamp)::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS timestamp)::text, TRY_CAST(NULL::timestamp AS timestamp) IS NULL ----- -1969-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamp)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS timestamp)::text, TRY_CAST(NULL::timestamp AS timestamp) IS NULL ----- -262142-12-31␠23:59:59 true true - -# timestamp -> timestamp(3) -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp(3))::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp(3))::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS timestamp(3))::text, TRY_CAST(NULL::timestamp AS timestamp(3)) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS timestamp(3))::text, TRY_CAST('1969-01-01'::timestamp AS timestamp(3))::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS timestamp(3))::text, TRY_CAST(NULL::timestamp AS timestamp(3)) IS NULL ----- -1969-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamp(3))::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS timestamp(3))::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS timestamp(3))::text, TRY_CAST(NULL::timestamp AS timestamp(3)) IS NULL ----- -262142-12-31␠23:59:59 true true - -# timestamp -> time -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS time)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS time)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS time)::text, TRY_CAST(NULL::timestamp AS time) IS NULL ----- -12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS time)::text, TRY_CAST('1969-01-01'::timestamp AS time)::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS time)::text, TRY_CAST(NULL::timestamp AS time) IS NULL ----- -00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS time)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS time)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS time)::text, TRY_CAST(NULL::timestamp AS time) IS NULL ----- -23:59:59 true true - -# timestamp -> text -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS text)::text, TRY_CAST('2020-01-01 12:34:56.789'::timestamp AS text)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789'::timestamp AS text)::text, TRY_CAST(NULL::timestamp AS text) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01'::timestamp AS text)::text, TRY_CAST('1969-01-01'::timestamp AS text)::text IS NOT DISTINCT FROM CAST('1969-01-01'::timestamp AS text)::text, TRY_CAST(NULL::timestamp AS text) IS NULL ----- -1969-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59'::timestamp AS text)::text, TRY_CAST('262142-12-31 23:59:59'::timestamp AS text)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59'::timestamp AS text)::text, TRY_CAST(NULL::timestamp AS text) IS NULL ----- -262142-12-31␠23:59:59 true true - -## timestamp with time zone sources - -# timestamp with time zone -> mz_timestamp -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS mz_timestamp)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS mz_timestamp)::text, TRY_CAST(NULL::timestamptz AS mz_timestamp) IS NULL ----- -1577882096789 true true - -statement error -SELECT CAST('1969-01-01 00:00:00+00'::timestamptz AS mz_timestamp) - -query T -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS mz_timestamp)::text ----- -NULL - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS mz_timestamp)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS mz_timestamp)::text, TRY_CAST(NULL::timestamptz AS mz_timestamp) IS NULL ----- -8210266876799000 true true - -# timestamp with time zone -> date -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS date)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS date)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS date)::text, TRY_CAST(NULL::timestamptz AS date) IS NULL ----- -2020-01-01 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS date)::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS date)::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS date)::text, TRY_CAST(NULL::timestamptz AS date) IS NULL ----- -1969-01-01 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS date)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS date)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS date)::text, TRY_CAST(NULL::timestamptz AS date) IS NULL ----- -262142-12-31 true true - -# timestamp with time zone -> timestamp -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp)::text, TRY_CAST(NULL::timestamptz AS timestamp) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp)::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp)::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp)::text, TRY_CAST(NULL::timestamptz AS timestamp) IS NULL ----- -1969-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp)::text, TRY_CAST(NULL::timestamptz AS timestamp) IS NULL ----- -262142-12-31␠23:59:59 true true - -# timestamp with time zone -> timestamp(3) -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp(3))::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp(3))::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamp(3))::text, TRY_CAST(NULL::timestamptz AS timestamp(3)) IS NULL ----- -2020-01-01␠12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp(3))::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp(3))::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamp(3))::text, TRY_CAST(NULL::timestamptz AS timestamp(3)) IS NULL ----- -1969-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp(3))::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp(3))::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamp(3))::text, TRY_CAST(NULL::timestamptz AS timestamp(3)) IS NULL ----- -262142-12-31␠23:59:59 true true - -# timestamp with time zone -> timestamptz -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamptz)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamptz)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS timestamptz)::text, TRY_CAST(NULL::timestamptz AS timestamptz) IS NULL ----- -2020-01-01␠12:34:56.789+00 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamptz)::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamptz)::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS timestamptz)::text, TRY_CAST(NULL::timestamptz AS timestamptz) IS NULL ----- -1969-01-01␠00:00:00+00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamptz)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamptz)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS timestamptz)::text, TRY_CAST(NULL::timestamptz AS timestamptz) IS NULL ----- -262142-12-31␠23:59:59+00 true true - -# timestamp with time zone -> time -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS time)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS time)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS time)::text, TRY_CAST(NULL::timestamptz AS time) IS NULL ----- -12:34:56.789 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS time)::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS time)::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS time)::text, TRY_CAST(NULL::timestamptz AS time) IS NULL ----- -00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS time)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS time)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS time)::text, TRY_CAST(NULL::timestamptz AS time) IS NULL ----- -23:59:59 true true - -# timestamp with time zone -> text -query TBB -SELECT TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS text)::text, TRY_CAST('2020-01-01 12:34:56.789+00'::timestamptz AS text)::text IS NOT DISTINCT FROM CAST('2020-01-01 12:34:56.789+00'::timestamptz AS text)::text, TRY_CAST(NULL::timestamptz AS text) IS NULL ----- -2020-01-01␠12:34:56.789+00 true true - -query TBB -SELECT TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS text)::text, TRY_CAST('1969-01-01 00:00:00+00'::timestamptz AS text)::text IS NOT DISTINCT FROM CAST('1969-01-01 00:00:00+00'::timestamptz AS text)::text, TRY_CAST(NULL::timestamptz AS text) IS NULL ----- -1969-01-01␠00:00:00+00 true true - -query TBB -SELECT TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS text)::text, TRY_CAST('262142-12-31 23:59:59+00'::timestamptz AS text)::text IS NOT DISTINCT FROM CAST('262142-12-31 23:59:59+00'::timestamptz AS text)::text, TRY_CAST(NULL::timestamptz AS text) IS NULL ----- -262142-12-31␠23:59:59+00 true true - -## date sources - -# date -> mz_timestamp -query TBB -SELECT TRY_CAST('2020-01-01'::date AS mz_timestamp)::text, TRY_CAST('2020-01-01'::date AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01'::date AS mz_timestamp)::text, TRY_CAST(NULL::date AS mz_timestamp) IS NULL ----- -1577836800000 true true - -query TBB -SELECT TRY_CAST('262142-12-31'::date AS mz_timestamp)::text, TRY_CAST('262142-12-31'::date AS mz_timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31'::date AS mz_timestamp)::text, TRY_CAST(NULL::date AS mz_timestamp) IS NULL ----- -8210266790400000 true true - -# date -> timestamp -query TBB -SELECT TRY_CAST('2020-01-01'::date AS timestamp)::text, TRY_CAST('2020-01-01'::date AS timestamp)::text IS NOT DISTINCT FROM CAST('2020-01-01'::date AS timestamp)::text, TRY_CAST(NULL::date AS timestamp) IS NULL ----- -2020-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31'::date AS timestamp)::text, TRY_CAST('262142-12-31'::date AS timestamp)::text IS NOT DISTINCT FROM CAST('262142-12-31'::date AS timestamp)::text, TRY_CAST(NULL::date AS timestamp) IS NULL ----- -262142-12-31␠00:00:00 true true - -# date -> timestamp(3) -query TBB -SELECT TRY_CAST('2020-01-01'::date AS timestamp(3))::text, TRY_CAST('2020-01-01'::date AS timestamp(3))::text IS NOT DISTINCT FROM CAST('2020-01-01'::date AS timestamp(3))::text, TRY_CAST(NULL::date AS timestamp(3)) IS NULL ----- -2020-01-01␠00:00:00 true true - -query TBB -SELECT TRY_CAST('262142-12-31'::date AS timestamp(3))::text, TRY_CAST('262142-12-31'::date AS timestamp(3))::text IS NOT DISTINCT FROM CAST('262142-12-31'::date AS timestamp(3))::text, TRY_CAST(NULL::date AS timestamp(3)) IS NULL ----- -262142-12-31␠00:00:00 true true - -# date -> timestamptz -query TBB -SELECT TRY_CAST('2020-01-01'::date AS timestamptz)::text, TRY_CAST('2020-01-01'::date AS timestamptz)::text IS NOT DISTINCT FROM CAST('2020-01-01'::date AS timestamptz)::text, TRY_CAST(NULL::date AS timestamptz) IS NULL ----- -2020-01-01␠00:00:00+00 true true - -query TBB -SELECT TRY_CAST('262142-12-31'::date AS timestamptz)::text, TRY_CAST('262142-12-31'::date AS timestamptz)::text IS NOT DISTINCT FROM CAST('262142-12-31'::date AS timestamptz)::text, TRY_CAST(NULL::date AS timestamptz) IS NULL ----- -262142-12-31␠00:00:00+00 true true - -# date -> text -query TBB -SELECT TRY_CAST('2020-01-01'::date AS text)::text, TRY_CAST('2020-01-01'::date AS text)::text IS NOT DISTINCT FROM CAST('2020-01-01'::date AS text)::text, TRY_CAST(NULL::date AS text) IS NULL ----- -2020-01-01 true true - -query TBB -SELECT TRY_CAST('262142-12-31'::date AS text)::text, TRY_CAST('262142-12-31'::date AS text)::text IS NOT DISTINCT FROM CAST('262142-12-31'::date AS text)::text, TRY_CAST(NULL::date AS text) IS NULL ----- -262142-12-31 true true - -## oid sources - -# oid -> int4 -query TBB -SELECT TRY_CAST(1::oid AS int4)::text, TRY_CAST(1::oid AS int4)::text IS NOT DISTINCT FROM CAST(1::oid AS int4)::text, TRY_CAST(NULL::oid AS int4) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS int4)::text, TRY_CAST(4294967295::oid AS int4)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS int4)::text, TRY_CAST(NULL::oid AS int4) IS NULL ----- --1 true true - -# oid -> int8 -query TBB -SELECT TRY_CAST(1::oid AS int8)::text, TRY_CAST(1::oid AS int8)::text IS NOT DISTINCT FROM CAST(1::oid AS int8)::text, TRY_CAST(NULL::oid AS int8) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS int8)::text, TRY_CAST(4294967295::oid AS int8)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS int8)::text, TRY_CAST(NULL::oid AS int8) IS NULL ----- -4294967295 true true - -# oid -> text -query TBB -SELECT TRY_CAST(1::oid AS text)::text, TRY_CAST(1::oid AS text)::text IS NOT DISTINCT FROM CAST(1::oid AS text)::text, TRY_CAST(NULL::oid AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS text)::text, TRY_CAST(4294967295::oid AS text)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS text)::text, TRY_CAST(NULL::oid AS text) IS NULL ----- -4294967295 true true - -# oid -> regclass -query TBB -SELECT TRY_CAST(1::oid AS regclass)::text, TRY_CAST(1::oid AS regclass)::text IS NOT DISTINCT FROM CAST(1::oid AS regclass)::text, TRY_CAST(NULL::oid AS regclass) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS regclass)::text, TRY_CAST(4294967295::oid AS regclass)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS regclass)::text, TRY_CAST(NULL::oid AS regclass) IS NULL ----- -4294967295 true true - -# oid -> regproc -query TBB -SELECT TRY_CAST(1::oid AS regproc)::text, TRY_CAST(1::oid AS regproc)::text IS NOT DISTINCT FROM CAST(1::oid AS regproc)::text, TRY_CAST(NULL::oid AS regproc) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS regproc)::text, TRY_CAST(4294967295::oid AS regproc)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS regproc)::text, TRY_CAST(NULL::oid AS regproc) IS NULL ----- -4294967295 true true - -# oid -> regtype -query TBB -SELECT TRY_CAST(1::oid AS regtype)::text, TRY_CAST(1::oid AS regtype)::text IS NOT DISTINCT FROM CAST(1::oid AS regtype)::text, TRY_CAST(NULL::oid AS regtype) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid AS regtype)::text, TRY_CAST(4294967295::oid AS regtype)::text IS NOT DISTINCT FROM CAST(4294967295::oid AS regtype)::text, TRY_CAST(NULL::oid AS regtype) IS NULL ----- -4294967295 true true - -## regclass sources - -# regclass -> oid -query TBB -SELECT TRY_CAST(1::regclass AS oid)::text, TRY_CAST(1::regclass AS oid)::text IS NOT DISTINCT FROM CAST(1::regclass AS oid)::text, TRY_CAST(NULL::regclass AS oid) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid::regclass AS oid)::text, TRY_CAST(4294967295::oid::regclass AS oid)::text IS NOT DISTINCT FROM CAST(4294967295::oid::regclass AS oid)::text, TRY_CAST(NULL::regclass AS oid) IS NULL ----- -4294967295 true true - -## regproc sources - -# regproc -> oid -query TBB -SELECT TRY_CAST(1::regproc AS oid)::text, TRY_CAST(1::regproc AS oid)::text IS NOT DISTINCT FROM CAST(1::regproc AS oid)::text, TRY_CAST(NULL::regproc AS oid) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid::regproc AS oid)::text, TRY_CAST(4294967295::oid::regproc AS oid)::text IS NOT DISTINCT FROM CAST(4294967295::oid::regproc AS oid)::text, TRY_CAST(NULL::regproc AS oid) IS NULL ----- -4294967295 true true - -## regtype sources - -# regtype -> oid -query TBB -SELECT TRY_CAST(1::regtype AS oid)::text, TRY_CAST(1::regtype AS oid)::text IS NOT DISTINCT FROM CAST(1::regtype AS oid)::text, TRY_CAST(NULL::regtype AS oid) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST(4294967295::oid::regtype AS oid)::text, TRY_CAST(4294967295::oid::regtype AS oid)::text IS NOT DISTINCT FROM CAST(4294967295::oid::regtype AS oid)::text, TRY_CAST(NULL::regtype AS oid) IS NULL ----- -4294967295 true true - -## real sources - -# real -> int2 -query TBB -SELECT TRY_CAST(1.5::float4 AS int2)::text, TRY_CAST(1.5::float4 AS int2)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS int2)::text, TRY_CAST(NULL::float4 AS int2) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS int2) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS int2)::text, TRY_CAST((-1.5)::float4 AS int2)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS int2)::text, TRY_CAST(NULL::float4 AS int2) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float4 AS int2) - -query T -SELECT TRY_CAST('NaN'::float4 AS int2)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS int2) - -query T -SELECT TRY_CAST('Infinity'::float4 AS int2)::text ----- -NULL - -# real -> int4 -query TBB -SELECT TRY_CAST(1.5::float4 AS int4)::text, TRY_CAST(1.5::float4 AS int4)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS int4)::text, TRY_CAST(NULL::float4 AS int4) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS int4) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS int4)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS int4)::text, TRY_CAST((-1.5)::float4 AS int4)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS int4)::text, TRY_CAST(NULL::float4 AS int4) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float4 AS int4) - -query T -SELECT TRY_CAST('NaN'::float4 AS int4)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS int4) - -query T -SELECT TRY_CAST('Infinity'::float4 AS int4)::text ----- -NULL - -# real -> int8 -query TBB -SELECT TRY_CAST(1.5::float4 AS int8)::text, TRY_CAST(1.5::float4 AS int8)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS int8)::text, TRY_CAST(NULL::float4 AS int8) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS int8) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS int8)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS int8)::text, TRY_CAST((-1.5)::float4 AS int8)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS int8)::text, TRY_CAST(NULL::float4 AS int8) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float4 AS int8) - -query T -SELECT TRY_CAST('NaN'::float4 AS int8)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS int8) - -query T -SELECT TRY_CAST('Infinity'::float4 AS int8)::text ----- -NULL - -# real -> uint2 -query TBB -SELECT TRY_CAST(1.5::float4 AS uint2)::text, TRY_CAST(1.5::float4 AS uint2)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS uint2)::text, TRY_CAST(NULL::float4 AS uint2) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS uint2) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS uint2)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float4 AS uint2) - -query T -SELECT TRY_CAST((-1.5)::float4 AS uint2)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float4 AS uint2) - -query T -SELECT TRY_CAST('NaN'::float4 AS uint2)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS uint2) - -query T -SELECT TRY_CAST('Infinity'::float4 AS uint2)::text ----- -NULL - -# real -> uint4 -query TBB -SELECT TRY_CAST(1.5::float4 AS uint4)::text, TRY_CAST(1.5::float4 AS uint4)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS uint4)::text, TRY_CAST(NULL::float4 AS uint4) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS uint4) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS uint4)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float4 AS uint4) - -query T -SELECT TRY_CAST((-1.5)::float4 AS uint4)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float4 AS uint4) - -query T -SELECT TRY_CAST('NaN'::float4 AS uint4)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS uint4) - -query T -SELECT TRY_CAST('Infinity'::float4 AS uint4)::text ----- -NULL - -# real -> uint8 -query TBB -SELECT TRY_CAST(1.5::float4 AS uint8)::text, TRY_CAST(1.5::float4 AS uint8)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS uint8)::text, TRY_CAST(NULL::float4 AS uint8) IS NULL ----- -2 true true - -statement error -SELECT CAST('3.4e38'::float4 AS uint8) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS uint8)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float4 AS uint8) - -query T -SELECT TRY_CAST((-1.5)::float4 AS uint8)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float4 AS uint8) - -query T -SELECT TRY_CAST('NaN'::float4 AS uint8)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float4 AS uint8) - -query T -SELECT TRY_CAST('Infinity'::float4 AS uint8)::text ----- -NULL - -# real -> float8 -query TBB -SELECT TRY_CAST(1.5::float4 AS float8)::text, TRY_CAST(1.5::float4 AS float8)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS float8)::text, TRY_CAST(NULL::float4 AS float8) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST('3.4e38'::float4 AS float8)::text, TRY_CAST('3.4e38'::float4 AS float8)::text IS NOT DISTINCT FROM CAST('3.4e38'::float4 AS float8)::text, TRY_CAST(NULL::float4 AS float8) IS NULL ----- -3.3999999521443642e+38 true true - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS float8)::text, TRY_CAST((-1.5)::float4 AS float8)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS float8)::text, TRY_CAST(NULL::float4 AS float8) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float4 AS float8)::text, TRY_CAST('NaN'::float4 AS float8)::text IS NOT DISTINCT FROM CAST('NaN'::float4 AS float8)::text, TRY_CAST(NULL::float4 AS float8) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST('Infinity'::float4 AS float8)::text, TRY_CAST('Infinity'::float4 AS float8)::text IS NOT DISTINCT FROM CAST('Infinity'::float4 AS float8)::text, TRY_CAST(NULL::float4 AS float8) IS NULL ----- -Infinity true true - -# real -> numeric -query TBB -SELECT TRY_CAST(1.5::float4 AS numeric)::text, TRY_CAST(1.5::float4 AS numeric)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS numeric)::text, TRY_CAST(NULL::float4 AS numeric) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST('3.4e38'::float4 AS numeric)::text, TRY_CAST('3.4e38'::float4 AS numeric)::text IS NOT DISTINCT FROM CAST('3.4e38'::float4 AS numeric)::text, TRY_CAST(NULL::float4 AS numeric) IS NULL ----- -340000000000000000000000000000000000000 true true - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS numeric)::text, TRY_CAST((-1.5)::float4 AS numeric)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS numeric)::text, TRY_CAST(NULL::float4 AS numeric) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float4 AS numeric)::text, TRY_CAST('NaN'::float4 AS numeric)::text IS NOT DISTINCT FROM CAST('NaN'::float4 AS numeric)::text, TRY_CAST(NULL::float4 AS numeric) IS NULL ----- -NaN true true - -statement error -SELECT CAST('Infinity'::float4 AS numeric) - -query T -SELECT TRY_CAST('Infinity'::float4 AS numeric)::text ----- -NULL - -# real -> text -query TBB -SELECT TRY_CAST(1.5::float4 AS text)::text, TRY_CAST(1.5::float4 AS text)::text IS NOT DISTINCT FROM CAST(1.5::float4 AS text)::text, TRY_CAST(NULL::float4 AS text) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST('3.4e38'::float4 AS text)::text, TRY_CAST('3.4e38'::float4 AS text)::text IS NOT DISTINCT FROM CAST('3.4e38'::float4 AS text)::text, TRY_CAST(NULL::float4 AS text) IS NULL ----- -3.4e+38 true true - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS text)::text, TRY_CAST((-1.5)::float4 AS text)::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS text)::text, TRY_CAST(NULL::float4 AS text) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float4 AS text)::text, TRY_CAST('NaN'::float4 AS text)::text IS NOT DISTINCT FROM CAST('NaN'::float4 AS text)::text, TRY_CAST(NULL::float4 AS text) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST('Infinity'::float4 AS text)::text, TRY_CAST('Infinity'::float4 AS text)::text IS NOT DISTINCT FROM CAST('Infinity'::float4 AS text)::text, TRY_CAST(NULL::float4 AS text) IS NULL ----- -Infinity true true - -## double precision sources - -# double precision -> int2 -query TBB -SELECT TRY_CAST(1.5::float8 AS int2)::text, TRY_CAST(1.5::float8 AS int2)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS int2)::text, TRY_CAST(NULL::float8 AS int2) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS int2) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS int2)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS int2)::text, TRY_CAST((-1.5)::float8 AS int2)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS int2)::text, TRY_CAST(NULL::float8 AS int2) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float8 AS int2) - -query T -SELECT TRY_CAST('NaN'::float8 AS int2)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS int2) - -query T -SELECT TRY_CAST('Infinity'::float8 AS int2)::text ----- -NULL - -# double precision -> int4 -query TBB -SELECT TRY_CAST(1.5::float8 AS int4)::text, TRY_CAST(1.5::float8 AS int4)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS int4)::text, TRY_CAST(NULL::float8 AS int4) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS int4) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS int4)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS int4)::text, TRY_CAST((-1.5)::float8 AS int4)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS int4)::text, TRY_CAST(NULL::float8 AS int4) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float8 AS int4) - -query T -SELECT TRY_CAST('NaN'::float8 AS int4)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS int4) - -query T -SELECT TRY_CAST('Infinity'::float8 AS int4)::text ----- -NULL - -# double precision -> int8 -query TBB -SELECT TRY_CAST(1.5::float8 AS int8)::text, TRY_CAST(1.5::float8 AS int8)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS int8)::text, TRY_CAST(NULL::float8 AS int8) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS int8) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS int8)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS int8)::text, TRY_CAST((-1.5)::float8 AS int8)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS int8)::text, TRY_CAST(NULL::float8 AS int8) IS NULL ----- --2 true true - -statement error -SELECT CAST('NaN'::float8 AS int8) - -query T -SELECT TRY_CAST('NaN'::float8 AS int8)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS int8) - -query T -SELECT TRY_CAST('Infinity'::float8 AS int8)::text ----- -NULL - -# double precision -> uint2 -query TBB -SELECT TRY_CAST(1.5::float8 AS uint2)::text, TRY_CAST(1.5::float8 AS uint2)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS uint2)::text, TRY_CAST(NULL::float8 AS uint2) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS uint2) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS uint2)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float8 AS uint2) - -query T -SELECT TRY_CAST((-1.5)::float8 AS uint2)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float8 AS uint2) - -query T -SELECT TRY_CAST('NaN'::float8 AS uint2)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS uint2) - -query T -SELECT TRY_CAST('Infinity'::float8 AS uint2)::text ----- -NULL - -# double precision -> uint4 -query TBB -SELECT TRY_CAST(1.5::float8 AS uint4)::text, TRY_CAST(1.5::float8 AS uint4)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS uint4)::text, TRY_CAST(NULL::float8 AS uint4) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS uint4) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS uint4)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float8 AS uint4) - -query T -SELECT TRY_CAST((-1.5)::float8 AS uint4)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float8 AS uint4) - -query T -SELECT TRY_CAST('NaN'::float8 AS uint4)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS uint4) - -query T -SELECT TRY_CAST('Infinity'::float8 AS uint4)::text ----- -NULL - -# double precision -> uint8 -query TBB -SELECT TRY_CAST(1.5::float8 AS uint8)::text, TRY_CAST(1.5::float8 AS uint8)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS uint8)::text, TRY_CAST(NULL::float8 AS uint8) IS NULL ----- -2 true true - -statement error -SELECT CAST('1.7e308'::float8 AS uint8) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS uint8)::text ----- -NULL - -statement error -SELECT CAST((-1.5)::float8 AS uint8) - -query T -SELECT TRY_CAST((-1.5)::float8 AS uint8)::text ----- -NULL - -statement error -SELECT CAST('NaN'::float8 AS uint8) - -query T -SELECT TRY_CAST('NaN'::float8 AS uint8)::text ----- -NULL - -statement error -SELECT CAST('Infinity'::float8 AS uint8) - -query T -SELECT TRY_CAST('Infinity'::float8 AS uint8)::text ----- -NULL - -# double precision -> float4 -query TBB -SELECT TRY_CAST(1.5::float8 AS float4)::text, TRY_CAST(1.5::float8 AS float4)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS float4)::text, TRY_CAST(NULL::float8 AS float4) IS NULL ----- -1.5 true true - -statement error -SELECT CAST('1.7e308'::float8 AS float4) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS float4)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS float4)::text, TRY_CAST((-1.5)::float8 AS float4)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS float4)::text, TRY_CAST(NULL::float8 AS float4) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float8 AS float4)::text, TRY_CAST('NaN'::float8 AS float4)::text IS NOT DISTINCT FROM CAST('NaN'::float8 AS float4)::text, TRY_CAST(NULL::float8 AS float4) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST('Infinity'::float8 AS float4)::text, TRY_CAST('Infinity'::float8 AS float4)::text IS NOT DISTINCT FROM CAST('Infinity'::float8 AS float4)::text, TRY_CAST(NULL::float8 AS float4) IS NULL ----- -Infinity true true - -# double precision -> numeric -query TBB -SELECT TRY_CAST(1.5::float8 AS numeric)::text, TRY_CAST(1.5::float8 AS numeric)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS numeric)::text, TRY_CAST(NULL::float8 AS numeric) IS NULL ----- -1.5 true true - -statement error -SELECT CAST('1.7e308'::float8 AS numeric) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS numeric)::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS numeric)::text, TRY_CAST((-1.5)::float8 AS numeric)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS numeric)::text, TRY_CAST(NULL::float8 AS numeric) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float8 AS numeric)::text, TRY_CAST('NaN'::float8 AS numeric)::text IS NOT DISTINCT FROM CAST('NaN'::float8 AS numeric)::text, TRY_CAST(NULL::float8 AS numeric) IS NULL ----- -NaN true true - -statement error -SELECT CAST('Infinity'::float8 AS numeric) - -query T -SELECT TRY_CAST('Infinity'::float8 AS numeric)::text ----- -NULL - -# double precision -> text -query TBB -SELECT TRY_CAST(1.5::float8 AS text)::text, TRY_CAST(1.5::float8 AS text)::text IS NOT DISTINCT FROM CAST(1.5::float8 AS text)::text, TRY_CAST(NULL::float8 AS text) IS NULL ----- -1.5 true true - -query TBB -SELECT TRY_CAST('1.7e308'::float8 AS text)::text, TRY_CAST('1.7e308'::float8 AS text)::text IS NOT DISTINCT FROM CAST('1.7e308'::float8 AS text)::text, TRY_CAST(NULL::float8 AS text) IS NULL ----- -1.7e+308 true true - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS text)::text, TRY_CAST((-1.5)::float8 AS text)::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS text)::text, TRY_CAST(NULL::float8 AS text) IS NULL ----- --1.5 true true - -query TBB -SELECT TRY_CAST('NaN'::float8 AS text)::text, TRY_CAST('NaN'::float8 AS text)::text IS NOT DISTINCT FROM CAST('NaN'::float8 AS text)::text, TRY_CAST(NULL::float8 AS text) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST('Infinity'::float8 AS text)::text, TRY_CAST('Infinity'::float8 AS text)::text IS NOT DISTINCT FROM CAST('Infinity'::float8 AS text)::text, TRY_CAST(NULL::float8 AS text) IS NULL ----- -Infinity true true - -## time sources - -# time -> interval -query TBB -SELECT TRY_CAST('12:34:56'::time AS interval)::text, TRY_CAST('12:34:56'::time AS interval)::text IS NOT DISTINCT FROM CAST('12:34:56'::time AS interval)::text, TRY_CAST(NULL::time AS interval) IS NULL ----- -12:34:56 true true - -# time -> text -query TBB -SELECT TRY_CAST('12:34:56'::time AS text)::text, TRY_CAST('12:34:56'::time AS text)::text IS NOT DISTINCT FROM CAST('12:34:56'::time AS text)::text, TRY_CAST(NULL::time AS text) IS NULL ----- -12:34:56 true true - -## interval sources - -# interval -> time -query TBB -SELECT TRY_CAST('1 day 02:03:04'::interval AS time)::text, TRY_CAST('1 day 02:03:04'::interval AS time)::text IS NOT DISTINCT FROM CAST('1 day 02:03:04'::interval AS time)::text, TRY_CAST(NULL::interval AS time) IS NULL ----- -02:03:04 true true - -query TBB -SELECT TRY_CAST('178000000 years'::interval AS time)::text, TRY_CAST('178000000 years'::interval AS time)::text IS NOT DISTINCT FROM CAST('178000000 years'::interval AS time)::text, TRY_CAST(NULL::interval AS time) IS NULL ----- -00:00:00 true true - -# interval -> text -query TBB -SELECT TRY_CAST('1 day 02:03:04'::interval AS text)::text, TRY_CAST('1 day 02:03:04'::interval AS text)::text IS NOT DISTINCT FROM CAST('1 day 02:03:04'::interval AS text)::text, TRY_CAST(NULL::interval AS text) IS NULL ----- -1␠day␠02:03:04 true true - -query TBB -SELECT TRY_CAST('178000000 years'::interval AS text)::text, TRY_CAST('178000000 years'::interval AS text)::text IS NOT DISTINCT FROM CAST('178000000 years'::interval AS text)::text, TRY_CAST(NULL::interval AS text) IS NULL ----- -178000000␠years true true - -## bytea sources - -# bytea -> text -query TBB -SELECT TRY_CAST('\x0102'::bytea AS text)::text, TRY_CAST('\x0102'::bytea AS text)::text IS NOT DISTINCT FROM CAST('\x0102'::bytea AS text)::text, TRY_CAST(NULL::bytea AS text) IS NULL ----- -\x0102 true true - -## char sources - -# char -> text -query TBB -SELECT TRY_CAST('abc'::char(3) AS text)::text, TRY_CAST('abc'::char(3) AS text)::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS text)::text, TRY_CAST(NULL::char(3) AS text) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::char(3) AS text)::text, TRY_CAST('abc'::char(3) AS text)::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS text)::text, TRY_CAST(NULL::char(3) AS text) IS NULL ----- -abc true true - -# char -> char(3) -query TBB -SELECT TRY_CAST('abc'::char(3) AS char(3))::text, TRY_CAST('abc'::char(3) AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS char(3))::text, TRY_CAST(NULL::char(3) AS char(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::char(3) AS char(3))::text, TRY_CAST('abc'::char(3) AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS char(3))::text, TRY_CAST(NULL::char(3) AS char(3)) IS NULL ----- -abc true true - -# char -> varchar(3) -query TBB -SELECT TRY_CAST('abc'::char(3) AS varchar(3))::text, TRY_CAST('abc'::char(3) AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS varchar(3))::text, TRY_CAST(NULL::char(3) AS varchar(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::char(3) AS varchar(3))::text, TRY_CAST('abc'::char(3) AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS varchar(3))::text, TRY_CAST(NULL::char(3) AS varchar(3)) IS NULL ----- -abc true true - -# char -> "char" -query TBB -SELECT TRY_CAST('a'::char(3) AS "char")::text, TRY_CAST('a'::char(3) AS "char")::text IS NOT DISTINCT FROM CAST('a'::char(3) AS "char")::text, TRY_CAST(NULL::char(3) AS "char") IS NULL ----- -a true true - -query TBB -SELECT TRY_CAST('abc'::char(3) AS "char")::text, TRY_CAST('abc'::char(3) AS "char")::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS "char")::text, TRY_CAST(NULL::char(3) AS "char") IS NULL ----- -a true true - -# char -> name -query TBB -SELECT TRY_CAST('abc'::char(3) AS name)::text, TRY_CAST('abc'::char(3) AS name)::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS name)::text, TRY_CAST(NULL::char(3) AS name) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::char(3) AS name)::text, TRY_CAST('abc'::char(3) AS name)::text IS NOT DISTINCT FROM CAST('abc'::char(3) AS name)::text, TRY_CAST(NULL::char(3) AS name) IS NULL ----- -abc true true - -## varchar sources - -# varchar -> text -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS text)::text, TRY_CAST('abc'::varchar(3) AS text)::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS text)::text, TRY_CAST(NULL::varchar(3) AS text) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS text)::text, TRY_CAST('abc'::varchar(3) AS text)::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS text)::text, TRY_CAST(NULL::varchar(3) AS text) IS NULL ----- -abc true true - -# varchar -> char(3) -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS char(3))::text, TRY_CAST('abc'::varchar(3) AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS char(3))::text, TRY_CAST(NULL::varchar(3) AS char(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS char(3))::text, TRY_CAST('abc'::varchar(3) AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS char(3))::text, TRY_CAST(NULL::varchar(3) AS char(3)) IS NULL ----- -abc true true - -# varchar -> varchar(3) -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS varchar(3))::text, TRY_CAST('abc'::varchar(3) AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS varchar(3))::text, TRY_CAST(NULL::varchar(3) AS varchar(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS varchar(3))::text, TRY_CAST('abc'::varchar(3) AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS varchar(3))::text, TRY_CAST(NULL::varchar(3) AS varchar(3)) IS NULL ----- -abc true true - -# varchar -> "char" -query TBB -SELECT TRY_CAST('a'::varchar(3) AS "char")::text, TRY_CAST('a'::varchar(3) AS "char")::text IS NOT DISTINCT FROM CAST('a'::varchar(3) AS "char")::text, TRY_CAST(NULL::varchar(3) AS "char") IS NULL ----- -a true true - -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS "char")::text, TRY_CAST('abc'::varchar(3) AS "char")::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS "char")::text, TRY_CAST(NULL::varchar(3) AS "char") IS NULL ----- -a true true - -# varchar -> name -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS name)::text, TRY_CAST('abc'::varchar(3) AS name)::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS name)::text, TRY_CAST(NULL::varchar(3) AS name) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::varchar(3) AS name)::text, TRY_CAST('abc'::varchar(3) AS name)::text IS NOT DISTINCT FROM CAST('abc'::varchar(3) AS name)::text, TRY_CAST(NULL::varchar(3) AS name) IS NULL ----- -abc true true - -## "char" sources - -# "char" -> text -query TBB -SELECT TRY_CAST('a'::"char" AS text)::text, TRY_CAST('a'::"char" AS text)::text IS NOT DISTINCT FROM CAST('a'::"char" AS text)::text, TRY_CAST(NULL::"char" AS text) IS NULL ----- -a true true - -# "char" -> char(3) -query TBB -SELECT TRY_CAST('a'::"char" AS char(3))::text, TRY_CAST('a'::"char" AS char(3))::text IS NOT DISTINCT FROM CAST('a'::"char" AS char(3))::text, TRY_CAST(NULL::"char" AS char(3)) IS NULL ----- -a true true - -# "char" -> varchar(3) -query TBB -SELECT TRY_CAST('a'::"char" AS varchar(3))::text, TRY_CAST('a'::"char" AS varchar(3))::text IS NOT DISTINCT FROM CAST('a'::"char" AS varchar(3))::text, TRY_CAST(NULL::"char" AS varchar(3)) IS NULL ----- -a true true - -# "char" -> int4 (by byte value) -query TBB -SELECT TRY_CAST('a'::"char" AS int4)::text, TRY_CAST('a'::"char" AS int4)::text IS NOT DISTINCT FROM CAST('a'::"char" AS int4)::text, TRY_CAST(NULL::"char" AS int4) IS NULL ----- -97 true true - -# "char" -> int8 (rendered to text, then parsed) -query TBB -SELECT TRY_CAST('7'::"char" AS int8)::text, TRY_CAST('7'::"char" AS int8)::text IS NOT DISTINCT FROM CAST('7'::"char" AS int8)::text, TRY_CAST(NULL::"char" AS int8) IS NULL ----- -7 true true - -statement error -SELECT CAST('a'::"char" AS int8) - -query T -SELECT TRY_CAST('a'::"char" AS int8)::text ----- -NULL - -## name sources - -# name -> text -query TBB -SELECT TRY_CAST('abc'::name AS text)::text, TRY_CAST('abc'::name AS text)::text IS NOT DISTINCT FROM CAST('abc'::name AS text)::text, TRY_CAST(NULL::name AS text) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::name AS text)::text, TRY_CAST('abc'::name AS text)::text IS NOT DISTINCT FROM CAST('abc'::name AS text)::text, TRY_CAST(NULL::name AS text) IS NULL ----- -abc true true - -# name -> char(3) -query TBB -SELECT TRY_CAST('abc'::name AS char(3))::text, TRY_CAST('abc'::name AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::name AS char(3))::text, TRY_CAST(NULL::name AS char(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::name AS char(3))::text, TRY_CAST('abc'::name AS char(3))::text IS NOT DISTINCT FROM CAST('abc'::name AS char(3))::text, TRY_CAST(NULL::name AS char(3)) IS NULL ----- -abc true true - -# name -> varchar(3) -query TBB -SELECT TRY_CAST('abc'::name AS varchar(3))::text, TRY_CAST('abc'::name AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::name AS varchar(3))::text, TRY_CAST(NULL::name AS varchar(3)) IS NULL ----- -abc true true - -query TBB -SELECT TRY_CAST('abc'::name AS varchar(3))::text, TRY_CAST('abc'::name AS varchar(3))::text IS NOT DISTINCT FROM CAST('abc'::name AS varchar(3))::text, TRY_CAST(NULL::name AS varchar(3)) IS NULL ----- -abc true true - -## record sources - -# record -> text -query TBB -SELECT TRY_CAST(ROW(1, 'a') AS text)::text, TRY_CAST(ROW(1, 'a') AS text)::text IS NOT DISTINCT FROM CAST(ROW(1, 'a') AS text)::text, TRY_CAST(NULL::rec8 AS text) IS NULL ----- -(1,a) true true - -query TBB -SELECT TRY_CAST(ROW(100000, 'a') AS text)::text, TRY_CAST(ROW(100000, 'a') AS text)::text IS NOT DISTINCT FROM CAST(ROW(100000, 'a') AS text)::text, TRY_CAST(NULL::rec8 AS text) IS NULL ----- -(100000,a) true true - -# record -> rec8 -query TBB -SELECT TRY_CAST(ROW(1, 'a') AS rec8)::text, TRY_CAST(ROW(1, 'a') AS rec8)::text IS NOT DISTINCT FROM CAST(ROW(1, 'a') AS rec8)::text, TRY_CAST(NULL::rec8 AS rec8) IS NULL ----- -(1,a) true true - -query TBB -SELECT TRY_CAST(ROW(100000, 'a') AS rec8)::text, TRY_CAST(ROW(100000, 'a') AS rec8)::text IS NOT DISTINCT FROM CAST(ROW(100000, 'a') AS rec8)::text, TRY_CAST(NULL::rec8 AS rec8) IS NULL ----- -(100000,a) true true - -# record -> rec2 -query TBB -SELECT TRY_CAST(ROW(1, 'a') AS rec2)::text, TRY_CAST(ROW(1, 'a') AS rec2)::text IS NOT DISTINCT FROM CAST(ROW(1, 'a') AS rec2)::text, TRY_CAST(NULL::rec8 AS rec2) IS NULL ----- -(1,a) true true - -statement error -SELECT CAST(ROW(100000, 'a') AS rec2) - -query T -SELECT TRY_CAST(ROW(100000, 'a') AS rec2)::text ----- -NULL - -## array sources - -# array -> text -query TBB -SELECT TRY_CAST(ARRAY[1, 2] AS text)::text, TRY_CAST(ARRAY[1, 2] AS text)::text IS NOT DISTINCT FROM CAST(ARRAY[1, 2] AS text)::text, TRY_CAST(NULL::int8[] AS text) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(ARRAY[100000] AS text)::text, TRY_CAST(ARRAY[100000] AS text)::text IS NOT DISTINCT FROM CAST(ARRAY[100000] AS text)::text, TRY_CAST(NULL::int8[] AS text) IS NULL ----- -{100000} true true - -# array -> int8 list -query TBB -SELECT TRY_CAST(ARRAY[1, 2] AS int8 list)::text, TRY_CAST(ARRAY[1, 2] AS int8 list)::text IS NOT DISTINCT FROM CAST(ARRAY[1, 2] AS int8 list)::text, TRY_CAST(NULL::int8[] AS int8 list) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(ARRAY[100000] AS int8 list)::text, TRY_CAST(ARRAY[100000] AS int8 list)::text IS NOT DISTINCT FROM CAST(ARRAY[100000] AS int8 list)::text, TRY_CAST(NULL::int8[] AS int8 list) IS NULL ----- -{100000} true true - -# array -> int2 list -query TBB -SELECT TRY_CAST(ARRAY[1, 2] AS int2 list)::text, TRY_CAST(ARRAY[1, 2] AS int2 list)::text IS NOT DISTINCT FROM CAST(ARRAY[1, 2] AS int2 list)::text, TRY_CAST(NULL::int8[] AS int2 list) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(ARRAY[100000] AS int2 list)::text, TRY_CAST(ARRAY[100000] AS int2 list)::text IS NOT DISTINCT FROM CAST(ARRAY[100000] AS int2 list)::text, TRY_CAST(NULL::int8[] AS int2 list) IS NULL ----- -{100000} true true - -# array -> int8[] -query TBB -SELECT TRY_CAST(ARRAY[1, 2] AS int8[])::text, TRY_CAST(ARRAY[1, 2] AS int8[])::text IS NOT DISTINCT FROM CAST(ARRAY[1, 2] AS int8[])::text, TRY_CAST(NULL::int8[] AS int8[]) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(ARRAY[100000] AS int8[])::text, TRY_CAST(ARRAY[100000] AS int8[])::text IS NOT DISTINCT FROM CAST(ARRAY[100000] AS int8[])::text, TRY_CAST(NULL::int8[] AS int8[]) IS NULL ----- -{100000} true true - -# array -> int2[] -query TBB -SELECT TRY_CAST(ARRAY[1, 2] AS int2[])::text, TRY_CAST(ARRAY[1, 2] AS int2[])::text IS NOT DISTINCT FROM CAST(ARRAY[1, 2] AS int2[])::text, TRY_CAST(NULL::int8[] AS int2[]) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST(ARRAY[100000] AS int2[]) - -query T -SELECT TRY_CAST(ARRAY[100000] AS int2[])::text ----- -NULL - -## int2vector sources - -# int2vector -> int8[] -query TBB -SELECT TRY_CAST('1 2'::int2vector AS int8[])::text, TRY_CAST('1 2'::int2vector AS int8[])::text IS NOT DISTINCT FROM CAST('1 2'::int2vector AS int8[])::text, TRY_CAST(NULL::int2vector AS int8[]) IS NULL ----- -{1,2} true true - -# int2vector -> int2[] -query TBB -SELECT TRY_CAST('1 2'::int2vector AS int2[])::text, TRY_CAST('1 2'::int2vector AS int2[])::text IS NOT DISTINCT FROM CAST('1 2'::int2vector AS int2[])::text, TRY_CAST(NULL::int2vector AS int2[]) IS NULL ----- -{1,2} true true - -# int2vector -> text -query TBB -SELECT TRY_CAST('1 2'::int2vector AS text)::text, TRY_CAST('1 2'::int2vector AS text)::text IS NOT DISTINCT FROM CAST('1 2'::int2vector AS text)::text, TRY_CAST(NULL::int2vector AS text) IS NULL ----- -1␠2 true true - -## list sources - -# list -> text -query TBB -SELECT TRY_CAST(LIST[1, 2] AS text)::text, TRY_CAST(LIST[1, 2] AS text)::text IS NOT DISTINCT FROM CAST(LIST[1, 2] AS text)::text, TRY_CAST(NULL::int8 list AS text) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(LIST[100000] AS text)::text, TRY_CAST(LIST[100000] AS text)::text IS NOT DISTINCT FROM CAST(LIST[100000] AS text)::text, TRY_CAST(NULL::int8 list AS text) IS NULL ----- -{100000} true true - -# list -> int8 list -query TBB -SELECT TRY_CAST(LIST[1, 2] AS int8 list)::text, TRY_CAST(LIST[1, 2] AS int8 list)::text IS NOT DISTINCT FROM CAST(LIST[1, 2] AS int8 list)::text, TRY_CAST(NULL::int8 list AS int8 list) IS NULL ----- -{1,2} true true - -query TBB -SELECT TRY_CAST(LIST[100000] AS int8 list)::text, TRY_CAST(LIST[100000] AS int8 list)::text IS NOT DISTINCT FROM CAST(LIST[100000] AS int8 list)::text, TRY_CAST(NULL::int8 list AS int8 list) IS NULL ----- -{100000} true true - -# list -> int2 list -query TBB -SELECT TRY_CAST(LIST[1, 2] AS int2 list)::text, TRY_CAST(LIST[1, 2] AS int2 list)::text IS NOT DISTINCT FROM CAST(LIST[1, 2] AS int2 list)::text, TRY_CAST(NULL::int8 list AS int2 list) IS NULL ----- -{1,2} true true - -statement error -SELECT CAST(LIST[100000] AS int2 list) - -query T -SELECT TRY_CAST(LIST[100000] AS int2 list)::text ----- -NULL - -## map sources - -# map -> text -query TBB -SELECT TRY_CAST('{a=>1}'::map[text=>int4] AS text)::text, TRY_CAST('{a=>1}'::map[text=>int4] AS text)::text IS NOT DISTINCT FROM CAST('{a=>1}'::map[text=>int4] AS text)::text, TRY_CAST(NULL::map[text=>int8] AS text) IS NULL ----- -{a=>1} true true - -## jsonb sources - -# jsonb -> bool -statement error -SELECT CAST('1'::jsonb AS bool) - -query T -SELECT TRY_CAST('1'::jsonb AS bool)::text ----- -NULL - -statement error -SELECT CAST('"x"'::jsonb AS bool) - -query T -SELECT TRY_CAST('"x"'::jsonb AS bool)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS bool) - -query T -SELECT TRY_CAST('[1]'::jsonb AS bool)::text ----- -NULL - -statement error -SELECT CAST('1e30'::jsonb AS bool) - -query T -SELECT TRY_CAST('1e30'::jsonb AS bool)::text ----- -NULL - -query TBB -SELECT TRY_CAST('true'::jsonb AS bool)::text, TRY_CAST('true'::jsonb AS bool)::text IS NOT DISTINCT FROM CAST('true'::jsonb AS bool)::text, TRY_CAST(NULL::jsonb AS bool) IS NULL ----- -true true true - -statement error -SELECT CAST('null'::jsonb AS bool) - -query T -SELECT TRY_CAST('null'::jsonb AS bool)::text ----- -NULL - -# jsonb -> int2 -query TBB -SELECT TRY_CAST('1'::jsonb AS int2)::text, TRY_CAST('1'::jsonb AS int2)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS int2)::text, TRY_CAST(NULL::jsonb AS int2) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS int2) - -query T -SELECT TRY_CAST('"x"'::jsonb AS int2)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS int2) - -query T -SELECT TRY_CAST('[1]'::jsonb AS int2)::text ----- -NULL - -statement error -SELECT CAST('1e30'::jsonb AS int2) - -query T -SELECT TRY_CAST('1e30'::jsonb AS int2)::text ----- -NULL - -statement error -SELECT CAST('true'::jsonb AS int2) - -query T -SELECT TRY_CAST('true'::jsonb AS int2)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS int2) - -query T -SELECT TRY_CAST('null'::jsonb AS int2)::text ----- -NULL - -# jsonb -> int4 -query TBB -SELECT TRY_CAST('1'::jsonb AS int4)::text, TRY_CAST('1'::jsonb AS int4)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS int4)::text, TRY_CAST(NULL::jsonb AS int4) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS int4) - -query T -SELECT TRY_CAST('"x"'::jsonb AS int4)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS int4) - -query T -SELECT TRY_CAST('[1]'::jsonb AS int4)::text ----- -NULL - -statement error -SELECT CAST('1e30'::jsonb AS int4) - -query T -SELECT TRY_CAST('1e30'::jsonb AS int4)::text ----- -NULL - -statement error -SELECT CAST('true'::jsonb AS int4) - -query T -SELECT TRY_CAST('true'::jsonb AS int4)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS int4) - -query T -SELECT TRY_CAST('null'::jsonb AS int4)::text ----- -NULL - -# jsonb -> int8 -query TBB -SELECT TRY_CAST('1'::jsonb AS int8)::text, TRY_CAST('1'::jsonb AS int8)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS int8)::text, TRY_CAST(NULL::jsonb AS int8) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS int8) - -query T -SELECT TRY_CAST('"x"'::jsonb AS int8)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS int8) - -query T -SELECT TRY_CAST('[1]'::jsonb AS int8)::text ----- -NULL - -statement error -SELECT CAST('1e30'::jsonb AS int8) - -query T -SELECT TRY_CAST('1e30'::jsonb AS int8)::text ----- -NULL - -statement error -SELECT CAST('true'::jsonb AS int8) - -query T -SELECT TRY_CAST('true'::jsonb AS int8)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS int8) - -query T -SELECT TRY_CAST('null'::jsonb AS int8)::text ----- -NULL - -# jsonb -> float4 -query TBB -SELECT TRY_CAST('1'::jsonb AS float4)::text, TRY_CAST('1'::jsonb AS float4)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS float4)::text, TRY_CAST(NULL::jsonb AS float4) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS float4) - -query T -SELECT TRY_CAST('"x"'::jsonb AS float4)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS float4) - -query T -SELECT TRY_CAST('[1]'::jsonb AS float4)::text ----- -NULL - -query TBB -SELECT TRY_CAST('1e30'::jsonb AS float4)::text, TRY_CAST('1e30'::jsonb AS float4)::text IS NOT DISTINCT FROM CAST('1e30'::jsonb AS float4)::text, TRY_CAST(NULL::jsonb AS float4) IS NULL ----- -1e+30 true true - -statement error -SELECT CAST('true'::jsonb AS float4) - -query T -SELECT TRY_CAST('true'::jsonb AS float4)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS float4) - -query T -SELECT TRY_CAST('null'::jsonb AS float4)::text ----- -NULL - -# jsonb -> float8 -query TBB -SELECT TRY_CAST('1'::jsonb AS float8)::text, TRY_CAST('1'::jsonb AS float8)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS float8)::text, TRY_CAST(NULL::jsonb AS float8) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS float8) - -query T -SELECT TRY_CAST('"x"'::jsonb AS float8)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS float8) - -query T -SELECT TRY_CAST('[1]'::jsonb AS float8)::text ----- -NULL - -query TBB -SELECT TRY_CAST('1e30'::jsonb AS float8)::text, TRY_CAST('1e30'::jsonb AS float8)::text IS NOT DISTINCT FROM CAST('1e30'::jsonb AS float8)::text, TRY_CAST(NULL::jsonb AS float8) IS NULL ----- -1e+30 true true - -statement error -SELECT CAST('true'::jsonb AS float8) - -query T -SELECT TRY_CAST('true'::jsonb AS float8)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS float8) - -query T -SELECT TRY_CAST('null'::jsonb AS float8)::text ----- -NULL - -# jsonb -> numeric -query TBB -SELECT TRY_CAST('1'::jsonb AS numeric)::text, TRY_CAST('1'::jsonb AS numeric)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS numeric)::text, TRY_CAST(NULL::jsonb AS numeric) IS NULL ----- -1 true true - -statement error -SELECT CAST('"x"'::jsonb AS numeric) - -query T -SELECT TRY_CAST('"x"'::jsonb AS numeric)::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS numeric) - -query T -SELECT TRY_CAST('[1]'::jsonb AS numeric)::text ----- -NULL - -query TBB -SELECT TRY_CAST('1e30'::jsonb AS numeric)::text, TRY_CAST('1e30'::jsonb AS numeric)::text IS NOT DISTINCT FROM CAST('1e30'::jsonb AS numeric)::text, TRY_CAST(NULL::jsonb AS numeric) IS NULL ----- -1000000000000000000000000000000 true true - -statement error -SELECT CAST('true'::jsonb AS numeric) - -query T -SELECT TRY_CAST('true'::jsonb AS numeric)::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS numeric) - -query T -SELECT TRY_CAST('null'::jsonb AS numeric)::text ----- -NULL - -# jsonb -> text -query TBB -SELECT TRY_CAST('1'::jsonb AS text)::text, TRY_CAST('1'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('1'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -1 true true - -query TBB -SELECT TRY_CAST('"x"'::jsonb AS text)::text, TRY_CAST('"x"'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('"x"'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -"x" true true - -query TBB -SELECT TRY_CAST('[1]'::jsonb AS text)::text, TRY_CAST('[1]'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('[1]'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -[1] true true - -query TBB -SELECT TRY_CAST('1e30'::jsonb AS text)::text, TRY_CAST('1e30'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('1e30'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -1000000000000000000000000000000 true true - -query TBB -SELECT TRY_CAST('true'::jsonb AS text)::text, TRY_CAST('true'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('true'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -true true true - -query TBB -SELECT TRY_CAST('null'::jsonb AS text)::text, TRY_CAST('null'::jsonb AS text)::text IS NOT DISTINCT FROM CAST('null'::jsonb AS text)::text, TRY_CAST(NULL::jsonb AS text) IS NULL ----- -null true true - -## uuid sources - -# uuid -> text -query TBB -SELECT TRY_CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid AS text)::text, TRY_CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid AS text)::text IS NOT DISTINCT FROM CAST('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'::uuid AS text)::text, TRY_CAST(NULL::uuid AS text) IS NULL ----- -a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 true true - -## range sources - -# range -> text -query TBB -SELECT TRY_CAST('[1,3)'::int4range AS text)::text, TRY_CAST('[1,3)'::int4range AS text)::text IS NOT DISTINCT FROM CAST('[1,3)'::int4range AS text)::text, TRY_CAST(NULL::int4range AS text) IS NULL ----- -[1,3) true true diff --git a/test/sqllogictest/try_cast_noindex.slt b/test/sqllogictest/try_cast_noindex.slt deleted file mode 100644 index 9646ee0d468bb..0000000000000 --- a/test/sqllogictest/try_cast_noindex.slt +++ /dev/null @@ -1,274 +0,0 @@ -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. - -# The numeric(5,2) blocks of the TRY_CAST matrix in try_cast.slt. They live -# apart because a constant-folded numeric(5,2) literal loses its scale on the -# view path: `SELECT 1.5::numeric(5,2)::text` gives `1.50`, but the same -# expression in a CREATE VIEW gives `1.5`, for CAST and TRY_CAST alike. The -# indexed views that --auto-index-selects wraps queries in hit that, so this -# file is kept out of that mode. The blocks have the same shape as in -# try_cast.slt. - -mode cockroach - -simple conn=mz_system,user=mz_system -ALTER SYSTEM SET enable_try_cast = true ----- -COMPLETE 0 - -## numeric(5,2) targets - -# smallint -> numeric(5,2) -query TBB -SELECT TRY_CAST(1::int2 AS numeric(5,2))::text, TRY_CAST(1::int2 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1::int2 AS numeric(5,2))::text, TRY_CAST(NULL::int2 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(32767::int2 AS numeric(5,2))::text, TRY_CAST(32767::int2 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(32767::int2 AS numeric(5,2))::text, TRY_CAST(NULL::int2 AS numeric(5,2)) IS NULL ----- -32767.00 true true - -query TBB -SELECT TRY_CAST((-1)::int2 AS numeric(5,2))::text, TRY_CAST((-1)::int2 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST((-1)::int2 AS numeric(5,2))::text, TRY_CAST(NULL::int2 AS numeric(5,2)) IS NULL ----- --1.00 true true - -# integer -> numeric(5,2) -query TBB -SELECT TRY_CAST(1 AS numeric(5,2))::text, TRY_CAST(1 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1 AS numeric(5,2))::text, TRY_CAST(NULL::int4 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(2147483647 AS numeric(5,2))::text, TRY_CAST(2147483647 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(2147483647 AS numeric(5,2))::text, TRY_CAST(NULL::int4 AS numeric(5,2)) IS NULL ----- -2147483647.00 true true - -query TBB -SELECT TRY_CAST(-1 AS numeric(5,2))::text, TRY_CAST(-1 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(-1 AS numeric(5,2))::text, TRY_CAST(NULL::int4 AS numeric(5,2)) IS NULL ----- --1.00 true true - -query TBB -SELECT TRY_CAST(100000 AS numeric(5,2))::text, TRY_CAST(100000 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(100000 AS numeric(5,2))::text, TRY_CAST(NULL::int4 AS numeric(5,2)) IS NULL ----- -100000.00 true true - -# bigint -> numeric(5,2) -query TBB -SELECT TRY_CAST(1::int8 AS numeric(5,2))::text, TRY_CAST(1::int8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1::int8 AS numeric(5,2))::text, TRY_CAST(NULL::int8 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(9223372036854775807::int8 AS numeric(5,2))::text, TRY_CAST(9223372036854775807::int8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(9223372036854775807::int8 AS numeric(5,2))::text, TRY_CAST(NULL::int8 AS numeric(5,2)) IS NULL ----- -9223372036854775807.00 true true - -query TBB -SELECT TRY_CAST((-1)::int8 AS numeric(5,2))::text, TRY_CAST((-1)::int8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST((-1)::int8 AS numeric(5,2))::text, TRY_CAST(NULL::int8 AS numeric(5,2)) IS NULL ----- --1.00 true true - -query TBB -SELECT TRY_CAST(3000000000::int8 AS numeric(5,2))::text, TRY_CAST(3000000000::int8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(3000000000::int8 AS numeric(5,2))::text, TRY_CAST(NULL::int8 AS numeric(5,2)) IS NULL ----- -3000000000.00 true true - -# uint2 -> numeric(5,2) -query TBB -SELECT TRY_CAST(1::uint2 AS numeric(5,2))::text, TRY_CAST(1::uint2 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1::uint2 AS numeric(5,2))::text, TRY_CAST(NULL::uint2 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(65535::uint2 AS numeric(5,2))::text, TRY_CAST(65535::uint2 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(65535::uint2 AS numeric(5,2))::text, TRY_CAST(NULL::uint2 AS numeric(5,2)) IS NULL ----- -65535.00 true true - -# uint4 -> numeric(5,2) -query TBB -SELECT TRY_CAST(1::uint4 AS numeric(5,2))::text, TRY_CAST(1::uint4 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1::uint4 AS numeric(5,2))::text, TRY_CAST(NULL::uint4 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(4294967295::uint4 AS numeric(5,2))::text, TRY_CAST(4294967295::uint4 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(4294967295::uint4 AS numeric(5,2))::text, TRY_CAST(NULL::uint4 AS numeric(5,2)) IS NULL ----- -4294967295.00 true true - -# uint8 -> numeric(5,2) -query TBB -SELECT TRY_CAST(1::uint8 AS numeric(5,2))::text, TRY_CAST(1::uint8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1::uint8 AS numeric(5,2))::text, TRY_CAST(NULL::uint8 AS numeric(5,2)) IS NULL ----- -1.00 true true - -query TBB -SELECT TRY_CAST(18446744073709551615::uint8 AS numeric(5,2))::text, TRY_CAST(18446744073709551615::uint8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(18446744073709551615::uint8 AS numeric(5,2))::text, TRY_CAST(NULL::uint8 AS numeric(5,2)) IS NULL ----- -18446744073709551615.00 true true - -# text -> numeric(5,2) -query TBB -SELECT TRY_CAST('1.5' AS numeric(5,2))::text, TRY_CAST('1.5' AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('1.5' AS numeric(5,2))::text, TRY_CAST(NULL AS numeric(5,2)) IS NULL ----- -1.50 true true - -statement error -SELECT CAST('abc' AS numeric(5,2)) - -query T -SELECT TRY_CAST('abc' AS numeric(5,2))::text ----- -NULL - -statement error -SELECT CAST('' AS numeric(5,2)) - -query T -SELECT TRY_CAST('' AS numeric(5,2))::text ----- -NULL - -# numeric -> numeric(5,2) -query TBB -SELECT TRY_CAST(1.5::numeric AS numeric(5,2))::text, TRY_CAST(1.5::numeric AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1.5::numeric AS numeric(5,2))::text, TRY_CAST(NULL::numeric AS numeric(5,2)) IS NULL ----- -1.50 true true - -statement error -SELECT CAST(1e38::numeric AS numeric(5,2)) - -query T -SELECT TRY_CAST(1e38::numeric AS numeric(5,2))::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::numeric AS numeric(5,2))::text, TRY_CAST((-1.5)::numeric AS numeric(5,2))::text IS NOT DISTINCT FROM CAST((-1.5)::numeric AS numeric(5,2))::text, TRY_CAST(NULL::numeric AS numeric(5,2)) IS NULL ----- --1.50 true true - -query TBB -SELECT TRY_CAST('NaN'::numeric AS numeric(5,2))::text, TRY_CAST('NaN'::numeric AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('NaN'::numeric AS numeric(5,2))::text, TRY_CAST(NULL::numeric AS numeric(5,2)) IS NULL ----- -NaN true true - -query TBB -SELECT TRY_CAST(123.456::numeric AS numeric(5,2))::text, TRY_CAST(123.456::numeric AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(123.456::numeric AS numeric(5,2))::text, TRY_CAST(NULL::numeric AS numeric(5,2)) IS NULL ----- -123.46 true true - -# real -> numeric(5,2) -query TBB -SELECT TRY_CAST(1.5::float4 AS numeric(5,2))::text, TRY_CAST(1.5::float4 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1.5::float4 AS numeric(5,2))::text, TRY_CAST(NULL::float4 AS numeric(5,2)) IS NULL ----- -1.50 true true - -statement error -SELECT CAST('3.4e38'::float4 AS numeric(5,2)) - -query T -SELECT TRY_CAST('3.4e38'::float4 AS numeric(5,2))::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float4 AS numeric(5,2))::text, TRY_CAST((-1.5)::float4 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST((-1.5)::float4 AS numeric(5,2))::text, TRY_CAST(NULL::float4 AS numeric(5,2)) IS NULL ----- --1.50 true true - -query TBB -SELECT TRY_CAST('NaN'::float4 AS numeric(5,2))::text, TRY_CAST('NaN'::float4 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('NaN'::float4 AS numeric(5,2))::text, TRY_CAST(NULL::float4 AS numeric(5,2)) IS NULL ----- -NaN true true - -statement error -SELECT CAST('Infinity'::float4 AS numeric(5,2)) - -query T -SELECT TRY_CAST('Infinity'::float4 AS numeric(5,2))::text ----- -NULL - -# double precision -> numeric(5,2) -query TBB -SELECT TRY_CAST(1.5::float8 AS numeric(5,2))::text, TRY_CAST(1.5::float8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST(1.5::float8 AS numeric(5,2))::text, TRY_CAST(NULL::float8 AS numeric(5,2)) IS NULL ----- -1.50 true true - -statement error -SELECT CAST('1.7e308'::float8 AS numeric(5,2)) - -query T -SELECT TRY_CAST('1.7e308'::float8 AS numeric(5,2))::text ----- -NULL - -query TBB -SELECT TRY_CAST((-1.5)::float8 AS numeric(5,2))::text, TRY_CAST((-1.5)::float8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST((-1.5)::float8 AS numeric(5,2))::text, TRY_CAST(NULL::float8 AS numeric(5,2)) IS NULL ----- --1.50 true true - -query TBB -SELECT TRY_CAST('NaN'::float8 AS numeric(5,2))::text, TRY_CAST('NaN'::float8 AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('NaN'::float8 AS numeric(5,2))::text, TRY_CAST(NULL::float8 AS numeric(5,2)) IS NULL ----- -NaN true true - -statement error -SELECT CAST('Infinity'::float8 AS numeric(5,2)) - -query T -SELECT TRY_CAST('Infinity'::float8 AS numeric(5,2))::text ----- -NULL - -# jsonb -> numeric(5,2) -query TBB -SELECT TRY_CAST('1'::jsonb AS numeric(5,2))::text, TRY_CAST('1'::jsonb AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('1'::jsonb AS numeric(5,2))::text, TRY_CAST(NULL::jsonb AS numeric(5,2)) IS NULL ----- -1.00 true true - -statement error -SELECT CAST('"x"'::jsonb AS numeric(5,2)) - -query T -SELECT TRY_CAST('"x"'::jsonb AS numeric(5,2))::text ----- -NULL - -statement error -SELECT CAST('[1]'::jsonb AS numeric(5,2)) - -query T -SELECT TRY_CAST('[1]'::jsonb AS numeric(5,2))::text ----- -NULL - -query TBB -SELECT TRY_CAST('1e30'::jsonb AS numeric(5,2))::text, TRY_CAST('1e30'::jsonb AS numeric(5,2))::text IS NOT DISTINCT FROM CAST('1e30'::jsonb AS numeric(5,2))::text, TRY_CAST(NULL::jsonb AS numeric(5,2)) IS NULL ----- -1000000000000000000000000000000.00 true true - -statement error -SELECT CAST('true'::jsonb AS numeric(5,2)) - -query T -SELECT TRY_CAST('true'::jsonb AS numeric(5,2))::text ----- -NULL - -statement error -SELECT CAST('null'::jsonb AS numeric(5,2)) - -query T -SELECT TRY_CAST('null'::jsonb AS numeric(5,2))::text ----- -NULL