From 8eedbc2cc4b37a5e0a5f6a158ddc77b8059930bc Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 2 Mar 2026 11:10:39 +0900 Subject: [PATCH] test_custom_types: Test module with fancy custom data types This commit adds a new test module called "test_custom_types", that can be used to stress code paths related to custom data type implementations. Currently, this is used as a test suite to validate the set of fixes done in 3b7a6fa15720, that requires some typanalyze callbacks that can force very specific backend behaviors, as of: - typanalyze callback that returns "false" as status, to mark a failure in computing statistics. - typanalyze callback that returns "true" but let's the backend know that no interesting stats could be computed, with stats_valid set to "false". This could be extended more in the future if more problems are found. For simplicity, the module uses a fake int4 data type, that requires a btree operator class to be usable with extended statistics. The type is created by the extension, and its properties are altered in the test. Like 3b7a6fa15720, this module is backpatched down to v14, for coverage purposes. Author: Michael Paquier Reviewed-by: Chao Li Discussion: https://postgr.es/m/aaDrJsE1I5mrE-QF@paquier.xyz Backpatch-through: 14 --- src/test/modules/Makefile | 1 + src/test/modules/test_custom_types/.gitignore | 4 + src/test/modules/test_custom_types/Makefile | 20 ++ src/test/modules/test_custom_types/README | 9 + .../expected/test_custom_types.out | 174 +++++++++++++++++ .../modules/test_custom_types/meson.build | 33 ++++ .../sql/test_custom_types.sql | 104 ++++++++++ .../test_custom_types--1.0.sql | 164 ++++++++++++++++ .../test_custom_types/test_custom_types.c | 184 ++++++++++++++++++ .../test_custom_types.control | 5 + 10 files changed, 698 insertions(+) create mode 100644 src/test/modules/test_custom_types/.gitignore create mode 100644 src/test/modules/test_custom_types/Makefile create mode 100644 src/test/modules/test_custom_types/README create mode 100644 src/test/modules/test_custom_types/expected/test_custom_types.out create mode 100644 src/test/modules/test_custom_types/meson.build create mode 100644 src/test/modules/test_custom_types/sql/test_custom_types.sql create mode 100644 src/test/modules/test_custom_types/test_custom_types--1.0.sql create mode 100644 src/test/modules/test_custom_types/test_custom_types.c create mode 100644 src/test/modules/test_custom_types/test_custom_types.control diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 317fedddb0c..32c02b8f6fb 100644 --- a/src/test/modules/Makefile +++ b/src/test/modules/Makefile @@ -15,6 +15,7 @@ SUBDIRS = \ snapshot_too_old \ spgist_name_ops \ test_bloomfilter \ + test_custom_types \ test_ddl_deparse \ test_escape \ test_extensions \ diff --git a/src/test/modules/test_custom_types/.gitignore b/src/test/modules/test_custom_types/.gitignore new file mode 100644 index 00000000000..5dcb3ff9723 --- /dev/null +++ b/src/test/modules/test_custom_types/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/test_custom_types/Makefile b/src/test/modules/test_custom_types/Makefile new file mode 100644 index 00000000000..e1b582b26ea --- /dev/null +++ b/src/test/modules/test_custom_types/Makefile @@ -0,0 +1,20 @@ +# src/test/modules/test_custom_types/Makefile + +MODULES = test_custom_types + +EXTENSION = test_custom_types +DATA = test_custom_types--1.0.sql +PGFILEDESC = "test_custom_types - tests for dummy custom types" + +REGRESS = test_custom_types + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/test_custom_types +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/test_custom_types/README b/src/test/modules/test_custom_types/README new file mode 100644 index 00000000000..a37d2db577e --- /dev/null +++ b/src/test/modules/test_custom_types/README @@ -0,0 +1,9 @@ +test_custom_types +================= + +This module contains a set of custom data types, with some of the following +patterns: + +- typanalyze function registered to a custom type, returning false. +- typanalyze function registered to a custom type, registering invalid stats + data. diff --git a/src/test/modules/test_custom_types/expected/test_custom_types.out b/src/test/modules/test_custom_types/expected/test_custom_types.out new file mode 100644 index 00000000000..2fb6b0b10ee --- /dev/null +++ b/src/test/modules/test_custom_types/expected/test_custom_types.out @@ -0,0 +1,174 @@ +-- Tests with various custom types +CREATE EXTENSION test_custom_types; +-- Test comparison functions +SELECT '42'::int_custom = '42'::int_custom AS eq_test; + eq_test +--------- + t +(1 row) + +SELECT '42'::int_custom <> '42'::int_custom AS nt_test; + nt_test +--------- + f +(1 row) + +SELECT '42'::int_custom < '100'::int_custom AS lt_test; + lt_test +--------- + t +(1 row) + +SELECT '100'::int_custom > '42'::int_custom AS gt_test; + gt_test +--------- + t +(1 row) + +SELECT '42'::int_custom <= '100'::int_custom AS le_test; + le_test +--------- + t +(1 row) + +SELECT '100'::int_custom >= '42'::int_custom AS ge_test; + ge_test +--------- + t +(1 row) + +-- Create a table with the int_custom type +CREATE TABLE test_table ( + id int, + data int_custom +); +INSERT INTO test_table VALUES (1, '42'), (2, '100'), (3, '200'); +-- Verify data was inserted correctly +SELECT * FROM test_table ORDER BY id; + id | data +----+------ + 1 | 42 + 2 | 100 + 3 | 200 +(3 rows) + +-- Dummy function used for expression evaluations. +-- Note that this function does not use a SQL-standard function body on +-- purpose, so as external statistics can be loaded from it. +CREATE OR REPLACE FUNCTION func_int_custom (p_value int_custom) + RETURNS int_custom LANGUAGE plpgsql AS $$ + BEGIN + RETURN p_value; + END; $$; +-- Switch type to use typanalyze function that always returns false. +ALTER TYPE int_custom SET (ANALYZE = int_custom_typanalyze_false); +-- Extended statistics with an attribute that cannot be analyzed. +-- This includes all statistics kinds. +CREATE STATISTICS test_stats ON data, id FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +WARNING: statistics object "public.test_stats" could not be computed for relation "public.test_table" +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; + stxname | expr_stats_is_null +------------+-------------------- + test_stats | t +(1 row) + +DROP STATISTICS test_stats; +-- Extended statistics with an expression that cannot be analyzed. +CREATE STATISTICS test_stats ON func_int_custom(data), (id) FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +WARNING: statistics object "public.test_stats" could not be computed for relation "public.test_table" +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; + stxname | expr_stats_is_null +------------+-------------------- + test_stats | t +(1 row) + +DROP STATISTICS test_stats; +-- There should be no data stored for the expression. +SELECT tablename, + statistics_name, + null_frac, + avg_width + FROM pg_stats_ext_exprs WHERE statistics_name = 'test_stats' \gx +(0 rows) + +-- Switch type to use typanalyze function that generates invalid data. +ALTER TYPE int_custom SET (ANALYZE = int_custom_typanalyze_invalid); +-- Extended statistics with an attribute that generates invalid stats. +CREATE STATISTICS test_stats ON data, id FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; + stxname | expr_stats_is_null +------------+-------------------- + test_stats | t +(1 row) + +DROP STATISTICS test_stats; +-- Extended statistics with an expression that generates invalid data. +CREATE STATISTICS test_stats ON func_int_custom(data), (id) FROM test_table; +-- Computation of the stats fails, some data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; + stxname | expr_stats_is_null +------------+-------------------- + test_stats | f +(1 row) + +-- There should be some data stored for the expression, stored as NULL. +SELECT tablename, + statistics_name, + null_frac, + avg_width, + n_distinct, + most_common_vals, + most_common_freqs, + histogram_bounds, + correlation, + most_common_elems, + most_common_elem_freqs, + elem_count_histogram + FROM pg_stats_ext_exprs WHERE statistics_name = 'test_stats' \gx +-[ RECORD 1 ]----------+----------- +tablename | test_table +statistics_name | test_stats +null_frac | +avg_width | +n_distinct | +most_common_vals | +most_common_freqs | +histogram_bounds | +correlation | +most_common_elems | +most_common_elem_freqs | +elem_count_histogram | + +-- Run a query able to load the extended stats, including the NULL data. +SELECT COUNT(*) FROM test_table GROUP BY (func_int_custom(data)); + count +------- + 1 + 1 + 1 +(3 rows) + +DROP STATISTICS test_stats; +-- Cleanup +DROP FUNCTION func_int_custom; +DROP TABLE test_table; +DROP EXTENSION test_custom_types; diff --git a/src/test/modules/test_custom_types/meson.build b/src/test/modules/test_custom_types/meson.build new file mode 100644 index 00000000000..3d4f433dd51 --- /dev/null +++ b/src/test/modules/test_custom_types/meson.build @@ -0,0 +1,33 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +test_custom_types_sources = files( + 'test_custom_types.c', +) + +if host_system == 'windows' + test_custom_types_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'test_custom_types', + '--FILEDESC', 'test_custom_types - tests for dummy custom types',]) +endif + +test_custom_types = shared_module('test_custom_types', + test_custom_types_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += test_custom_types + +test_install_data += files( + 'test_custom_types.control', + 'test_custom_types--1.0.sql', +) + +tests += { + 'name': 'test_custom_types', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'regress': { + 'sql': [ + 'test_custom_types', + ], + }, +} diff --git a/src/test/modules/test_custom_types/sql/test_custom_types.sql b/src/test/modules/test_custom_types/sql/test_custom_types.sql new file mode 100644 index 00000000000..206a17c667f --- /dev/null +++ b/src/test/modules/test_custom_types/sql/test_custom_types.sql @@ -0,0 +1,104 @@ +-- Tests with various custom types + +CREATE EXTENSION test_custom_types; + +-- Test comparison functions +SELECT '42'::int_custom = '42'::int_custom AS eq_test; +SELECT '42'::int_custom <> '42'::int_custom AS nt_test; +SELECT '42'::int_custom < '100'::int_custom AS lt_test; +SELECT '100'::int_custom > '42'::int_custom AS gt_test; +SELECT '42'::int_custom <= '100'::int_custom AS le_test; +SELECT '100'::int_custom >= '42'::int_custom AS ge_test; + +-- Create a table with the int_custom type +CREATE TABLE test_table ( + id int, + data int_custom +); +INSERT INTO test_table VALUES (1, '42'), (2, '100'), (3, '200'); + +-- Verify data was inserted correctly +SELECT * FROM test_table ORDER BY id; + +-- Dummy function used for expression evaluations. +-- Note that this function does not use a SQL-standard function body on +-- purpose, so as external statistics can be loaded from it. +CREATE OR REPLACE FUNCTION func_int_custom (p_value int_custom) + RETURNS int_custom LANGUAGE plpgsql AS $$ + BEGIN + RETURN p_value; + END; $$; + +-- Switch type to use typanalyze function that always returns false. +ALTER TYPE int_custom SET (ANALYZE = int_custom_typanalyze_false); + +-- Extended statistics with an attribute that cannot be analyzed. +-- This includes all statistics kinds. +CREATE STATISTICS test_stats ON data, id FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; +DROP STATISTICS test_stats; + +-- Extended statistics with an expression that cannot be analyzed. +CREATE STATISTICS test_stats ON func_int_custom(data), (id) FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; +DROP STATISTICS test_stats; +-- There should be no data stored for the expression. +SELECT tablename, + statistics_name, + null_frac, + avg_width + FROM pg_stats_ext_exprs WHERE statistics_name = 'test_stats' \gx + +-- Switch type to use typanalyze function that generates invalid data. +ALTER TYPE int_custom SET (ANALYZE = int_custom_typanalyze_invalid); + +-- Extended statistics with an attribute that generates invalid stats. +CREATE STATISTICS test_stats ON data, id FROM test_table; +-- Computation of the stats fails, no data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; +DROP STATISTICS test_stats; + +-- Extended statistics with an expression that generates invalid data. +CREATE STATISTICS test_stats ON func_int_custom(data), (id) FROM test_table; +-- Computation of the stats fails, some data generated. +ANALYZE test_table; +SELECT stxname, stxdexpr IS NULL as expr_stats_is_null + FROM pg_statistic_ext s + LEFT JOIN pg_statistic_ext_data d ON s.oid = d.stxoid + WHERE stxname = 'test_stats'; +-- There should be some data stored for the expression, stored as NULL. +SELECT tablename, + statistics_name, + null_frac, + avg_width, + n_distinct, + most_common_vals, + most_common_freqs, + histogram_bounds, + correlation, + most_common_elems, + most_common_elem_freqs, + elem_count_histogram + FROM pg_stats_ext_exprs WHERE statistics_name = 'test_stats' \gx +-- Run a query able to load the extended stats, including the NULL data. +SELECT COUNT(*) FROM test_table GROUP BY (func_int_custom(data)); +DROP STATISTICS test_stats; + +-- Cleanup +DROP FUNCTION func_int_custom; +DROP TABLE test_table; +DROP EXTENSION test_custom_types; diff --git a/src/test/modules/test_custom_types/test_custom_types--1.0.sql b/src/test/modules/test_custom_types/test_custom_types--1.0.sql new file mode 100644 index 00000000000..ce0e905d636 --- /dev/null +++ b/src/test/modules/test_custom_types/test_custom_types--1.0.sql @@ -0,0 +1,164 @@ +/* src/test/modules/test_custom_types/test_custom_types--1.0.sql */ + +-- complain if script is sourced in psql, rather than via CREATE EXTENSION +\echo Use "CREATE EXTENSION test_custom_types" to load this file. \quit + +-- +-- Input/output functions for int_custom type +-- +CREATE FUNCTION int_custom_in(cstring) +RETURNS int_custom +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_out(int_custom) +RETURNS cstring +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +-- +-- Typanalyze function that returns false +-- +CREATE FUNCTION int_custom_typanalyze_false(internal) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +-- +-- Typanalyze function that returns invalid stats +-- +CREATE FUNCTION int_custom_typanalyze_invalid(internal) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT; + +-- +-- The int_custom type definition +-- +-- This type is identical to int4 in storage, and is used in subsequent +-- tests to have different properties. +-- +CREATE TYPE int_custom ( + INPUT = int_custom_in, + OUTPUT = int_custom_out, + LIKE = int4 +); + +-- +-- Comparison functions for int_custom +-- +-- These are required to create a btree operator class, which is needed +-- for the type to be usable in extended statistics objects. +-- +CREATE FUNCTION int_custom_eq(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_ne(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_lt(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_le(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_gt(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_ge(int_custom, int_custom) +RETURNS boolean +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION int_custom_cmp(int_custom, int_custom) +RETURNS integer +AS 'MODULE_PATHNAME' +LANGUAGE C IMMUTABLE STRICT; + +-- Operators for int_custom, for btree operator class +CREATE OPERATOR = ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_eq, + COMMUTATOR = =, + NEGATOR = <>, + RESTRICT = eqsel, + JOIN = eqjoinsel, + HASHES, + MERGES +); + +CREATE OPERATOR <> ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_ne, + COMMUTATOR = <>, + NEGATOR = =, + RESTRICT = neqsel, + JOIN = neqjoinsel +); + +CREATE OPERATOR < ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_lt, + COMMUTATOR = >, + NEGATOR = >=, + RESTRICT = scalarltsel, + JOIN = scalarltjoinsel +); + +CREATE OPERATOR <= ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_le, + COMMUTATOR = >=, + NEGATOR = >, + RESTRICT = scalarlesel, + JOIN = scalarlejoinsel +); + +CREATE OPERATOR > ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_gt, + COMMUTATOR = <, + NEGATOR = <=, + RESTRICT = scalargtsel, + JOIN = scalargtjoinsel +); + +CREATE OPERATOR >= ( + LEFTARG = int_custom, + RIGHTARG = int_custom, + FUNCTION = int_custom_ge, + COMMUTATOR = <=, + NEGATOR = <, + RESTRICT = scalargesel, + JOIN = scalargejoinsel +); + +-- +-- Btree operator class for int_custom +-- +-- This is required for the type to be usable in extended statistics objects, +-- for attributes and expressions. +-- +CREATE OPERATOR CLASS int_custom_ops + DEFAULT FOR TYPE int_custom USING btree AS + OPERATOR 1 <, + OPERATOR 2 <=, + OPERATOR 3 =, + OPERATOR 4 >=, + OPERATOR 5 >, + FUNCTION 1 int_custom_cmp(int_custom, int_custom); diff --git a/src/test/modules/test_custom_types/test_custom_types.c b/src/test/modules/test_custom_types/test_custom_types.c new file mode 100644 index 00000000000..67e25a80b56 --- /dev/null +++ b/src/test/modules/test_custom_types/test_custom_types.c @@ -0,0 +1,184 @@ +/*-------------------------------------------------------------------------- + * + * test_custom_types.c + * Test module for a set of functions for custom types. + * + * The custom type used in this module is similar to int4 for simplicity, + * except that it is able to use various typanalyze functions to enforce + * check patterns with ANALYZE. + * + * Copyright (c) 1996-2026, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_custom_types/test_custom_types.c + * + *-------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "fmgr.h" +#include "commands/vacuum.h" +#include "utils/builtins.h" + +PG_MODULE_MAGIC; + +/* Function declarations */ +PG_FUNCTION_INFO_V1(int_custom_in); +PG_FUNCTION_INFO_V1(int_custom_out); +PG_FUNCTION_INFO_V1(int_custom_typanalyze_false); +PG_FUNCTION_INFO_V1(int_custom_typanalyze_invalid); +PG_FUNCTION_INFO_V1(int_custom_eq); +PG_FUNCTION_INFO_V1(int_custom_ne); +PG_FUNCTION_INFO_V1(int_custom_lt); +PG_FUNCTION_INFO_V1(int_custom_le); +PG_FUNCTION_INFO_V1(int_custom_gt); +PG_FUNCTION_INFO_V1(int_custom_ge); +PG_FUNCTION_INFO_V1(int_custom_cmp); + +/* + * int_custom_in - input function for int_custom type + * + * Converts a string to a int_custom (which is just an int32 internally). + */ +Datum +int_custom_in(PG_FUNCTION_ARGS) +{ + char *num = PG_GETARG_CSTRING(0); + + PG_RETURN_INT32(pg_strtoint32(num)); +} + +/* + * int_custom_out - output function for int_custom type + * + * Converts a int_custom to a string. + */ +Datum +int_custom_out(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + char *result = (char *) palloc(12); /* sign, 10 digits, '\0' */ + + pg_ltoa(arg1, result); + PG_RETURN_CSTRING(result); +} + +/* + * int_custom_typanalyze_false - typanalyze function that returns false + * + * This function returns false, to simulate a type that cannot be analyzed. + */ +Datum +int_custom_typanalyze_false(PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(false); +} + +/* + * Callback used to compute invalid statistics. + */ +static void +int_custom_invalid_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc, + int samplerows, double totalrows) +{ + /* We are not valid, and do not want to be. */ + stats->stats_valid = false; +} + +/* + * int_custom_typanalyze_invalid + * + * This function sets some invalid stats data, letting the caller know that + * we are safe for an analyze, returning true. + */ +Datum +int_custom_typanalyze_invalid(PG_FUNCTION_ARGS) +{ + VacAttrStats *stats = (VacAttrStats *) PG_GETARG_POINTER(0); + Form_pg_attribute attr = stats->attr; + + /* If the attstattarget column is negative, use the default value */ + /* NB: it is okay to scribble on stats->attr since it's a copy */ + if (attr->attstattarget < 0) + attr->attstattarget = default_statistics_target; + + /* Buggy number, no need to care as long as it is positive */ + stats->minrows = 300; + + /* Set callback to compute some invalid stats */ + stats->compute_stats = int_custom_invalid_stats; + + PG_RETURN_BOOL(true); +} + +/* + * Comparison functions for int_custom type + */ +Datum +int_custom_eq(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 == arg2); +} + +Datum +int_custom_ne(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 != arg2); +} + +Datum +int_custom_lt(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 < arg2); +} + +Datum +int_custom_le(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 <= arg2); +} + +Datum +int_custom_gt(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 > arg2); +} + +Datum +int_custom_ge(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + PG_RETURN_BOOL(arg1 >= arg2); +} + +Datum +int_custom_cmp(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + int32 arg2 = PG_GETARG_INT32(1); + + if (arg1 < arg2) + PG_RETURN_INT32(-1); + else if (arg1 > arg2) + PG_RETURN_INT32(1); + else + PG_RETURN_INT32(0); +} diff --git a/src/test/modules/test_custom_types/test_custom_types.control b/src/test/modules/test_custom_types/test_custom_types.control new file mode 100644 index 00000000000..d25a74ef5c4 --- /dev/null +++ b/src/test/modules/test_custom_types/test_custom_types.control @@ -0,0 +1,5 @@ +# test_custom_types extension +comment = 'Tests for dummy custom types' +default_version = '1.0' +module_pathname = '$libdir/test_custom_types' +relocatable = true