Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-cloudberry-rocky8.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ jobs:
"contrib/pgcrypto:installcheck",
"contrib/pgstattuple:installcheck",
"contrib/tablefunc:installcheck",
"contrib/try_convert:installcheck",
"contrib/passwordcheck:installcheck",
"contrib/pg_buffercache:installcheck",
"contrib/sslinfo:installcheck"]
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-cloudberry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ jobs:
"contrib/pgcrypto:installcheck",
"contrib/pgstattuple:installcheck",
"contrib/tablefunc:installcheck",
"contrib/try_convert:installcheck",
"contrib/passwordcheck:installcheck",
"contrib/pg_buffercache:installcheck",
"contrib/sslinfo:installcheck"]
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-deb-cloudberry-ubuntu24.04.yml
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ jobs:
"contrib/pgcrypto:installcheck",
"contrib/pgstattuple:installcheck",
"contrib/tablefunc:installcheck",
"contrib/try_convert:installcheck",
"contrib/passwordcheck:installcheck",
"contrib/pg_buffercache:installcheck",
"contrib/sslinfo:installcheck"]
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build-deb-cloudberry.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ jobs:
"contrib/pgcrypto:installcheck",
"contrib/pgstattuple:installcheck",
"contrib/tablefunc:installcheck",
"contrib/try_convert:installcheck",
"contrib/passwordcheck:installcheck",
"contrib/pg_buffercache:installcheck",
"contrib/sslinfo:installcheck"]
Expand Down
2 changes: 2 additions & 0 deletions GNUmakefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ all:
$(MAKE) -C contrib/btree_gin all
$(MAKE) -C contrib/pg_trgm all
$(MAKE) -C contrib/tablefunc all
$(MAKE) -C contrib/try_convert all
$(MAKE) -C contrib/passwordcheck all
$(MAKE) -C contrib/pg_buffercache all
ifeq ($(with_openssl), yes)
Expand Down Expand Up @@ -77,6 +78,7 @@ install:
$(MAKE) -C contrib/btree_gin $@
$(MAKE) -C contrib/pg_trgm $@
$(MAKE) -C contrib/tablefunc $@
$(MAKE) -C contrib/try_convert $@
$(MAKE) -C contrib/passwordcheck $@
$(MAKE) -C contrib/pg_buffercache $@
ifeq ($(enable_pax), yes)
Expand Down
1 change: 1 addition & 0 deletions contrib/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ SUBDIRS = \
tablefunc \
tcn \
test_decoding \
try_convert \
tsm_system_rows \
tsm_system_time \
unaccent \
Expand Down
6 changes: 6 additions & 0 deletions contrib/try_convert/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated subdirectories
/results/
/sql/
/expected/
/input/
/output/
52 changes: 52 additions & 0 deletions contrib/try_convert/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# contrib/try_convert/Makefile

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

MODULE_big = try_convert
OBJS = try_convert.o $(WIN32RES)

EXTENSION = try_convert
DATA = try_convert--1.0.sql
PGFILEDESC = "try_convert - function for type cast"

# The test cases are derived from the catalog files and from the sample values
# in data/, so they are generated by generate-tests rather than stored in the
# tree, together with the sql/ and expected/ files pg_regress derives from them.
REGRESS = try_convert
REGRESS_PREP = generate-tests
EXTRA_CLEAN = input output sql expected results

ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/try_convert
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

.PHONY: generate-tests
generate-tests:
$(MKDIR_P) input output
python3 scripts/generate_test.py

.PHONY: verify
verify: generate-tests
python3 scripts/verify.py
116 changes: 116 additions & 0 deletions contrib/try_convert/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 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);
```


40 changes: 40 additions & 0 deletions contrib/try_convert/data/corr_date.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
13:35:45
13:35:90
13:121:45
2323:35:45
13:35:-45
13:-35:45
-13:35:45

12:52:43 AM
15:52:43 AM
12:152:43 AM
12:52:143 AM
12:52:43 PM
15:52:43 PM
12:152:43 PM
12:52:143 PM

21:52:07 JST
21:52:07 MSSSSSK

21:52:07+03:02
21:52:07+33:02
21:52:07-33:02
21:52:07+03:222

1982-06-27 18:52:43
1982-06-32 18:52:43
1982-32-27 18:52:43
99999999999999-06-27 18:52:43

1982-12-12 01:01:59+09:00
1982-12-12 01:01:59+29:00

1982-06-27
1982-06-32
1982-32-27
999999999999999-06-27

11-30-0002 BC
11-30-200000000 BC
17 changes: 17 additions & 0 deletions contrib/try_convert/data/corr_float4.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
2
333333333
333333333333333333333
5555555555555555555555555555555555555555555
dfgdg435fw2342rf445
6.6332342343243242342342342342343243244324
354345345345435345345345345.23467567867325345345
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7
Twenty-two
12234441231.13123123
485728435843759345135.4324234234
4823849249230.f
19203234723472-e102
0000000000000.00000000000000000001
234324+e4443
17 changes: 17 additions & 0 deletions contrib/try_convert/data/corr_float8.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
2
333333333
333333333333333333333
5555555555555555555555555555555555555555555
dfgdg435fw2342rf445
6.6332342343243242342342342342343243244324
354345345345435345345345345.23467567867325345345
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7
Twenty-two
12234441231.13123123
485728435843759345135.4324234234
4823849249230.f
19203234723472-e102
0000000000000.00000000000000000001
234324+e4443
17 changes: 17 additions & 0 deletions contrib/try_convert/data/corr_int2.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
2
333333333
333333333333333333333
5555555555555555555555555555555555555555555
dfgdg435fw2342rf445
6.6332342343243242342342342342343243244324
354345345345435345345345345.23467567867325345345
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7
Twenty-two
12234441231.13123123
485728435843759345135.4324234234
4823849249230.f
19203234723472-e102
0000000000000.00000000000000000001
234324+e4443
17 changes: 17 additions & 0 deletions contrib/try_convert/data/corr_int4.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
2
333333333
333333333333333333333
5555555555555555555555555555555555555555555
dfgdg435fw2342rf445
6.6332342343243242342342342342343243244324
354345345345435345345345345.23467567867325345345
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7
Twenty-two
12234441231.13123123
485728435843759345135.4324234234
4823849249230.f
19203234723472-e102
0000000000000.00000000000000000001
234324+e4443
17 changes: 17 additions & 0 deletions contrib/try_convert/data/corr_int8.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
1
2
333333333
333333333333333333333
5555555555555555555555555555555555555555555
dfgdg435fw2342rf445
6.6332342343243242342342342342343243244324
354345345345435345345345345.23467567867325345345
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7.7
Twenty-two
12234441231.13123123
485728435843759345135.4324234234
4823849249230.f
19203234723472-e102
0000000000000.00000000000000000001
234324+e4443
18 changes: 18 additions & 0 deletions contrib/try_convert/data/corr_json.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{

[
{"world":"CC", "query":"AA", "line":14, "disabled":false, "date":"CAC", "coauthors":"AB"}
{"world":"CCquery":"AA"}
{,,,,,,}
{"world":"CC",,,,, "query":"AA", "line":14, "disabled":false, "date":"CAC", "coauthors":"AB"}
{"world":"CC", "query"::"AA", "line":14, "disabled":false, "date":"CAC", "coauthors":"AB"}

{"world"}
{"world":"CC", "query":["AA":"BB", "sds"], "line":14, "disabled":false, "date":"CAC", "coauthors":"AB"}
}}}}}}}}
[[[[[[]]]]]]
{[]}
[{}]
][][][][][][]
{}}{}{}}}{{{}{}{}{}}}}
kfgbidsfgadgbdfiugbhdiug
Loading
Loading