1996-07-09 08:22:35 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
--
|
|
|
|
-- complex.sql-
|
|
|
|
-- This file shows how to create a new user-defined type and how to
|
1996-12-28 03:22:12 +01:00
|
|
|
-- use this new type.
|
2010-11-23 21:27:50 +01:00
|
|
|
--
|
1996-07-09 08:22:35 +02:00
|
|
|
--
|
2023-01-02 21:00:37 +01:00
|
|
|
-- Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
|
2002-07-30 07:24:56 +02:00
|
|
|
-- Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
--
|
2010-09-20 22:08:53 +02:00
|
|
|
-- src/tutorial/complex.source
|
1996-07-09 08:22:35 +02:00
|
|
|
--
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-----------------------------
|
|
|
|
-- Creating a new type:
|
2003-10-22 00:51:14 +02:00
|
|
|
-- We are going to create a new type called 'complex' which represents
|
|
|
|
-- complex numbers.
|
|
|
|
-- A user-defined type must have an input and an output function, and
|
|
|
|
-- optionally can have binary input and output functions. All of these
|
|
|
|
-- are usually user-defined C functions.
|
1996-07-09 08:22:35 +02:00
|
|
|
-----------------------------
|
|
|
|
|
2001-10-26 22:45:33 +02:00
|
|
|
-- Assume the user defined functions are in _OBJWD_/complex$DLSUFFIX
|
2003-10-22 00:51:14 +02:00
|
|
|
-- (we do not want to assume this is in the dynamic loader search path).
|
|
|
|
-- Look at $PWD/complex.c for the source. Note that we declare all of
|
|
|
|
-- them as STRICT, so we do not need to cope with NULL inputs in the
|
|
|
|
-- C code. We also mark them IMMUTABLE, since they always return the
|
|
|
|
-- same outputs given the same inputs.
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2010-11-23 21:27:50 +01:00
|
|
|
-- the input function 'complex_in' takes a null-terminated string (the
|
1996-07-09 08:22:35 +02:00
|
|
|
-- textual representation of the type) and turns it into the internal
|
|
|
|
-- (in memory) representation. You will get a message telling you 'complex'
|
|
|
|
-- does not exist yet but that's okay.
|
|
|
|
|
2002-08-22 02:01:51 +02:00
|
|
|
CREATE FUNCTION complex_in(cstring)
|
1996-07-09 08:22:35 +02:00
|
|
|
RETURNS complex
|
2001-10-26 22:45:33 +02:00
|
|
|
AS '_OBJWD_/complex'
|
2003-10-22 00:51:14 +02:00
|
|
|
LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
-- the output function 'complex_out' takes the internal representation and
|
|
|
|
-- converts it into the textual representation.
|
|
|
|
|
2002-08-22 02:01:51 +02:00
|
|
|
CREATE FUNCTION complex_out(complex)
|
|
|
|
RETURNS cstring
|
2001-10-26 22:45:33 +02:00
|
|
|
AS '_OBJWD_/complex'
|
2003-10-22 00:51:14 +02:00
|
|
|
LANGUAGE C IMMUTABLE STRICT;
|
|
|
|
|
|
|
|
-- the binary input function 'complex_recv' takes a StringInfo buffer
|
|
|
|
-- and turns its contents into the internal representation.
|
|
|
|
|
|
|
|
CREATE FUNCTION complex_recv(internal)
|
|
|
|
RETURNS complex
|
|
|
|
AS '_OBJWD_/complex'
|
|
|
|
LANGUAGE C IMMUTABLE STRICT;
|
|
|
|
|
|
|
|
-- the binary output function 'complex_send' takes the internal representation
|
|
|
|
-- and converts it into a (hopefully) platform-independent bytea string.
|
|
|
|
|
|
|
|
CREATE FUNCTION complex_send(complex)
|
|
|
|
RETURNS bytea
|
|
|
|
AS '_OBJWD_/complex'
|
|
|
|
LANGUAGE C IMMUTABLE STRICT;
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
-- now, we can create the type. The internallength specifies the size of the
|
|
|
|
-- memory block required to hold the type (we need two 8-byte doubles).
|
|
|
|
|
|
|
|
CREATE TYPE complex (
|
2010-11-23 21:27:50 +01:00
|
|
|
internallength = 16,
|
1996-07-09 08:22:35 +02:00
|
|
|
input = complex_in,
|
2002-07-30 07:24:56 +02:00
|
|
|
output = complex_out,
|
2003-10-22 00:51:14 +02:00
|
|
|
receive = complex_recv,
|
|
|
|
send = complex_send,
|
2002-07-30 07:24:56 +02:00
|
|
|
alignment = double
|
1996-07-09 08:22:35 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-----------------------------
|
|
|
|
-- Using the new type:
|
2002-07-30 07:24:56 +02:00
|
|
|
-- user-defined types can be used like ordinary built-in types.
|
1996-07-09 08:22:35 +02:00
|
|
|
-----------------------------
|
|
|
|
|
2003-10-22 00:51:14 +02:00
|
|
|
-- eg. we can use it in a table
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
CREATE TABLE test_complex (
|
|
|
|
a complex,
|
|
|
|
b complex
|
|
|
|
);
|
|
|
|
|
2002-07-30 07:24:56 +02:00
|
|
|
-- data for user-defined types are just strings in the proper textual
|
2010-11-23 21:27:50 +01:00
|
|
|
-- representation.
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1998-03-01 00:37:10 +01:00
|
|
|
INSERT INTO test_complex VALUES ('(1.0, 2.5)', '(4.2, 3.55 )');
|
|
|
|
INSERT INTO test_complex VALUES ('(33.0, 51.4)', '(100.42, 93.55)');
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
SELECT * FROM test_complex;
|
|
|
|
|
|
|
|
-----------------------------
|
|
|
|
-- Creating an operator for the new type:
|
|
|
|
-- Let's define an add operator for complex types. Since POSTGRES
|
|
|
|
-- supports function overloading, we'll use + as the add operator.
|
2010-11-23 21:27:50 +01:00
|
|
|
-- (Operator names can be reused with different numbers and types of
|
1996-07-09 08:22:35 +02:00
|
|
|
-- arguments.)
|
|
|
|
-----------------------------
|
|
|
|
|
1998-03-01 05:52:59 +01:00
|
|
|
-- first, define a function complex_add (also in complex.c)
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_add(complex, complex)
|
|
|
|
RETURNS complex
|
2001-10-26 22:45:33 +02:00
|
|
|
AS '_OBJWD_/complex'
|
2003-10-22 00:51:14 +02:00
|
|
|
LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
-- we can now define the operator. We show a binary operator here but you
|
Remove support for postfix (right-unary) operators.
This feature has been a thorn in our sides for a long time, causing
many grammatical ambiguity problems. It doesn't seem worth the
pain to continue to support it, so remove it.
There are some follow-on improvements we can make in the grammar,
but this commit only removes the bare minimum number of productions,
plus assorted backend support code.
Note that pg_dump and psql continue to have full support, since
they may be used against older servers. However, pg_dump warns
about postfix operators. There is also a check in pg_upgrade.
Documentation-wise, I (tgl) largely removed the "left unary"
terminology in favor of saying "prefix operator", which is
a more standard and IMO less confusing term.
I included a catversion bump, although no initial catalog data
changes here, to mark the boundary at which oprkind = 'r'
stopped being valid in pg_operator.
Mark Dilger, based on work by myself and Robert Haas;
review by John Naylor
Discussion: https://postgr.es/m/38ca86db-42ab-9b48-2902-337a0d6b8311@2ndquadrant.com
2020-09-18 01:38:05 +02:00
|
|
|
-- can also define a prefix operator by omitting the leftarg.
|
2010-11-23 21:27:50 +01:00
|
|
|
CREATE OPERATOR + (
|
1996-07-09 08:22:35 +02:00
|
|
|
leftarg = complex,
|
|
|
|
rightarg = complex,
|
|
|
|
procedure = complex_add,
|
|
|
|
commutator = +
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
SELECT (a + b) AS c FROM test_complex;
|
|
|
|
|
|
|
|
-- Occasionally, you may find it useful to cast the string to the desired
|
|
|
|
-- type explicitly. :: denotes a type cast.
|
|
|
|
|
|
|
|
SELECT a + '(1.0,1.0)'::complex AS aa,
|
|
|
|
b + '(1.0,1.0)'::complex AS bb
|
|
|
|
FROM test_complex;
|
|
|
|
|
|
|
|
|
|
|
|
-----------------------------
|
|
|
|
-- Creating aggregate functions
|
1996-12-28 03:22:12 +01:00
|
|
|
-- you can also define aggregate functions. The syntax is somewhat
|
1996-07-09 08:22:35 +02:00
|
|
|
-- cryptic but the idea is to express the aggregate in terms of state
|
|
|
|
-- transition functions.
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
CREATE AGGREGATE complex_sum (
|
2000-07-17 05:05:41 +02:00
|
|
|
sfunc = complex_add,
|
1996-07-09 08:22:35 +02:00
|
|
|
basetype = complex,
|
2000-07-17 05:05:41 +02:00
|
|
|
stype = complex,
|
|
|
|
initcond = '(0,0)'
|
1996-07-09 08:22:35 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
SELECT complex_sum(a) FROM test_complex;
|
|
|
|
|
|
|
|
|
|
|
|
-----------------------------
|
2002-07-30 07:24:56 +02:00
|
|
|
-- Interfacing New Types with Indexes:
|
1996-07-09 08:22:35 +02:00
|
|
|
-- We cannot define a secondary index (eg. a B-tree) over the new type
|
2002-07-30 07:24:56 +02:00
|
|
|
-- yet. We need to create all the required operators and support
|
|
|
|
-- functions, then we can make the operator class.
|
1996-07-09 08:22:35 +02:00
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
-- first, define the required operators
|
|
|
|
CREATE FUNCTION complex_abs_lt(complex, complex) RETURNS bool
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_abs_le(complex, complex) RETURNS bool
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_abs_eq(complex, complex) RETURNS bool
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_abs_ge(complex, complex) RETURNS bool
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_abs_gt(complex, complex) RETURNS bool
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
CREATE OPERATOR < (
|
|
|
|
leftarg = complex, rightarg = complex, procedure = complex_abs_lt,
|
2003-10-22 00:51:14 +02:00
|
|
|
commutator = > , negator = >= ,
|
2000-01-24 08:16:52 +01:00
|
|
|
restrict = scalarltsel, join = scalarltjoinsel
|
1998-03-01 00:37:10 +01:00
|
|
|
);
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE OPERATOR <= (
|
|
|
|
leftarg = complex, rightarg = complex, procedure = complex_abs_le,
|
2003-10-22 00:51:14 +02:00
|
|
|
commutator = >= , negator = > ,
|
Distinguish selectivity of < from <= and > from >=.
Historically, the selectivity functions have simply not distinguished
< from <=, or > from >=, arguing that the fraction of the population that
satisfies the "=" aspect can be considered to be vanishingly small, if the
comparison value isn't any of the most-common-values for the variable.
(If it is, the code path that executes the operator against each MCV will
take care of things properly.) But that isn't really true unless we're
dealing with a continuum of variable values, and in practice we seldom are.
If "x = const" would estimate a nonzero number of rows for a given const
value, then it follows that we ought to estimate different numbers of rows
for "x < const" and "x <= const", even if the const is not one of the MCVs.
Handling this more honestly makes a significant difference in edge cases,
such as the estimate for a tight range (x BETWEEN y AND z where y and z
are close together).
Hence, split scalarltsel into scalarltsel/scalarlesel, and similarly
split scalargtsel into scalargtsel/scalargesel. Adjust <= and >=
operator definitions to reference the new selectivity functions.
Improve the core ineq_histogram_selectivity() function to make a
correction for equality. (Along the way, I learned quite a bit about
exactly why that function gives good answers, which I tried to memorialize
in improved comments.)
The corresponding join selectivity functions were, and remain, just stubs.
But I chose to split them similarly, to avoid confusion and to prevent the
need for doing this exercise again if someone ever makes them less stubby.
In passing, change ineq_histogram_selectivity's clamp for extreme
probability estimates so that it varies depending on the histogram
size, instead of being hardwired at 0.0001. With the default histogram
size of 100 entries, you still get the old clamp value, but bigger
histograms should allow us to put more faith in edge values.
Tom Lane, reviewed by Aleksander Alekseev and Kuntal Ghosh
Discussion: https://postgr.es/m/12232.1499140410@sss.pgh.pa.us
2017-09-13 17:12:39 +02:00
|
|
|
restrict = scalarlesel, join = scalarlejoinsel
|
1998-03-01 00:37:10 +01:00
|
|
|
);
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE OPERATOR = (
|
|
|
|
leftarg = complex, rightarg = complex, procedure = complex_abs_eq,
|
2003-10-22 00:51:14 +02:00
|
|
|
commutator = = ,
|
|
|
|
-- leave out negator since we didn't create <> operator
|
|
|
|
-- negator = <> ,
|
1996-07-09 08:22:35 +02:00
|
|
|
restrict = eqsel, join = eqjoinsel
|
1998-03-01 00:37:10 +01:00
|
|
|
);
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE OPERATOR >= (
|
|
|
|
leftarg = complex, rightarg = complex, procedure = complex_abs_ge,
|
2003-10-22 00:51:14 +02:00
|
|
|
commutator = <= , negator = < ,
|
Distinguish selectivity of < from <= and > from >=.
Historically, the selectivity functions have simply not distinguished
< from <=, or > from >=, arguing that the fraction of the population that
satisfies the "=" aspect can be considered to be vanishingly small, if the
comparison value isn't any of the most-common-values for the variable.
(If it is, the code path that executes the operator against each MCV will
take care of things properly.) But that isn't really true unless we're
dealing with a continuum of variable values, and in practice we seldom are.
If "x = const" would estimate a nonzero number of rows for a given const
value, then it follows that we ought to estimate different numbers of rows
for "x < const" and "x <= const", even if the const is not one of the MCVs.
Handling this more honestly makes a significant difference in edge cases,
such as the estimate for a tight range (x BETWEEN y AND z where y and z
are close together).
Hence, split scalarltsel into scalarltsel/scalarlesel, and similarly
split scalargtsel into scalargtsel/scalargesel. Adjust <= and >=
operator definitions to reference the new selectivity functions.
Improve the core ineq_histogram_selectivity() function to make a
correction for equality. (Along the way, I learned quite a bit about
exactly why that function gives good answers, which I tried to memorialize
in improved comments.)
The corresponding join selectivity functions were, and remain, just stubs.
But I chose to split them similarly, to avoid confusion and to prevent the
need for doing this exercise again if someone ever makes them less stubby.
In passing, change ineq_histogram_selectivity's clamp for extreme
probability estimates so that it varies depending on the histogram
size, instead of being hardwired at 0.0001. With the default histogram
size of 100 entries, you still get the old clamp value, but bigger
histograms should allow us to put more faith in edge values.
Tom Lane, reviewed by Aleksander Alekseev and Kuntal Ghosh
Discussion: https://postgr.es/m/12232.1499140410@sss.pgh.pa.us
2017-09-13 17:12:39 +02:00
|
|
|
restrict = scalargesel, join = scalargejoinsel
|
1998-03-01 00:37:10 +01:00
|
|
|
);
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE OPERATOR > (
|
|
|
|
leftarg = complex, rightarg = complex, procedure = complex_abs_gt,
|
2003-10-22 00:51:14 +02:00
|
|
|
commutator = < , negator = <= ,
|
2000-01-24 08:16:52 +01:00
|
|
|
restrict = scalargtsel, join = scalargtjoinsel
|
1996-07-09 08:22:35 +02:00
|
|
|
);
|
|
|
|
|
2002-07-30 07:24:56 +02:00
|
|
|
-- create the support function too
|
1996-07-09 08:22:35 +02:00
|
|
|
CREATE FUNCTION complex_abs_cmp(complex, complex) RETURNS int4
|
2003-10-22 00:51:14 +02:00
|
|
|
AS '_OBJWD_/complex' LANGUAGE C IMMUTABLE STRICT;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2002-07-30 07:24:56 +02:00
|
|
|
-- now we can make the operator class
|
|
|
|
CREATE OPERATOR CLASS complex_abs_ops
|
|
|
|
DEFAULT FOR TYPE complex USING btree AS
|
|
|
|
OPERATOR 1 < ,
|
|
|
|
OPERATOR 2 <= ,
|
|
|
|
OPERATOR 3 = ,
|
|
|
|
OPERATOR 4 >= ,
|
|
|
|
OPERATOR 5 > ,
|
|
|
|
FUNCTION 1 complex_abs_cmp(complex, complex);
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
-- now, we can define a btree index on complex types. First, let's populate
|
1996-12-28 03:22:12 +01:00
|
|
|
-- the table. Note that postgres needs many more tuples to start using the
|
|
|
|
-- btree index during selects.
|
1998-03-01 00:37:10 +01:00
|
|
|
INSERT INTO test_complex VALUES ('(56.0,-22.5)', '(-43.2,-0.07)');
|
1996-07-09 08:22:35 +02:00
|
|
|
INSERT INTO test_complex VALUES ('(-91.9,33.6)', '(8.6,3.0)');
|
|
|
|
|
|
|
|
CREATE INDEX test_cplx_ind ON test_complex
|
|
|
|
USING btree(a complex_abs_ops);
|
|
|
|
|
|
|
|
SELECT * from test_complex where a = '(56.0,-22.5)';
|
|
|
|
SELECT * from test_complex where a < '(56.0,-22.5)';
|
1996-12-28 03:22:12 +01:00
|
|
|
SELECT * from test_complex where a > '(56.0,-22.5)';
|
1998-03-01 00:37:10 +01:00
|
|
|
|
1998-03-01 05:52:59 +01:00
|
|
|
|
2002-07-30 07:24:56 +02:00
|
|
|
-- clean up the example
|
|
|
|
DROP TABLE test_complex;
|
|
|
|
DROP TYPE complex CASCADE;
|