diff --git a/src/include/postgres_binary_file_reader.hpp b/src/include/postgres_binary_file_reader.hpp index 6406656b5..1aff03604 100644 --- a/src/include/postgres_binary_file_reader.hpp +++ b/src/include/postgres_binary_file_reader.hpp @@ -22,7 +22,8 @@ class PostgresBinaryFileReader { static constexpr idx_t COPY_FILE_HEADER_SIZE = PostgresConversion::COPY_HEADER_LENGTH + 8; PostgresBinaryFileReader(ClientContext &context, const string &file_path, vector types, - vector postgres_types, idx_t buffer_size = DEFAULT_BUFFER_SIZE); + vector postgres_types, PostgresTypeConfig type_config, + idx_t buffer_size = DEFAULT_BUFFER_SIZE); bool ReadChunk(DataChunk &output); diff --git a/src/include/postgres_binary_parser.hpp b/src/include/postgres_binary_parser.hpp index 04883144b..e6b417fa9 100644 --- a/src/include/postgres_binary_parser.hpp +++ b/src/include/postgres_binary_parser.hpp @@ -19,7 +19,8 @@ namespace duckdb { class PostgresBinaryParser { public: - PostgresBinaryParser(vector types, vector postgres_types); + PostgresBinaryParser(vector types, vector postgres_types, + PostgresTypeConfig type_config); void SetBuffer(data_ptr_t buf, idx_t len); bool ReadChunk(DataChunk &output, const vector &column_ids); @@ -40,6 +41,7 @@ class PostgresBinaryParser { vector types; vector postgres_types; + PostgresTypeConfig type_config; private: template @@ -151,14 +153,25 @@ class PostgresBinaryParser { PostgresDecimalConfig ReadDecimalConfig(); + static PostgresDecimalKind NonFiniteDecimalKindFromSign(uint16_t dec_sign); + + static string NonFiniteDecimalKindToString(PostgresDecimalKind kind); + template - T ReadDecimal() { + PostgresDecimal ReadDecimal() { // this is wild auto config = ReadDecimalConfig(); + + // we do not support non-finite numerics, need to return NULL or throw + if (config.sign == NUMERIC_NAN || config.sign == NUMERIC_PINF || config.sign == NUMERIC_NINF) { + PostgresDecimalKind kind = NonFiniteDecimalKindFromSign(config.sign); + return PostgresDecimal(kind); + } + auto scale_POWER = OP::GetPowerOfTen(config.scale); if (config.ndigits == 0) { - return 0; + return PostgresDecimal(static_cast(0)); } T integral_part = 0, fractional_part = 0; @@ -210,7 +223,8 @@ class PostgresBinaryParser { // finally auto base_res = OP::Finalize(config, integral_part + fractional_part); - return (config.is_negative ? -base_res : base_res); + auto val = (config.is_negative ? -base_res : base_res); + return PostgresDecimal(val); } void ReadGeometry(const LogicalType &type, const PostgresType &postgres_type, Vector &out_vec, idx_t output_offset); @@ -219,6 +233,8 @@ class PostgresBinaryParser { uint32_t current_count, uint32_t dimensions[], uint32_t ndim); void ReadValue(const LogicalType &type, const PostgresType &postgres_type, Vector &out_vec, idx_t output_offset); + + bool CheckDecimalKindSetNull(Vector &out_vec, idx_t output_offset, PostgresDecimalKind dec_kind); }; } // namespace duckdb diff --git a/src/include/postgres_conversion.hpp b/src/include/postgres_conversion.hpp index 900ee2c66..fc1467b59 100644 --- a/src/include/postgres_conversion.hpp +++ b/src/include/postgres_conversion.hpp @@ -99,6 +99,21 @@ struct PostgresDecimalConfig { uint16_t ndigits; int16_t weight; bool is_negative; + uint16_t sign; +}; + +enum class PostgresDecimalKind { ORDINARY, NOT_A_NUMBER, POSITIVE_INFINITY, NEGATIVE_INFINITY }; + +template +struct PostgresDecimal { + T value; + PostgresDecimalKind kind; + + PostgresDecimal(T val) : value(val), kind(PostgresDecimalKind::ORDINARY) { + } + + PostgresDecimal(PostgresDecimalKind kind_p) : value(0), kind(kind_p) { + } }; struct PostgresConversion { diff --git a/src/include/postgres_scanner.hpp b/src/include/postgres_scanner.hpp index f26740efc..085ef4283 100644 --- a/src/include/postgres_scanner.hpp +++ b/src/include/postgres_scanner.hpp @@ -54,6 +54,7 @@ struct PostgresBindData : public dbconnector::BindData { //! DML without RETURNING). InitGlobalState executes it and returns a single-row Success result. bool command_only = false; idx_t max_threads = 1; + PostgresTypeConfig type_config; dbconnector::optimizer::OrderByAndLimitBindData order_by_and_limit_bind_data; dbconnector::optimizer::AggregateBindData aggregate_bind_data; diff --git a/src/include/postgres_utils.hpp b/src/include/postgres_utils.hpp index 9fbcc2786..7ebde316f 100644 --- a/src/include/postgres_utils.hpp +++ b/src/include/postgres_utils.hpp @@ -28,6 +28,7 @@ struct PostgresTypeData { struct PostgresTypeConfig { bool array_as_varchar = false; bool numeric_as_varchar = false; + bool numeric_nan_as_null = true; static PostgresTypeConfig FromContext(optional_ptr context); }; diff --git a/src/postgres_binary_copy.cpp b/src/postgres_binary_copy.cpp index 0be525e5c..8f25e18b3 100644 --- a/src/postgres_binary_copy.cpp +++ b/src/postgres_binary_copy.cpp @@ -141,8 +141,9 @@ static unique_ptr PostgresBinaryReadInitGlobal(ClientC TableFunctionInitInput &input) { auto &bind_data = input.bind_data->Cast(); auto result = make_uniq(); + PostgresTypeConfig type_config = PostgresTypeConfig::FromContext(context); result->reader = make_uniq(context, bind_data.file_path, bind_data.types, - bind_data.postgres_types, bind_data.buffer_size); + bind_data.postgres_types, type_config, bind_data.buffer_size); return std::move(result); } diff --git a/src/postgres_binary_file_reader.cpp b/src/postgres_binary_file_reader.cpp index b106f71b2..9b0b8a5d2 100644 --- a/src/postgres_binary_file_reader.cpp +++ b/src/postgres_binary_file_reader.cpp @@ -14,10 +14,10 @@ static vector MakeSequentialColumnIds(idx_t count) { PostgresBinaryFileReader::PostgresBinaryFileReader(ClientContext &context, const string &file_path, vector types_p, vector postgres_types_p, - idx_t buffer_size_p) - : column_ids(MakeSequentialColumnIds(types_p.size())), parser(std::move(types_p), std::move(postgres_types_p)), - buffer_size(buffer_size_p), file_offset(0), leftover(0), leftover_offset(0), finished(false), - header_scanned(false) { + PostgresTypeConfig type_config, idx_t buffer_size_p) + : column_ids(MakeSequentialColumnIds(types_p.size())), + parser(std::move(types_p), std::move(postgres_types_p), type_config), buffer_size(buffer_size_p), file_offset(0), + leftover(0), leftover_offset(0), finished(false), header_scanned(false) { auto &fs = FileSystem::GetFileSystem(context); file_handle = fs.OpenFile(file_path, FileFlags::FILE_FLAGS_READ); file_size = file_handle->GetFileSize(); diff --git a/src/postgres_binary_parser.cpp b/src/postgres_binary_parser.cpp index 8cd68d1b6..63fd24832 100644 --- a/src/postgres_binary_parser.cpp +++ b/src/postgres_binary_parser.cpp @@ -7,8 +7,9 @@ namespace duckdb { -PostgresBinaryParser::PostgresBinaryParser(vector types_p, vector postgres_types_p) - : types(std::move(types_p)), postgres_types(std::move(postgres_types_p)) { +PostgresBinaryParser::PostgresBinaryParser(vector types_p, vector postgres_types_p, + PostgresTypeConfig type_config_p) + : types(std::move(types_p)), postgres_types(std::move(postgres_types_p)), type_config(type_config_p) { } void PostgresBinaryParser::SetBuffer(data_ptr_t buf, idx_t len) { @@ -81,6 +82,7 @@ PostgresDecimalConfig PostgresBinaryParser::ReadDecimalConfig() { sign == NUMERIC_NEG)) { throw NotImplementedException("Postgres numeric NA/Inf"); } + config.sign = sign; config.is_negative = sign == NUMERIC_NEG; config.scale = ReadInteger(); @@ -188,7 +190,25 @@ void PostgresBinaryParser::ReadValue(const LogicalType &type, const PostgresType break; case LogicalTypeId::DOUBLE: { if (postgres_type.info == PostgresTypeAnnotation::NUMERIC_AS_DOUBLE) { - FlatVector::GetDataMutable(out_vec)[output_offset] = ReadDecimal(); + PostgresDecimal dec = ReadDecimal(); + double double_value = 0; + switch (dec.kind) { + case PostgresDecimalKind::ORDINARY: + double_value = dec.value; + break; + case PostgresDecimalKind::NOT_A_NUMBER: + double_value = std::numeric_limits::quiet_NaN(); + break; + case PostgresDecimalKind::POSITIVE_INFINITY: + double_value = std::numeric_limits::infinity(); + break; + case PostgresDecimalKind::NEGATIVE_INFINITY: + double_value = -std::numeric_limits::infinity(); + break; + default: + throw InvalidInputException("Unsupported decimal kind"); + } + FlatVector::GetDataMutable(out_vec)[output_offset] = double_value; break; } D_ASSERT(value_len == sizeof(double)); @@ -235,19 +255,34 @@ void PostgresBinaryParser::ReadValue(const LogicalType &type, const PostgresType throw InvalidInputException("Need at least 8 bytes to read a Postgres decimal. Got %d", value_len); } switch (type.InternalType()) { - case PhysicalType::INT16: - FlatVector::GetDataMutable(out_vec)[output_offset] = ReadDecimal(); + case PhysicalType::INT16: { + PostgresDecimal dec = ReadDecimal(); + if (!CheckDecimalKindSetNull(out_vec, output_offset, dec.kind)) { + FlatVector::GetDataMutable(out_vec)[output_offset] = dec.value; + } break; - case PhysicalType::INT32: - FlatVector::GetDataMutable(out_vec)[output_offset] = ReadDecimal(); + } + case PhysicalType::INT32: { + PostgresDecimal dec = ReadDecimal(); + if (!CheckDecimalKindSetNull(out_vec, output_offset, dec.kind)) { + FlatVector::GetDataMutable(out_vec)[output_offset] = dec.value; + } break; - case PhysicalType::INT64: - FlatVector::GetDataMutable(out_vec)[output_offset] = ReadDecimal(); + } + case PhysicalType::INT64: { + PostgresDecimal dec = ReadDecimal(); + if (!CheckDecimalKindSetNull(out_vec, output_offset, dec.kind)) { + FlatVector::GetDataMutable(out_vec)[output_offset] = dec.value; + } break; - case PhysicalType::INT128: - FlatVector::GetDataMutable(out_vec)[output_offset] = - ReadDecimal(); + } + case PhysicalType::INT128: { + PostgresDecimal dec = ReadDecimal(); + if (!CheckDecimalKindSetNull(out_vec, output_offset, dec.kind)) { + FlatVector::GetDataMutable(out_vec)[output_offset] = dec.value; + } break; + } default: throw InvalidInputException("Unsupported decimal storage type"); } @@ -387,4 +422,42 @@ void PostgresBinaryParser::ReadValue(const LogicalType &type, const PostgresType } } +PostgresDecimalKind PostgresBinaryParser::NonFiniteDecimalKindFromSign(uint16_t dec_sign) { + if (dec_sign == NUMERIC_NAN) { + return PostgresDecimalKind::NOT_A_NUMBER; + } + if (dec_sign == NUMERIC_PINF) { + return PostgresDecimalKind::POSITIVE_INFINITY; + } + if (dec_sign == NUMERIC_NINF) { + return PostgresDecimalKind::NEGATIVE_INFINITY; + } + throw InvalidInputException("Unsupported unbound NUMERIC sign: %u", static_cast(dec_sign)); +} + +string PostgresBinaryParser::NonFiniteDecimalKindToString(PostgresDecimalKind kind) { + switch (kind) { + case PostgresDecimalKind::NOT_A_NUMBER: + return "NaN"; + case PostgresDecimalKind::POSITIVE_INFINITY: + return "Infinity"; + case PostgresDecimalKind::NEGATIVE_INFINITY: + return "-Infinity"; + default: + throw InvalidInputException("Unsupported unbound NUMERIC kind"); + } +} + +bool PostgresBinaryParser::CheckDecimalKindSetNull(Vector &out_vec, idx_t output_offset, PostgresDecimalKind dec_kind) { + if (dec_kind == PostgresDecimalKind::ORDINARY) { + return false; + } + if (!type_config.numeric_nan_as_null) { + string kind_str = NonFiniteDecimalKindToString(dec_kind); + throw InvalidInputException("Unsupported NUMERIC value: %s", kind_str); + } + FlatVector::SetNull(out_vec, output_offset, true); + return true; +} + } // namespace duckdb diff --git a/src/postgres_binary_reader.cpp b/src/postgres_binary_reader.cpp index 153deac83..d86dd992c 100644 --- a/src/postgres_binary_reader.cpp +++ b/src/postgres_binary_reader.cpp @@ -10,7 +10,8 @@ namespace duckdb { PostgresBinaryReader::PostgresBinaryReader(PostgresConnection &con_p, const vector &column_ids, const PostgresBindData &bind_data) - : PostgresResultReader(con_p, column_ids, bind_data), parser(bind_data.types, bind_data.postgres_types) { + : PostgresResultReader(con_p, column_ids, bind_data), + parser(bind_data.types, bind_data.postgres_types, bind_data.type_config) { } PostgresBinaryReader::~PostgresBinaryReader() { diff --git a/src/postgres_extension.cpp b/src/postgres_extension.cpp index f4cbe338e..e811cf1a0 100644 --- a/src/postgres_extension.cpp +++ b/src/postgres_extension.cpp @@ -191,6 +191,9 @@ static void LoadInternal(ExtensionLoader &loader) { "pg_numeric_as_varchar", "Read Postgres numerics without precision and scale or precision > 38 as varchar instead of double", LogicalType::BOOLEAN, Value::BOOLEAN(false), PostgresClearCacheFunction::ClearCacheOnSetting); + config.AddExtensionOption( + "pg_numeric_nan_as_null", "Read Postgres numeric NaN value as NULL instead of throwing an error", + LogicalType::BOOLEAN, Value::BOOLEAN(true), PostgresClearCacheFunction::ClearCacheOnSetting); config.AddExtensionOption( "pg_connection_cache", "Whether or not to use the connection pooling." diff --git a/src/postgres_query.cpp b/src/postgres_query.cpp index decbe116c..7069be166 100644 --- a/src/postgres_query.cpp +++ b/src/postgres_query.cpp @@ -134,6 +134,7 @@ static unique_ptr PGQueryBind(ClientContext &context, TableFunctio } // set up the bind data + result->type_config = type_config; result->SetCatalog(pg_catalog); result->dsn = con.GetDSN(); result->types = return_types; diff --git a/src/postgres_utils.cpp b/src/postgres_utils.cpp index 5ba111594..6bd6158f6 100644 --- a/src/postgres_utils.cpp +++ b/src/postgres_utils.cpp @@ -193,6 +193,9 @@ PostgresTypeConfig PostgresTypeConfig::FromContext(optional_ptr c if (context->TryGetCurrentSetting("pg_numeric_as_varchar", setting)) { result.numeric_as_varchar = BooleanValue::Get(setting); } + if (context->TryGetCurrentSetting("pg_numeric_nan_as_null", setting)) { + result.numeric_nan_as_null = BooleanValue::Get(setting); + } return result; } diff --git a/src/storage/postgres_table_entry.cpp b/src/storage/postgres_table_entry.cpp index 8042af900..a03f4fb88 100644 --- a/src/storage/postgres_table_entry.cpp +++ b/src/storage/postgres_table_entry.cpp @@ -58,6 +58,7 @@ TableFunction PostgresTableEntry::GetScanFunction(ClientContext &context, unique result->names = postgres_names; result->postgres_types = postgres_types; result->read_only = transaction.IsReadOnly(); + result->type_config = PostgresTypeConfig::FromContext(context); PostgresScanFunction::PrepareBind(pg_catalog.GetPostgresVersion(), context, *result, approx_num_pages.load(std::memory_order_acquire)); diff --git a/test/sql/scanner/numerics_nan_inf.test b/test/sql/scanner/numerics_nan_inf.test new file mode 100644 index 000000000..a06c97cd2 --- /dev/null +++ b/test/sql/scanner/numerics_nan_inf.test @@ -0,0 +1,360 @@ +# name: test/sql/scanner/numerics_nan_inf.test +# description: Test for out-of-bound values for numeric types +# group: [scanner] + +require postgres_scanner + +require-env POSTGRES_TEST_DATABASE_AVAILABLE + +statement ok +ATTACH 'dbname=postgresscanner' AS s (TYPE POSTGRES) + +# FLOAT + +query I +FROM postgres_query('s', 'SELECT ''NaN''::REAL') +---- +NaN + +query I +FROM postgres_query('s', 'SELECT ''Inf''::REAL') +---- +Inf + +query I +FROM postgres_query('s', 'SELECT ''-Inf''::REAL') +---- +-Inf + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::REAL') +) +---- +FLOAT + +# DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''NaN''::DOUBLE PRECISION') +---- +NaN + +query I +FROM postgres_query('s', 'SELECT ''Inf''::DOUBLE PRECISION') +---- +Inf + +query I +FROM postgres_query('s', 'SELECT ''-Inf''::DOUBLE PRECISION') +---- +-Inf + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::DOUBLE PRECISION') +) +---- +DOUBLE + +# NUMERIC + +# int16_t + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(4,3)') +) +---- +DECIMAL(4,3) + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(4,3)') +---- +NULL + +statement error +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC(4,3)') +---- +A field with precision 4, scale 3 cannot hold an infinite value. + +statement error +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC(4,3)') +---- +A field with precision 4, scale 3 cannot hold an infinite value. + +statement ok +SET pg_numeric_nan_as_null = FALSE + +statement error +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(4,3)') +---- +Unsupported NUMERIC value: NaN + +statement ok +RESET pg_numeric_nan_as_null + +# int32_t + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(9,3)') +) +---- +DECIMAL(9,3) + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(9,3)') +---- +NULL + +statement error +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC(9,3)') +---- +A field with precision 9, scale 3 cannot hold an infinite value. + +statement error +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC(9,3)') +---- +A field with precision 9, scale 3 cannot hold an infinite value. + +statement ok +SET pg_numeric_nan_as_null = FALSE + +statement error +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(9,3)') +---- +Unsupported NUMERIC value: NaN + +statement ok +RESET pg_numeric_nan_as_null + +# int64_t + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(18,3)') +) +---- +DECIMAL(18,3) + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(18,3)') +---- +NULL + +statement error +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC(18,3)') +---- +A field with precision 18, scale 3 cannot hold an infinite value. + +statement error +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC(18,3)') +---- +A field with precision 18, scale 3 cannot hold an infinite value. + +statement ok +SET pg_numeric_nan_as_null = FALSE + +statement error +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(18,3)') +---- +Unsupported NUMERIC value: NaN + +statement ok +RESET pg_numeric_nan_as_null + +# huge_int + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(38,19)') +) +---- +DECIMAL(38,19) + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(38,19)') +---- +NULL + +statement error +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC(38,19)') +---- +A field with precision 38, scale 19 cannot hold an infinite value. + +statement error +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC(38,19)') +---- +A field with precision 38, scale 19 cannot hold an infinite value. + +statement ok +SET pg_numeric_nan_as_null = FALSE + +statement error +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC(38,19)') +---- +Unsupported NUMERIC value: NaN + +# plain scan + +statement ok +CALL postgres_execute('s', 'DROP TABLE IF EXISTS out_of_bounds_numeric_test') + +statement ok +CALL postgres_execute('s', 'CREATE TABLE out_of_bounds_numeric_test(col1 INT, col2 NUMERIC(38,19))') + +statement ok +CALL postgres_execute('s', 'INSERT INTO out_of_bounds_numeric_test values(42, ''NaN'')') + +statement error +SELECT divide(col1, 1), col2 FROM s.out_of_bounds_numeric_test +---- +Unsupported NUMERIC value: NaN + +statement ok +CALL postgres_execute('s', 'DROP TABLE out_of_bounds_numeric_test') + +statement ok +RESET pg_numeric_nan_as_null + +# NUMERIC as DOUBLE + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +---- +NaN + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +---- +Infinity + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +---- +-Infinity + +statement ok +SET pg_numeric_nan_as_null = FALSE + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +---- +NaN + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +---- +Infinity + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +) +---- +DOUBLE + +query I +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +---- +-Infinity + +statement ok +RESET pg_numeric_nan_as_null + +# NUMERIC as VARCHAR + +statement ok +SET pg_numeric_as_varchar = TRUE + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +) +---- +VARCHAR + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +---- +NaN + +query I +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +---- +Infinity + +query I +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +---- +-Infinity + +statement ok +SET pg_numeric_nan_as_null = FALSE + +query I +SELECT column_type FROM ( + DESCRIBE FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +) +---- +VARCHAR + +query I +FROM postgres_query('s', 'SELECT ''NaN''::NUMERIC') +---- +NaN + +query I +FROM postgres_query('s', 'SELECT ''Infinity''::NUMERIC') +---- +Infinity + +query I +FROM postgres_query('s', 'SELECT ''-Infinity''::NUMERIC') +---- +-Infinity + +statement ok +RESET pg_numeric_nan_as_null + +statement ok +RESET pg_numeric_as_varchar + +statement ok +DETACH s