Skip to content

Commit

Permalink
Merge pull request #23 from hydradatabase/type_support_tests
Browse files Browse the repository at this point in the history
Add basic tests for the currently supported types
mkaruza authored May 29, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 80271bc + 759d0d8 commit 495e3d9
Showing 9 changed files with 1,127 additions and 25 deletions.
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -68,7 +68,15 @@ third_party/duckdb/Makefile:
git submodule update --init --recursive

third_party/duckdb/build/$(QUACK_BUILD_DUCKDB)/src/$(DUCKDB_LIB):
$(MAKE) -C third_party/duckdb $(QUACK_BUILD_DUCKDB) DISABLE_SANITIZER=1 ENABLE_UBSAN=0 BUILD_UNITTESTS=OFF BUILD_HTTPFS=1 CMAKE_EXPORT_COMPILE_COMMANDS=1 -j8
$(MAKE) -C third_party/duckdb \
$(QUACK_BUILD_DUCKDB) \
DISABLE_SANITIZER=1 \
ENABLE_UBSAN=0 \
BUILD_UNITTESTS=OFF \
BUILD_HTTPFS=1 \
BUILD_JSON=1 \
CMAKE_EXPORT_COMPILE_COMMANDS=1 \
-j8

install-duckdb:
$(install_bin) -m 755 third_party/duckdb/build/$(QUACK_BUILD_DUCKDB)/src/$(DUCKDB_LIB) $(DESTDIR)$(PG_LIB)
4 changes: 2 additions & 2 deletions include/quack/quack_types.hpp
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ namespace quack {
constexpr int32_t QUACK_DUCK_DATE_OFFSET = 10957;
constexpr int64_t QUACK_DUCK_TIMESTAMP_OFFSET = INT64CONST(10957) * USECS_PER_DAY;

duckdb::LogicalType ConvertPostgresToDuckColumnType(Oid type);
Oid GetPostgresDuckDBType(duckdb::LogicalTypeId type);
duckdb::LogicalType ConvertPostgresToDuckColumnType(Oid type, int32_t typmod);
Oid GetPostgresDuckDBType(duckdb::LogicalType type);
void ConvertPostgresToDuckValue(Datum value, duckdb::Vector &result, idx_t offset);
void ConvertDuckToPostgresValue(TupleTableSlot *slot, duckdb::Value &value, idx_t col);
void InsertTupleIntoChunk(duckdb::DataChunk &output, PostgresHeapSeqScanThreadInfo &threadScanInfo,
385 changes: 385 additions & 0 deletions include/quack/types/decimal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,385 @@
#pragma once

#define NUMERIC_POS 0x0000
#define NUMERIC_NEG 0x4000
#define NUMERIC_NAN 0xC000
#define NUMERIC_NULL 0xF000
#define NUMERIC_MAX_PRECISION 1000
#define NUMERIC_MAX_DISPLAY_SCALE NUMERIC_MAX_PRECISION
#define NUMERIC_MIN_DISPLAY_SCALE 0
#define NUMERIC_MIN_SIG_DIGITS 16

#define NBASE 10000
#define HALF_NBASE 5000
#define DEC_DIGITS 4 /* decimal digits per NBASE digit */
#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */
#define DIV_GUARD_DIGITS 4

#define NUMERIC_EXT_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_EXT_SIGN_MASK)
#define NUMERIC_IS_NAN(n) ((n)->choice.n_header == NUMERIC_NAN)
#define NUMERIC_IS_PINF(n) ((n)->choice.n_header == NUMERIC_PINF)
#define NUMERIC_IS_NINF(n) ((n)->choice.n_header == NUMERIC_NINF)
#define NUMERIC_IS_INF(n) \
(((n)->choice.n_header & ~NUMERIC_INF_SIGN_MASK) == NUMERIC_PINF)

/*
* Interpretation of high bits.
*/

#define NUMERIC_SIGN_MASK 0xC000
#define NUMERIC_POS 0x0000
#define NUMERIC_NEG 0x4000
#define NUMERIC_SHORT 0x8000
#define NUMERIC_SPECIAL 0xC000

#define NUMERIC_FLAGBITS(n) ((n)->choice.n_header & NUMERIC_SIGN_MASK)
#define NUMERIC_IS_SHORT(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SHORT)
#define NUMERIC_IS_SPECIAL(n) (NUMERIC_FLAGBITS(n) == NUMERIC_SPECIAL)

#define NUMERIC_HDRSZ (VARHDRSZ + sizeof(uint16) + sizeof(int16))
#define NUMERIC_HDRSZ_SHORT (VARHDRSZ + sizeof(uint16))

/*
* If the flag bits are NUMERIC_SHORT or NUMERIC_SPECIAL, we want the short
* header; otherwise, we want the long one. Instead of testing against each
* value, we can just look at the high bit, for a slight efficiency gain.
*/
#define NUMERIC_HEADER_IS_SHORT(n) (((n)->choice.n_header & 0x8000) != 0)
#define NUMERIC_HEADER_SIZE(n) \
(VARHDRSZ + sizeof(uint16) + \
(NUMERIC_HEADER_IS_SHORT(n) ? 0 : sizeof(int16)))

/*
* Definitions for special values (NaN, positive infinity, negative infinity).
*
* The two bits after the NUMERIC_SPECIAL bits are 00 for NaN, 01 for positive
* infinity, 11 for negative infinity. (This makes the sign bit match where
* it is in a short-format value, though we make no use of that at present.)
* We could mask off the remaining bits before testing the active bits, but
* currently those bits must be zeroes, so masking would just add cycles.
*/
#define NUMERIC_EXT_SIGN_MASK 0xF000 /* high bits plus NaN/Inf flag bits */
#define NUMERIC_NAN 0xC000
#define NUMERIC_PINF 0xD000
#define NUMERIC_NINF 0xF000
#define NUMERIC_INF_SIGN_MASK 0x2000

/*
* Short format definitions.
*/

#define NUMERIC_SHORT_SIGN_MASK 0x2000
#define NUMERIC_SHORT_DSCALE_MASK 0x1F80
#define NUMERIC_SHORT_DSCALE_SHIFT 7
#define NUMERIC_SHORT_DSCALE_MAX \
(NUMERIC_SHORT_DSCALE_MASK >> NUMERIC_SHORT_DSCALE_SHIFT)
#define NUMERIC_SHORT_WEIGHT_SIGN_MASK 0x0040
#define NUMERIC_SHORT_WEIGHT_MASK 0x003F
#define NUMERIC_SHORT_WEIGHT_MAX NUMERIC_SHORT_WEIGHT_MASK
#define NUMERIC_SHORT_WEIGHT_MIN (-(NUMERIC_SHORT_WEIGHT_MASK+1))

#define NUMERIC_DSCALE_MASK 0x3FFF
#define NUMERIC_DSCALE_MAX NUMERIC_DSCALE_MASK

#define NUMERIC_SIGN(n) \
(NUMERIC_IS_SHORT(n) ? \
(((n)->choice.n_short.n_header & NUMERIC_SHORT_SIGN_MASK) ? \
NUMERIC_NEG : NUMERIC_POS) : \
(NUMERIC_IS_SPECIAL(n) ? \
NUMERIC_EXT_FLAGBITS(n) : NUMERIC_FLAGBITS(n)))
#define NUMERIC_DSCALE(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \
((n)->choice.n_short.n_header & NUMERIC_SHORT_DSCALE_MASK) \
>> NUMERIC_SHORT_DSCALE_SHIFT \
: ((n)->choice.n_long.n_sign_dscale & NUMERIC_DSCALE_MASK))
#define NUMERIC_WEIGHT(n) (NUMERIC_HEADER_IS_SHORT((n)) ? \
(((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_SIGN_MASK ? \
~NUMERIC_SHORT_WEIGHT_MASK : 0) \
| ((n)->choice.n_short.n_header & NUMERIC_SHORT_WEIGHT_MASK)) \
: ((n)->choice.n_long.n_weight))

#define NUMERIC_DIGITS(num) (NUMERIC_HEADER_IS_SHORT(num) ? \
(num)->choice.n_short.n_data : (num)->choice.n_long.n_data)
#define NUMERIC_NDIGITS(num) \
((VARSIZE(num) - NUMERIC_HEADER_SIZE(num)) / sizeof(NumericDigit))
#define NUMERIC_CAN_BE_SHORT(scale,weight) \
((scale) <= NUMERIC_SHORT_DSCALE_MAX && \
(weight) <= NUMERIC_SHORT_WEIGHT_MAX && \
(weight) >= NUMERIC_SHORT_WEIGHT_MIN)

#include "duckdb.hpp"
#include <cmath>

extern "C" {
#include "postgres.h"
#include "miscadmin.h"
#include "catalog/pg_type.h"
#include "executor/tuptable.h"
#include "utils/numeric.h"
}

typedef int16_t NumericDigit;

struct NumericShort
{
uint16_t n_header; /* Sign + display scale + weight */
NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */
};

struct NumericLong
{
uint16_t n_sign_dscale; /* Sign + display scale */
int16_t n_weight; /* Weight of 1st digit */
NumericDigit n_data[FLEXIBLE_ARRAY_MEMBER]; /* Digits */
};

union NumericChoice
{
uint16_t n_header; /* Header word */
struct NumericLong n_long; /* Long form (4-byte header) */
struct NumericShort n_short; /* Short form (2-byte header) */
};

struct NumericData
{
int32_t vl_len_; /* varlena header (do not touch directly!) */
union NumericChoice choice; /* choice of format */
};

namespace quack {

struct NumericAsDouble : public duckdb::ExtraTypeInfo {
// Dummy struct to indicate at conversion that the source is a Numeric
public:
NumericAsDouble() : ExtraTypeInfo(duckdb::ExtraTypeInfoType::INVALID_TYPE_INFO) {}
};

using duckdb::hugeint_t;

// Stolen from postgres, they hide these details in numeric.c
typedef struct NumericVar
{
int32_t ndigits; /* # of digits in digits[] - can be 0! */
int32_t weight; /* weight of first digit */
int32_t sign; /* NUMERIC_POS, _NEG, _NAN, _PINF, or _NINF */
int32_t dscale; /* display scale */
NumericDigit *buf; /* start of palloc'd space for digits[] */
NumericDigit *digits; /* base-NBASE digits */
} NumericVar;


NumericVar FromNumeric(Numeric num)
{
NumericVar dest;
dest.ndigits = NUMERIC_NDIGITS(num);
dest.weight = NUMERIC_WEIGHT(num);
dest.sign = NUMERIC_SIGN(num);
dest.dscale = NUMERIC_DSCALE(num);
dest.digits = NUMERIC_DIGITS(num);
dest.buf = NULL; /* digits array is not palloc'd */
return dest;
}

/*
* make_result_opt_error() -
*
* Create the packed db numeric format in palloc()'d memory from
* a variable. This will handle NaN and Infinity cases.
*
* If "have_error" isn't NULL, on overflow *have_error is set to true and
* NULL is returned. This is helpful when caller needs to handle errors.
*/
Numeric CreateNumeric(const NumericVar &var, bool *have_error)
{
Numeric result;
NumericDigit *digits = var.digits;
int weight = var.weight;
int sign = var.sign;
int n;
Size len;

if (have_error)
*have_error = false;

if ((sign & NUMERIC_SIGN_MASK) == NUMERIC_SPECIAL)
{
/*
* Verify valid special value. This could be just an Assert, perhaps,
* but it seems worthwhile to expend a few cycles to ensure that we
* never write any nonzero reserved bits to disk.
*/
if (!(sign == NUMERIC_NAN ||
sign == NUMERIC_PINF ||
sign == NUMERIC_NINF))
elog(ERROR, "invalid numeric sign value 0x%x", sign);

result = (Numeric) palloc(NUMERIC_HDRSZ_SHORT);

SET_VARSIZE(result, NUMERIC_HDRSZ_SHORT);
result->choice.n_header = sign;
/* the header word is all we need */
return result;
}

n = var.ndigits;

/* truncate leading zeroes */
while (n > 0 && *digits == 0)
{
digits++;
weight--;
n--;
}
/* truncate trailing zeroes */
while (n > 0 && digits[n - 1] == 0)
n--;

/* If zero result, force to weight=0 and positive sign */
if (n == 0)
{
weight = 0;
sign = NUMERIC_POS;
}

/* Build the result */
if (NUMERIC_CAN_BE_SHORT(var.dscale, weight))
{
len = NUMERIC_HDRSZ_SHORT + n * sizeof(NumericDigit);
result = (Numeric) palloc(len);
SET_VARSIZE(result, len);
result->choice.n_short.n_header =
(sign == NUMERIC_NEG ? (NUMERIC_SHORT | NUMERIC_SHORT_SIGN_MASK)
: NUMERIC_SHORT)
| (var.dscale << NUMERIC_SHORT_DSCALE_SHIFT)
| (weight < 0 ? NUMERIC_SHORT_WEIGHT_SIGN_MASK : 0)
| (weight & NUMERIC_SHORT_WEIGHT_MASK);
}
else
{
len = NUMERIC_HDRSZ + n * sizeof(NumericDigit);
result = (Numeric) palloc(len);
SET_VARSIZE(result, len);
result->choice.n_long.n_sign_dscale =
sign | (var.dscale & NUMERIC_DSCALE_MASK);
result->choice.n_long.n_weight = weight;
}

Assert(NUMERIC_NDIGITS(result) == n);
if (n > 0)
memcpy(NUMERIC_DIGITS(result), digits, n * sizeof(NumericDigit));

/* Check for overflow of int16 fields */
if (NUMERIC_WEIGHT(result) != weight ||
NUMERIC_DSCALE(result) != var.dscale)
{
if (have_error)
{
*have_error = true;
return NULL;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value overflows numeric format")));
}
}
return result;
}

struct DecimalConversionInteger {
static int64_t GetPowerOfTen(idx_t index) {
static const int64_t POWERS_OF_TEN[] {1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000};
if (index >= 19) {
throw duckdb::InternalException("DecimalConversionInteger::GetPowerOfTen - Out of range");
}
return POWERS_OF_TEN[index];
}

template <class T>
static T Finalize(const NumericVar &numeric, T result) {
return result;
}
};

struct DecimalConversionHugeint {
static hugeint_t GetPowerOfTen(idx_t index) {
static const hugeint_t POWERS_OF_TEN[] {
hugeint_t(1),
hugeint_t(10),
hugeint_t(100),
hugeint_t(1000),
hugeint_t(10000),
hugeint_t(100000),
hugeint_t(1000000),
hugeint_t(10000000),
hugeint_t(100000000),
hugeint_t(1000000000),
hugeint_t(10000000000),
hugeint_t(100000000000),
hugeint_t(1000000000000),
hugeint_t(10000000000000),
hugeint_t(100000000000000),
hugeint_t(1000000000000000),
hugeint_t(10000000000000000),
hugeint_t(100000000000000000),
hugeint_t(1000000000000000000),
hugeint_t(1000000000000000000) * hugeint_t(10),
hugeint_t(1000000000000000000) * hugeint_t(100),
hugeint_t(1000000000000000000) * hugeint_t(1000),
hugeint_t(1000000000000000000) * hugeint_t(10000),
hugeint_t(1000000000000000000) * hugeint_t(100000),
hugeint_t(1000000000000000000) * hugeint_t(1000000),
hugeint_t(1000000000000000000) * hugeint_t(10000000),
hugeint_t(1000000000000000000) * hugeint_t(100000000),
hugeint_t(1000000000000000000) * hugeint_t(1000000000),
hugeint_t(1000000000000000000) * hugeint_t(10000000000),
hugeint_t(1000000000000000000) * hugeint_t(100000000000),
hugeint_t(1000000000000000000) * hugeint_t(1000000000000),
hugeint_t(1000000000000000000) * hugeint_t(10000000000000),
hugeint_t(1000000000000000000) * hugeint_t(100000000000000),
hugeint_t(1000000000000000000) * hugeint_t(1000000000000000),
hugeint_t(1000000000000000000) * hugeint_t(10000000000000000),
hugeint_t(1000000000000000000) * hugeint_t(100000000000000000),
hugeint_t(1000000000000000000) * hugeint_t(1000000000000000000),
hugeint_t(1000000000000000000) * hugeint_t(1000000000000000000) * hugeint_t(10),
hugeint_t(1000000000000000000) * hugeint_t(1000000000000000000) * hugeint_t(100)};
if (index >= 39) {
throw duckdb::InternalException("DecimalConversionHugeint::GetPowerOfTen - Out of range");
}
return POWERS_OF_TEN[index];
}

static hugeint_t Finalize(const NumericVar &numeric, hugeint_t result) {
return result;
}
};

struct DecimalConversionDouble {
static double GetPowerOfTen(idx_t index) {
return pow(10, double(index));
}

static double Finalize(const NumericVar &numeric, double result) {
return result / GetPowerOfTen(numeric.dscale);
}
};

} // namespace quack
5 changes: 3 additions & 2 deletions src/quack_heap_scan.cpp
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ PostgresHeapScanFunctionData::~PostgresHeapScanFunctionData() {

PostgresHeapScanGlobalState::PostgresHeapScanGlobalState(PostgresHeapSeqScan &relation,
duckdb::TableFunctionInitInput &input) {
elog(DEBUG3, "-- (DuckDB/PostgresHeapScanGlobalState) Running %lu threads -- ", MaxThreads());
elog(DEBUG3, "-- (DuckDB/PostgresHeapScanGlobalState) Running %llu threads -- ", MaxThreads());
relation.InitParallelScanState(input);
}

@@ -89,8 +89,9 @@ PostgresHeapScanFunction::PostgresHeapBind(duckdb::ClientContext &context, duckd
for (int i = 0; i < tupleDesc->natts; i++) {
Form_pg_attribute attr = &tupleDesc->attrs[i];
Oid type_oid = attr->atttypid;
auto typmod = attr->atttypmod;
auto col_name = duckdb::string(NameStr(attr->attname));
auto duck_type = ConvertPostgresToDuckColumnType(type_oid);
auto duck_type = ConvertPostgresToDuckColumnType(type_oid, typmod);
return_types.push_back(duck_type);
names.push_back(col_name);
/* Log column name and type */
2 changes: 1 addition & 1 deletion src/quack_planner.cpp
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ quack_create_plan(Query *parse, const char *query) {

for (auto i = 0; i < preparedResultTypes.size(); i++) {
auto &column = preparedResultTypes[i];
Oid postgresColumnOid = quack::GetPostgresDuckDBType(column.id());
Oid postgresColumnOid = quack::GetPostgresDuckDBType(column);

if (OidIsValid(postgresColumnOid)) {
HeapTuple tp;
347 changes: 329 additions & 18 deletions src/quack_types.cpp

Large diffs are not rendered by default.

254 changes: 254 additions & 0 deletions test/regression/expected/type_support.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
-- CHAR
CREATE TABLE chr(a CHAR);
INSERT INTO chr SELECT CAST(a AS CHAR) from (VALUES (-128), (0), (127)) t(a);
SELECT * FROM chr;
a
---
-
0
1
(3 rows)

-- SMALLINT
CREATE TABLE small(a SMALLINT);
INSERT INTO small SELECT CAST(a AS SMALLINT) from (VALUES (-32768), (0), (32767)) t(a);
SELECT * FROM small;
a
--------
-32768
0
32767
(3 rows)

-- INTEGER
CREATE TABLE intgr(a INTEGER);
INSERT INTO intgr SELECT CAST(a AS INTEGER) from (VALUES (-2147483648), (0), (2147483647)) t(a);
SELECT * FROM intgr;
a
-------------
-2147483648
0
2147483647
(3 rows)

-- BIGINT
CREATE TABLE big(a BIGINT);
INSERT INTO big SELECT CAST(a AS BIGINT) from (VALUES (-9223372036854775808), (0), (9223372036854775807)) t(a);
SELECT * FROM big;
a
----------------------
-9223372036854775808
0
9223372036854775807
(3 rows)

--- BOOL
CREATE TABLE bool_tbl(a BOOL);
INSERT INTO bool_tbl SELECT CAST(a AS BOOL) from (VALUES (False), (NULL), (True)) t(a);
SELECT * FROM bool_tbl;
a
---
f

t
(3 rows)

--- VARCHAR
CREATE TABLE varchar_tbl(a VARCHAR);
INSERT INTO varchar_tbl SELECT CAST(a AS VARCHAR) from (VALUES (''), (NULL), ('test'), ('this is a long string')) t(a);
SELECT * FROM varchar_tbl;
a
-----------------------


test
this is a long string
(4 rows)

-- DATE
CREATE TABLE date_tbl(a DATE);
INSERT INTO date_tbl SELECT CAST(a AS DATE) FROM (VALUES ('2022-04-29'::DATE), (NULL), ('2023-05-15'::DATE)) t(a);
SELECT * FROM date_tbl;
a
------------
04-29-2022

05-15-2023
(3 rows)

-- TIMESTAMP
CREATE TABLE timestamp_tbl(a TIMESTAMP);
INSERT INTO timestamp_tbl SELECT CAST(a AS TIMESTAMP) FROM (VALUES
('2022-04-29 10:15:30'::TIMESTAMP),
(NULL),
('2023-05-15 12:30:45'::TIMESTAMP)
) t(a);
SELECT * FROM timestamp_tbl;
a
--------------------------
Fri Apr 29 10:15:30 2022

Mon May 15 12:30:45 2023
(3 rows)

-- FLOAT4
CREATE TABLE float4_tbl(a FLOAT4);
INSERT INTO float4_tbl SELECT CAST(a AS FLOAT4) FROM (VALUES
(0.234234234::FLOAT4),
(NULL),
(458234502034234234234.000012::FLOAT4)
) t(a);
SELECT * FROM float4_tbl;
a
--------------
0.23423423

4.582345e+20
(3 rows)

-- FLOAT8
CREATE TABLE float8_tbl(a FLOAT8);
INSERT INTO float8_tbl SELECT CAST(a AS FLOAT8) FROM (VALUES
(0.234234234::FLOAT8),
(NULL),
(458234502034234234234.000012::FLOAT8)
) t(a);
SELECT * FROM float8_tbl;
a
-----------------------
0.234234234

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
(0.23),
(NULL),
(45.12)
) t(a);
SELECT * FROM smallint_numeric;
INFO: SMALLINT
INFO: SMALLINT
a
-------
0.23

45.12
(3 rows)

-- NUMERIC with a physical type of INTEGER
CREATE TABLE integer_numeric(a NUMERIC(9, 6));
INSERT INTO integer_numeric SELECT a FROM (VALUES
(243.345035::NUMERIC(9,6)),
(NULL),
(45.000012::NUMERIC(9,6))
) t(a);
SELECT * FROM integer_numeric;
INFO: INTEGER
INFO: INTEGER
a
------------
243.345035

45.000012
(3 rows)

-- NUMERIC with a physical type of BIGINT
CREATE TABLE bigint_numeric(a NUMERIC(18, 12));
INSERT INTO bigint_numeric SELECT a FROM (VALUES
(856324.111122223333::NUMERIC(18,12)),
(NULL),
(12.000000000001::NUMERIC(18,12))
) t(a);
SELECT * FROM bigint_numeric;
INFO: BIGINT
INFO: BIGINT
a
---------------------
856324.111122223333

12.000000000001
(3 rows)

-- NUMERIC with a physical type of HUGEINT
CREATE TABLE hugeint_numeric(a NUMERIC(38, 24));
INSERT INTO hugeint_numeric SELECT a FROM (VALUES
(32942348563242.111222333444555666777888::NUMERIC(38,24)),
(NULL),
(123456789.000000000000000000000001::NUMERIC(38,24))
) t(a);
SELECT * FROM hugeint_numeric;
INFO: HUGEINT
INFO: HUGEINT
a
-----------------------------------------
32942348563242.111222333444555666777888

123456789.000000000000000000000001
(3 rows)

-- UUID
CREATE TABLE uuid_tbl(a UUID);
INSERT INTO uuid_tbl SELECT CAST(a as UUID) FROM (VALUES
('80bf0be9-89be-4ef8-bc58-fc7d691c5544'),
(NULL),
('00000000-0000-0000-0000-000000000000')
) t(a);
SELECT * FROM uuid_tbl;
a
--------------------------------------
80bf0be9-89be-4ef8-bc58-fc7d691c5544

00000000-0000-0000-0000-000000000000
(3 rows)

-- JSON
CREATE TABLE json_tbl(a JSON);
INSERT INTO json_tbl SELECT CAST(a as JSON) FROM (VALUES
('{"key1": "value1", "key2": "value2"}'),
('["item1", "item2", "item3"]'),
(NULL),
('{}')
) t(a);
SELECT * FROM json_tbl;
a
--------------------------------------
{"key1": "value1", "key2": "value2"}
["item1", "item2", "item3"]

{}
(4 rows)

DROP TABLE chr;
DROP TABLE small;
DROP TABLE intgr;
DROP TABLE big;
DROP TABLE varchar_tbl;
DROP TABLE date_tbl;
DROP TABLE timestamp_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;
DROP TABLE hugeint_numeric;
DROP TABLE uuid_tbl;
DROP TABLE json_tbl;
3 changes: 2 additions & 1 deletion test/regression/schedule
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
test: basic
test: search_path
test: search_path
test: type_support
142 changes: 142 additions & 0 deletions test/regression/sql/type_support.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
-- CHAR
CREATE TABLE chr(a CHAR);
INSERT INTO chr SELECT CAST(a AS CHAR) from (VALUES (-128), (0), (127)) t(a);
SELECT * FROM chr;

-- SMALLINT
CREATE TABLE small(a SMALLINT);
INSERT INTO small SELECT CAST(a AS SMALLINT) from (VALUES (-32768), (0), (32767)) t(a);
SELECT * FROM small;

-- INTEGER
CREATE TABLE intgr(a INTEGER);
INSERT INTO intgr SELECT CAST(a AS INTEGER) from (VALUES (-2147483648), (0), (2147483647)) t(a);
SELECT * FROM intgr;

-- BIGINT
CREATE TABLE big(a BIGINT);
INSERT INTO big SELECT CAST(a AS BIGINT) from (VALUES (-9223372036854775808), (0), (9223372036854775807)) t(a);
SELECT * FROM big;

--- BOOL
CREATE TABLE bool_tbl(a BOOL);
INSERT INTO bool_tbl SELECT CAST(a AS BOOL) from (VALUES (False), (NULL), (True)) t(a);
SELECT * FROM bool_tbl;

--- VARCHAR
CREATE TABLE varchar_tbl(a VARCHAR);
INSERT INTO varchar_tbl SELECT CAST(a AS VARCHAR) from (VALUES (''), (NULL), ('test'), ('this is a long string')) t(a);
SELECT * FROM varchar_tbl;

-- DATE
CREATE TABLE date_tbl(a DATE);
INSERT INTO date_tbl SELECT CAST(a AS DATE) FROM (VALUES ('2022-04-29'::DATE), (NULL), ('2023-05-15'::DATE)) t(a);
SELECT * FROM date_tbl;

-- TIMESTAMP
CREATE TABLE timestamp_tbl(a TIMESTAMP);
INSERT INTO timestamp_tbl SELECT CAST(a AS TIMESTAMP) FROM (VALUES
('2022-04-29 10:15:30'::TIMESTAMP),
(NULL),
('2023-05-15 12:30:45'::TIMESTAMP)
) t(a);
SELECT * FROM timestamp_tbl;

-- FLOAT4
CREATE TABLE float4_tbl(a FLOAT4);
INSERT INTO float4_tbl SELECT CAST(a AS FLOAT4) FROM (VALUES
(0.234234234::FLOAT4),
(NULL),
(458234502034234234234.000012::FLOAT4)
) t(a);
SELECT * FROM float4_tbl;

-- FLOAT8
CREATE TABLE float8_tbl(a FLOAT8);
INSERT INTO float8_tbl SELECT CAST(a AS FLOAT8) FROM (VALUES
(0.234234234::FLOAT8),
(NULL),
(458234502034234234234.000012::FLOAT8)
) 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
(0.23),
(NULL),
(45.12)
) t(a);
SELECT * FROM smallint_numeric;

-- NUMERIC with a physical type of INTEGER
CREATE TABLE integer_numeric(a NUMERIC(9, 6));
INSERT INTO integer_numeric SELECT a FROM (VALUES
(243.345035::NUMERIC(9,6)),
(NULL),
(45.000012::NUMERIC(9,6))
) t(a);
SELECT * FROM integer_numeric;

-- NUMERIC with a physical type of BIGINT
CREATE TABLE bigint_numeric(a NUMERIC(18, 12));
INSERT INTO bigint_numeric SELECT a FROM (VALUES
(856324.111122223333::NUMERIC(18,12)),
(NULL),
(12.000000000001::NUMERIC(18,12))
) t(a);
SELECT * FROM bigint_numeric;

-- NUMERIC with a physical type of HUGEINT
CREATE TABLE hugeint_numeric(a NUMERIC(38, 24));
INSERT INTO hugeint_numeric SELECT a FROM (VALUES
(32942348563242.111222333444555666777888::NUMERIC(38,24)),
(NULL),
(123456789.000000000000000000000001::NUMERIC(38,24))
) t(a);
SELECT * FROM hugeint_numeric;

-- UUID
CREATE TABLE uuid_tbl(a UUID);
INSERT INTO uuid_tbl SELECT CAST(a as UUID) FROM (VALUES
('80bf0be9-89be-4ef8-bc58-fc7d691c5544'),
(NULL),
('00000000-0000-0000-0000-000000000000')
) t(a);
SELECT * FROM uuid_tbl;

-- JSON
CREATE TABLE json_tbl(a JSON);
INSERT INTO json_tbl SELECT CAST(a as JSON) FROM (VALUES
('{"key1": "value1", "key2": "value2"}'),
('["item1", "item2", "item3"]'),
(NULL),
('{}')
) t(a);
SELECT * FROM json_tbl;

DROP TABLE chr;
DROP TABLE small;
DROP TABLE intgr;
DROP TABLE big;
DROP TABLE varchar_tbl;
DROP TABLE date_tbl;
DROP TABLE timestamp_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;
DROP TABLE hugeint_numeric;
DROP TABLE uuid_tbl;
DROP TABLE json_tbl;

0 comments on commit 495e3d9

Please sign in to comment.