blob: e1f20d9bf5f93ce61f5a6dd85702256b0b78b9b0 [file] [view]
# TRY_CONVERT
TRY_CONVERT is Greenplum/Cloudberry extension, which adds function for error-safe type cast like [TRY_CAST from SQL-Server](https://learn.microsoft.com/ru-ru/sql/t-sql/functions/try-cast-transact-sql?view=sql-server-ver16)
## Usage
```
TRY_CONVERT(SOURCE_VALUE, DEFAULT_VALUE::TARGET_TYPE)
returns (VALUE_IN_TARGET_TYPE or DEFAULT_VALUE)
```
```
TRY_CONVERT('42'::text, NULL::int2) -- returns 42::int2
TRY_CONVERT('42d'::text, NULL::int2) -- returns NULL::int2
TRY_CONVERT('42d'::text, 1234::int2) -- returns 1234::int2
```
### Extension's type casts
Casting from extensions types is able only for extensions:
- hstore
- citext
To enable casting from hstore and citext types use, `add_type_for_try_convert(regtype)` function
## Error handling
The cast is executed inside a `PG_TRY()` block: when the cast function reports
a failure, the error is discarded and the default value is returned instead.
Query cancellation and assertion failures are never swallowed, they are
re-thrown, the same way plpgsql handles `EXCEPTION WHEN others`.
The long-term plan is to replace the `PG_TRY()` block by the "soft" error
handling concept introduced in Postgres 17 (https://github.com/postgres/postgres/commit/ccff2d20ed9622815df2a7deffce8a7b14830965),
which lets a datatype input function report a conversion failure without
throwing. That concept was spread on data types in [21be368
Preview](https://github.com/open-gpdb/gpdb/commit/21be3688729ec4468ffd083da197721860fa2cbd) and [d31f362
](https://github.com/open-gpdb/gpdb/commit/d31f362250105e456961c2c9249693e42e67eca9) commits.
It requires converting the datatype input functions of Cloudberry first.
## Why signature is so strange?
Greenplum/Cloudberry function polymorphism accept to have polymorphic functions only one any type in signature.
## Supported casts
Values Cast
Types with typemod
Array-Array Cast
To Domain type cast
An unsupported or non-existing cast is a query error, it is not turned into the
default value: only failures caused by the converted data are.
## Tests
The regression test is generated out of the catalog files and of the sample
values in `data/`, so it is not stored in the repository. `make installcheck`
generates it into `input/` and `output/` and then runs it:
```
make -C contrib/try_convert installcheck
```
`make -C contrib/try_convert generate-tests` generates it without running it.
## Benchmark results by pgbench
| | without errors | with errors |
| --- | --- | --- |
| cast | 299.346 | fails |
| try_convert | 984.280 | 1004.524 |
| sql | 1384.784 | 5787.115 |
| sql execute | 5843.220 | 5898.813 |
SQL version:
```
CREATE OR REPLACE FUNCTION try_convert_into_int(_in text, d int2) RETURNS int2
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN CAST(_in AS int2);
EXCEPTION WHEN others THEN
RETURN d;
END
$func$;
```
SQL with execute version:
```
CREATE OR REPLACE FUNCTION try_convert_by_sql(_in text, INOUT _out ANYELEMENT)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT %L::%s', $1, pg_typeof(_out))
INTO _out;
EXCEPTION WHEN others THEN
-- do nothing: _out already carries default
END
$func$;
```
Data:
```
drop table if exists text_ints; create table text_ints (n text);
Insert into text_ints(n) select (random()*1000)::int4::text from generate_series(1,1000000);
drop table if exists text_error_ints; create table text_error_ints (n text);
Insert into text_error_ints(n) select (random()*1000000)::int8::text from generate_series(1,1000000);
```