Skip to content

Commit

Permalink
disallow table creation with out-of-range scale/width
Browse files Browse the repository at this point in the history
  • Loading branch information
dentiny committed Dec 5, 2024
1 parent 76681ba commit f08e29f
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 34 deletions.
43 changes: 43 additions & 0 deletions src/pgduckdb_ddl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,45 @@ static bool ctas_skip_data = false;
static bool top_level_ddl = true;
static ProcessUtility_hook_type prev_process_utility_hook = NULL;

// Get precision and scale from postgres type modifier.
static int
GetPgNumericPrecision(int32 typmod) {
return ((typmod - VARHDRSZ) >> 16) & 0xffff;
}
static int
GetPgNumericScale(int32 typmod) {
return (((typmod - VARHDRSZ) & 0x7ff) ^ 1024) - 1024;
}

// If column is of type "numeric", check whether it's acceptable for duckdb;
// If not, exception is thrown via `elog`.
static void
ValidateColumnNumericType(Form_pg_attribute &attribute) {
auto &type = attribute->atttypid;
if (type != NUMERICOID) {
return;
}
auto &typmod = attribute->atttypmod;
auto precision = GetPgNumericPrecision(typmod);
auto scale = GetPgNumericScale(typmod);
// duckdb's "numeric" type's max supported precision is 38.
if (typmod == -1 || precision < 0 || scale < 0 || precision > 38) {
elog(ERROR,
"Unsupported type when creating column: type precision %d with scale %d is not supported by duckdb, which "
"only allows maximum precision 38",
precision, scale);
}
}

// Validate new relation creation, if invalid throw exception via `elog`.
static void
ValidateRelationCreation(TupleDesc desc) {
for (int i = 0; i < desc->natts; i++) {
Form_pg_attribute attr = &desc->attrs[i];
ValidateColumnNumericType(attr);
}
}

static void
DuckdbHandleDDL(Node *parsetree) {
if (!pgduckdb::IsExtensionRegistered()) {
Expand Down Expand Up @@ -253,6 +292,10 @@ DECLARE_PG_FUNCTION(duckdb_create_table_trigger) {
elog(ERROR, "Expected single table to be created, but found %" PRIu64, static_cast<uint64_t>(SPI_processed));
}

// Check whether new table creation is supported in duckdb.
// TODO(hjiang): Same type validation should be applied to other DDL as well.
ValidateRelationCreation(SPI_tuptable->tupdesc);

HeapTuple tuple = SPI_tuptable->vals[0];
bool isnull;
Datum relid_datum = SPI_getbinval(tuple, SPI_tuptable->tupdesc, 1, &isnull);
Expand Down
6 changes: 2 additions & 4 deletions src/pgduckdb_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ ConvertNumericDatum(const duckdb::Value &value) {
return ConvertDoubleDatum(value);
}
NumericVar numeric_var;
D_ASSERT(value_type_id == duckdb::LogicalTypeId::DECIMAL ||
value_type_id == duckdb::LogicalTypeId::HUGEINT ||
D_ASSERT(value_type_id == duckdb::LogicalTypeId::DECIMAL || value_type_id == duckdb::LogicalTypeId::HUGEINT ||
value_type_id == duckdb::LogicalTypeId::UBIGINT);
auto physical_type = value.type().InternalType();
const bool is_decimal = value_type_id == duckdb::LogicalTypeId::DECIMAL;
Expand Down Expand Up @@ -766,8 +765,7 @@ ConvertPostgresToDuckColumnType(Form_pg_attribute &attribute) {
auto scale = numeric_typmod_scale(typmod);
// duckdb's "numeric" type's max supported precision is 38.
if (typmod == -1 || precision < 0 || scale < 0 || precision > 38) {
const auto user_type_info = duckdb::StringUtil::Format(
"type precision %d with scale %d", precision, scale);
const auto user_type_info = duckdb::StringUtil::Format("type precision %d with scale %d", precision, scale);
base_type = duckdb::LogicalType::USER(user_type_info);
} else {
base_type = duckdb::LogicalType::DECIMAL(precision, scale);
Expand Down
4 changes: 2 additions & 2 deletions test/regression/expected/array_type_support.out
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ SELECT * FROM float8_array_1d;
(4 rows)

-- NUMERIC (single dimension)
CREATE TABLE numeric_array_1d(a NUMERIC[]);
CREATE TABLE numeric_array_1d(a NUMERIC(2, 1)[]);
INSERT INTO numeric_array_1d SELECT CAST(a as NUMERIC[]) FROM (VALUES
('{1.1, 2.2, 3.3}'),
(NULL),
Expand Down Expand Up @@ -373,7 +373,7 @@ SELECT * FROM float8_array_2d;
(5 rows)

-- NUMERIC (two dimensions)
CREATE TABLE numeric_array_2d(a NUMERIC[][]);
CREATE TABLE numeric_array_2d(a NUMERIC(3, 1)[][]);
INSERT INTO numeric_array_2d VALUES
('{{1.1,2.2},{3.3,4.4}}'),
('{{5.5,6.6,7.7},{8.8,9.9,10.1}}'),
Expand Down
16 changes: 0 additions & 16 deletions test/regression/expected/type_support.out
Original file line number Diff line number Diff line change
Expand Up @@ -201,21 +201,6 @@ SELECT * FROM float8_tbl;
4.582345020342342e+20
(3 rows)

-- NUMERIC as DOUBLE
CREATE TABLE numeric_as_double(a NUMERIC);
INSERT INTO numeric_as_double SELECT a FROM (VALUES
(0.234234234),
(NULL),
(458234502034234234234.000012)
) t(a);
SELECT * FROM numeric_as_double;
a
-----------------------
0.234234234

4.582345020342342e+20
(3 rows)

-- NUMERIC with a physical type of SMALLINT
CREATE TABLE smallint_numeric(a NUMERIC(4, 2));
INSERT INTO smallint_numeric SELECT a FROM (VALUES
Expand Down Expand Up @@ -330,7 +315,6 @@ DROP TABLE timestamp_tbl;
DROP TABLE timestamptz_tbl;
DROP TABLE float4_tbl;
DROP TABLE float8_tbl;
DROP TABLE numeric_as_double;
DROP TABLE smallint_numeric;
DROP TABLE integer_numeric;
DROP TABLE bigint_numeric;
Expand Down
4 changes: 2 additions & 2 deletions test/regression/sql/array_type_support.sql
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ INSERT INTO float8_array_1d SELECT CAST(a as FLOAT8[]) FROM (VALUES
SELECT * FROM float8_array_1d;

-- NUMERIC (single dimension)
CREATE TABLE numeric_array_1d(a NUMERIC[]);
CREATE TABLE numeric_array_1d(a NUMERIC(2, 1)[]);
INSERT INTO numeric_array_1d SELECT CAST(a as NUMERIC[]) FROM (VALUES
('{1.1, 2.2, 3.3}'),
(NULL),
Expand Down Expand Up @@ -226,7 +226,7 @@ INSERT INTO float8_array_2d VALUES
SELECT * FROM float8_array_2d;

-- NUMERIC (two dimensions)
CREATE TABLE numeric_array_2d(a NUMERIC[][]);
CREATE TABLE numeric_array_2d(a NUMERIC(3, 1)[][]);
INSERT INTO numeric_array_2d VALUES
('{{1.1,2.2},{3.3,4.4}}'),
('{{5.5,6.6,7.7},{8.8,9.9,10.1}}'),
Expand Down
10 changes: 0 additions & 10 deletions test/regression/sql/type_support.sql
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,6 @@ INSERT INTO float8_tbl SELECT CAST(a AS FLOAT8) FROM (VALUES
) t(a);
SELECT * FROM float8_tbl;

-- NUMERIC as DOUBLE
CREATE TABLE numeric_as_double(a NUMERIC);
INSERT INTO numeric_as_double SELECT a FROM (VALUES
(0.234234234),
(NULL),
(458234502034234234234.000012)
) t(a);
SELECT * FROM numeric_as_double;

-- NUMERIC with a physical type of SMALLINT
CREATE TABLE smallint_numeric(a NUMERIC(4, 2));
INSERT INTO smallint_numeric SELECT a FROM (VALUES
Expand Down Expand Up @@ -171,7 +162,6 @@ DROP TABLE timestamp_tbl;
DROP TABLE timestamptz_tbl;
DROP TABLE float4_tbl;
DROP TABLE float8_tbl;
DROP TABLE numeric_as_double;
DROP TABLE smallint_numeric;
DROP TABLE integer_numeric;
DROP TABLE bigint_numeric;
Expand Down

0 comments on commit f08e29f

Please sign in to comment.