Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the PostgreSQL domain #532

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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
38 changes: 35 additions & 3 deletions src/pgduckdb_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extern "C" {

#include "pgduckdb/pgduckdb_filter.hpp"
#include "pgduckdb/pgduckdb_detoast.hpp"
#include "pgduckdb/pgduckdb_process_lock.hpp"

namespace pgduckdb {

Expand Down Expand Up @@ -917,10 +918,41 @@ ConvertPostgresToBaseDuckColumnType(Form_pg_attribute &attribute) {

duckdb::LogicalType
ConvertPostgresToDuckColumnType(Form_pg_attribute &attribute) {
int dimensions = -1;
Oid save_typoid = InvalidOid;
std::lock_guard<std::mutex> lock(GlobalProcessLock::GetLock());

if (get_typtype(attribute->atttypid) == TYPTYPE_DOMAIN) {
/* If the domain is an array type, you need to obtain the corresponding array dimension information */
if (type_is_array_domain(attribute->atttypid)) {
HeapTuple typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(attribute->atttypid));
Leo-XM-Zeng marked this conversation as resolved.
Show resolved Hide resolved
dimensions = ((Form_pg_type)GETSTRUCT(typeTuple))->typndims;
ReleaseSysCache(typeTuple);
}

save_typoid = attribute->atttypid;
/* It is a domain type that needs to be reduced to its base type */
attribute->atttypid = getBaseType(attribute->atttypid);
} else if (type_is_array(attribute->atttypid)) {
Oid elem_type = get_base_element_type(attribute->atttypid);
if (OidIsValid(elem_type) && get_typtype(elem_type) == TYPTYPE_DOMAIN) {
save_typoid = attribute->atttypid;
/* When the member type of an array is domain, you need to build a base array type */
attribute->atttypid = get_array_type(getBaseType(elem_type));
}
}

auto base_type = ConvertPostgresToBaseDuckColumnType(attribute);
auto dimensions = attribute->attndims;
if (dimensions == 0) {
return base_type;

if (save_typoid != InvalidOid) {
attribute->atttypid = save_typoid;
}

if (dimensions == -1) {
dimensions = attribute->attndims;
if (dimensions == 0) {
return base_type;
}
}

for (int i = 0; i < dimensions; i++) {
Expand Down
109 changes: 109 additions & 0 deletions test/regression/expected/domain.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
create domain domainvarchar varchar(5) check (value is not null);
create domain domainnumeric numeric(8,2) check (value is not null);
create domain domainint4 int4 check (value > 0);
create domain domaintext text check (value is not null);
-- Test tables using domains
create table basictest
( testint4 domainint4
, testtext domaintext
, testvarchar domainvarchar
, testnumeric domainnumeric
);
INSERT INTO basictest values ('88', 'haha', 'short', '123.12'); -- Good
INSERT INTO basictest values ('88', 'haha', 'short text', '123.12'); -- Bad varchar
ERROR: value too long for type character varying(5)
INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate numeric
-- domain check
INSERT INTO basictest values ('-1', 'haha', 'short', '123.1212'); -- Bad int4
ERROR: value for domain domainint4 violates check constraint "domainint4_check"
INSERT INTO basictest values ('88', NULL, 'short', '123.1212'); -- Bad text
ERROR: value for domain domaintext violates check constraint "domaintext_check"
INSERT INTO basictest values ('88', 'haha', NULL, '123.1212'); -- Bad varchar
ERROR: value for domain domainvarchar violates check constraint "domainvarchar_check"
INSERT INTO basictest values ('88', 'haha', 'short', NULL); -- Bad numeric
ERROR: value for domain domainnumeric violates check constraint "domainnumeric_check"
SELECT 5::domainint4; -- Good
domainint4
------------
5
(1 row)

SELECT (-5)::domainint4; -- Bad int4
ERROR: value for domain domainint4 violates check constraint "domainint4_check"
-- not support. It will be converted to the following statement
-- SELECT ('-5'::integer)::domainint4 AS domainint4 FROM pgduckdb.xxx.basictest
SELECT (-5)::domainint4 FROM basictest;
WARNING: (PGDuckDB/CreatePlan) Prepared query returned an error: 'Catalog Error: Type with name domainint4 does not exist!
Did you mean "tinyint"?
ERROR: value for domain domainint4 violates check constraint "domainint4_check"
select * from basictest;
testint4 | testtext | testvarchar | testnumeric
----------+----------+-------------+-------------
88 | haha | short | 123.12
88 | haha | short | 123.12
(2 rows)

select testtext || testvarchar as concat, testnumeric + 42 as sum
from basictest;
concat | sum
-----------+---------
hahashort | 165.120
hahashort | 165.120
(2 rows)

select * from basictest where testtext = 'haha';
testint4 | testtext | testvarchar | testnumeric
----------+----------+-------------+-------------
88 | haha | short | 123.12
88 | haha | short | 123.12
(2 rows)

select * from basictest where testvarchar = 'short';
testint4 | testtext | testvarchar | testnumeric
----------+----------+-------------+-------------
88 | haha | short | 123.12
88 | haha | short | 123.12
(2 rows)

-- array_domain
create domain domain_int_array as INT[];
CREATE TABLE domain_int_array_1d(a domain_int_array);
INSERT INTO domain_int_array_1d SELECT CAST(a as domain_int_array) FROM (VALUES
('{1, 2, 3}'),
(NULL),
('{4, 5, NULL, 7}'),
('{}')
) t(a);
SELECT * FROM domain_int_array_1d;
a
--------------
{1,2,3}

{4,5,NULL,7}
{}
(4 rows)

CREATE TABLE domain_int_array_2d(a domainint4[]);
INSERT INTO domain_int_array_2d SELECT CAST(a as domain_int_array) FROM (VALUES
('{1, 2, 3}'),
(NULL),
('{4, 5, NULL, 7}'),
('{}')
) t(a);
SELECT * FROM domain_int_array_2d;
a
--------------
{1,2,3}

{4,5,NULL,7}
{}
(4 rows)

drop table domain_int_array_2d;
drop table domain_int_array_1d;
drop domain domain_int_array;
drop table basictest;
drop domain domainvarchar restrict;
drop domain domainnumeric restrict;
drop domain domainint4 restrict;
drop domain domaintext;
1 change: 1 addition & 0 deletions test/regression/schedule
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ test: hugeint_conversion
test: read_functions
test: duckdb_only_functions
test: duckdb_recycle
test: domain
test: cte
test: create_schema
test: create_table_as
Expand Down
65 changes: 65 additions & 0 deletions test/regression/sql/domain.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
create domain domainvarchar varchar(5) check (value is not null);
create domain domainnumeric numeric(8,2) check (value is not null);
create domain domainint4 int4 check (value > 0);
create domain domaintext text check (value is not null);

-- Test tables using domains
create table basictest
( testint4 domainint4
, testtext domaintext
, testvarchar domainvarchar
, testnumeric domainnumeric
);

INSERT INTO basictest values ('88', 'haha', 'short', '123.12'); -- Good
INSERT INTO basictest values ('88', 'haha', 'short text', '123.12'); -- Bad varchar
INSERT INTO basictest values ('88', 'haha', 'short', '123.1212'); -- Truncate numeric

-- domain check
INSERT INTO basictest values ('-1', 'haha', 'short', '123.1212'); -- Bad int4
INSERT INTO basictest values ('88', NULL, 'short', '123.1212'); -- Bad text
INSERT INTO basictest values ('88', 'haha', NULL, '123.1212'); -- Bad varchar
INSERT INTO basictest values ('88', 'haha', 'short', NULL); -- Bad numeric
SELECT 5::domainint4; -- Good
SELECT (-5)::domainint4; -- Bad int4

-- not support. It will be converted to the following statement
-- SELECT ('-5'::integer)::domainint4 AS domainint4 FROM pgduckdb.xxx.basictest
SELECT (-5)::domainint4 FROM basictest;

select * from basictest;

select testtext || testvarchar as concat, testnumeric + 42 as sum
from basictest;

select * from basictest where testtext = 'haha';
select * from basictest where testvarchar = 'short';

-- array_domain
create domain domain_int_array as INT[];
CREATE TABLE domain_int_array_1d(a domain_int_array);
INSERT INTO domain_int_array_1d SELECT CAST(a as domain_int_array) FROM (VALUES
('{1, 2, 3}'),
(NULL),
('{4, 5, NULL, 7}'),
('{}')
) t(a);
SELECT * FROM domain_int_array_1d;

CREATE TABLE domain_int_array_2d(a domainint4[]);
INSERT INTO domain_int_array_2d SELECT CAST(a as domain_int_array) FROM (VALUES
('{1, 2, 3}'),
(NULL),
('{4, 5, NULL, 7}'),
('{}')
) t(a);
SELECT * FROM domain_int_array_2d;

drop table domain_int_array_2d;
drop table domain_int_array_1d;
drop domain domain_int_array;
drop table basictest;
drop domain domainvarchar restrict;
drop domain domainnumeric restrict;
drop domain domainint4 restrict;
drop domain domaintext;