mirror of
https://github.com/postgres/postgres.git
synced 2026-04-05 01:07:27 -04:00
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 in3b7a6fa157, 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. Like3b7a6fa157, this module is backpatched down to v14, for coverage purposes. Author: Michael Paquier <michael@paquier.xyz> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Discussion: https://postgr.es/m/aaDrJsE1I5mrE-QF@paquier.xyz Backpatch-through: 14
This commit is contained in:
parent
f033abc6c4
commit
8eedbc2cc4
10 changed files with 698 additions and 0 deletions
|
|
@ -15,6 +15,7 @@ SUBDIRS = \
|
|||
snapshot_too_old \
|
||||
spgist_name_ops \
|
||||
test_bloomfilter \
|
||||
test_custom_types \
|
||||
test_ddl_deparse \
|
||||
test_escape \
|
||||
test_extensions \
|
||||
|
|
|
|||
4
src/test/modules/test_custom_types/.gitignore
vendored
Normal file
4
src/test/modules/test_custom_types/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Generated subdirectories
|
||||
/log/
|
||||
/results/
|
||||
/tmp_check/
|
||||
20
src/test/modules/test_custom_types/Makefile
Normal file
20
src/test/modules/test_custom_types/Makefile
Normal file
|
|
@ -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
|
||||
9
src/test/modules/test_custom_types/README
Normal file
9
src/test/modules/test_custom_types/README
Normal file
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
33
src/test/modules/test_custom_types/meson.build
Normal file
33
src/test/modules/test_custom_types/meson.build
Normal file
|
|
@ -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',
|
||||
],
|
||||
},
|
||||
}
|
||||
104
src/test/modules/test_custom_types/sql/test_custom_types.sql
Normal file
104
src/test/modules/test_custom_types/sql/test_custom_types.sql
Normal file
|
|
@ -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;
|
||||
164
src/test/modules/test_custom_types/test_custom_types--1.0.sql
Normal file
164
src/test/modules/test_custom_types/test_custom_types--1.0.sql
Normal file
|
|
@ -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);
|
||||
184
src/test/modules/test_custom_types/test_custom_types.c
Normal file
184
src/test/modules/test_custom_types/test_custom_types.c
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in a new issue