Import Upstream version 12.2
This commit is contained in:
commit
5f44daeadb
|
@ -0,0 +1,19 @@
|
||||||
|
;; see also src/tools/editors/emacs.samples for more complete settings
|
||||||
|
|
||||||
|
((c-mode . ((c-basic-offset . 4)
|
||||||
|
(c-file-style . "bsd")
|
||||||
|
(fill-column . 78)
|
||||||
|
(indent-tabs-mode . t)
|
||||||
|
(tab-width . 4)))
|
||||||
|
(nxml-mode . ((fill-column . 78)
|
||||||
|
(indent-tabs-mode . nil)))
|
||||||
|
(perl-mode . ((perl-indent-level . 4)
|
||||||
|
(perl-continued-statement-offset . 2)
|
||||||
|
(perl-continued-brace-offset . -2)
|
||||||
|
(perl-brace-offset . 0)
|
||||||
|
(perl-brace-imaginary-offset . 0)
|
||||||
|
(perl-label-offset . -2)
|
||||||
|
(indent-tabs-mode . t)
|
||||||
|
(tab-width . 4)))
|
||||||
|
(sgml-mode . ((fill-column . 78)
|
||||||
|
(indent-tabs-mode . nil))))
|
|
@ -0,0 +1,34 @@
|
||||||
|
* whitespace=space-before-tab,trailing-space
|
||||||
|
*.[chly] whitespace=space-before-tab,trailing-space,indent-with-non-tab,tabwidth=4
|
||||||
|
*.dsl whitespace=space-before-tab,trailing-space,tab-in-indent
|
||||||
|
*.patch -whitespace
|
||||||
|
*.pl whitespace=space-before-tab,trailing-space,tabwidth=4
|
||||||
|
*.po whitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eof
|
||||||
|
*.sgml whitespace=space-before-tab,trailing-space,tab-in-indent,-blank-at-eol
|
||||||
|
*.x[ms]l whitespace=space-before-tab,trailing-space,tab-in-indent
|
||||||
|
|
||||||
|
# Avoid confusing ASCII underlines with leftover merge conflict markers
|
||||||
|
README conflict-marker-size=32
|
||||||
|
README.* conflict-marker-size=32
|
||||||
|
|
||||||
|
# Certain data files that contain special whitespace, and other special cases
|
||||||
|
*.data -whitespace
|
||||||
|
contrib/pgcrypto/sql/pgp-armor.sql whitespace=-blank-at-eol
|
||||||
|
src/backend/catalog/sql_features.txt whitespace=space-before-tab,blank-at-eof,-blank-at-eol
|
||||||
|
|
||||||
|
# Test output files that contain extra whitespace
|
||||||
|
*.out -whitespace
|
||||||
|
contrib/*/output/*.source -whitespace
|
||||||
|
src/test/regress/output/*.source -whitespace
|
||||||
|
src/interfaces/ecpg/test/expected/* -whitespace
|
||||||
|
src/interfaces/libpq/test/expected.out whitespace=-blank-at-eof
|
||||||
|
|
||||||
|
# These files are maintained or generated elsewhere. We take them as is.
|
||||||
|
configure -whitespace
|
||||||
|
ppport.h -whitespace
|
||||||
|
src/backend/regex/COPYRIGHT -whitespace
|
||||||
|
src/backend/regex/re_syntax.n -whitespace
|
||||||
|
src/backend/snowball/libstemmer/*.c -whitespace
|
||||||
|
src/backend/utils/mb/Unicode/*-std.txt -whitespace
|
||||||
|
src/include/snowball/libstemmer/* -whitespace
|
||||||
|
src/timezone/data/* -whitespace
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Global excludes across all subdirectories
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.bc
|
||||||
|
*.so
|
||||||
|
*.so.[0-9]
|
||||||
|
*.so.[0-9].[0-9]
|
||||||
|
*.so.[0-9].[0-9][0-9]
|
||||||
|
*.sl
|
||||||
|
*.sl.[0-9]
|
||||||
|
*.sl.[0-9].[0-9]
|
||||||
|
*.sl.[0-9].[0-9][0-9]
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.exp
|
||||||
|
*.a
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
objfiles.txt
|
||||||
|
.deps/
|
||||||
|
*.gcno
|
||||||
|
*.gcda
|
||||||
|
*.gcov
|
||||||
|
*.gcov.out
|
||||||
|
lcov*.info
|
||||||
|
coverage/
|
||||||
|
coverage-html-stamp
|
||||||
|
*.vcproj
|
||||||
|
*.vcxproj
|
||||||
|
win32ver.rc
|
||||||
|
*.exe
|
||||||
|
lib*dll.def
|
||||||
|
lib*.pc
|
||||||
|
|
||||||
|
# Local excludes in root directory
|
||||||
|
/GNUmakefile
|
||||||
|
/config.cache
|
||||||
|
/config.log
|
||||||
|
/config.status
|
||||||
|
/pgsql.sln
|
||||||
|
/pgsql.sln.cache
|
||||||
|
/Debug/
|
||||||
|
/Release/
|
||||||
|
/tmp_install/
|
|
@ -0,0 +1,23 @@
|
||||||
|
PostgreSQL Database Management System
|
||||||
|
(formerly known as Postgres, then as Postgres95)
|
||||||
|
|
||||||
|
Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
|
||||||
|
|
||||||
|
Portions Copyright (c) 1994, The Regents of the University of California
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software and its
|
||||||
|
documentation for any purpose, without fee, and without a written agreement
|
||||||
|
is hereby granted, provided that the above copyright notice and this
|
||||||
|
paragraph and the following two paragraphs appear in all copies.
|
||||||
|
|
||||||
|
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||||
|
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
|
||||||
|
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
|
||||||
|
DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||||
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||||
|
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
|
||||||
|
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
@ -0,0 +1,134 @@
|
||||||
|
#
|
||||||
|
# PostgreSQL top level makefile
|
||||||
|
#
|
||||||
|
# GNUmakefile.in
|
||||||
|
#
|
||||||
|
|
||||||
|
subdir =
|
||||||
|
top_builddir = .
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
|
$(call recurse,all install,src config)
|
||||||
|
|
||||||
|
all:
|
||||||
|
+@echo "All of PostgreSQL successfully made. Ready to install."
|
||||||
|
|
||||||
|
docs:
|
||||||
|
$(MAKE) -C doc all
|
||||||
|
|
||||||
|
$(call recurse,world,doc src config contrib,all)
|
||||||
|
world:
|
||||||
|
+@echo "PostgreSQL, contrib, and documentation successfully made. Ready to install."
|
||||||
|
|
||||||
|
# build src/ before contrib/
|
||||||
|
world-contrib-recurse: world-src-recurse
|
||||||
|
|
||||||
|
html man:
|
||||||
|
$(MAKE) -C doc $@
|
||||||
|
|
||||||
|
install:
|
||||||
|
+@echo "PostgreSQL installation complete."
|
||||||
|
|
||||||
|
install-docs:
|
||||||
|
$(MAKE) -C doc install
|
||||||
|
|
||||||
|
$(call recurse,install-world,doc src config contrib,install)
|
||||||
|
install-world:
|
||||||
|
+@echo "PostgreSQL, contrib, and documentation installation complete."
|
||||||
|
|
||||||
|
# build src/ before contrib/
|
||||||
|
install-world-contrib-recurse: install-world-src-recurse
|
||||||
|
|
||||||
|
$(call recurse,installdirs uninstall init-po update-po,doc src config)
|
||||||
|
|
||||||
|
$(call recurse,distprep coverage,doc src config contrib)
|
||||||
|
|
||||||
|
# clean, distclean, etc should apply to contrib too, even though
|
||||||
|
# it's not built by default
|
||||||
|
$(call recurse,clean,doc contrib src config)
|
||||||
|
clean:
|
||||||
|
rm -rf tmp_install/
|
||||||
|
# Garbage from autoconf:
|
||||||
|
@rm -rf autom4te.cache/
|
||||||
|
|
||||||
|
# Important: distclean `src' last, otherwise Makefile.global
|
||||||
|
# will be gone too soon.
|
||||||
|
distclean maintainer-clean:
|
||||||
|
$(MAKE) -C doc $@
|
||||||
|
$(MAKE) -C contrib $@
|
||||||
|
$(MAKE) -C config $@
|
||||||
|
$(MAKE) -C src $@
|
||||||
|
rm -rf tmp_install/
|
||||||
|
# Garbage from autoconf:
|
||||||
|
@rm -rf autom4te.cache/
|
||||||
|
rm -f config.cache config.log config.status GNUmakefile
|
||||||
|
|
||||||
|
check check-tests installcheck installcheck-parallel installcheck-tests: CHECKPREP_TOP=src/test/regress
|
||||||
|
check check-tests installcheck installcheck-parallel installcheck-tests: submake-generated-headers
|
||||||
|
$(MAKE) -C src/test/regress $@
|
||||||
|
|
||||||
|
$(call recurse,check-world,src/test src/pl src/interfaces/ecpg contrib src/bin,check)
|
||||||
|
$(call recurse,checkprep, src/test src/pl src/interfaces/ecpg contrib src/bin)
|
||||||
|
|
||||||
|
$(call recurse,installcheck-world,src/test src/pl src/interfaces/ecpg contrib src/bin,installcheck)
|
||||||
|
|
||||||
|
GNUmakefile: GNUmakefile.in $(top_builddir)/config.status
|
||||||
|
./config.status $@
|
||||||
|
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
distdir = postgresql-$(VERSION)
|
||||||
|
dummy = =install=
|
||||||
|
|
||||||
|
dist: $(distdir).tar.gz $(distdir).tar.bz2
|
||||||
|
rm -rf $(distdir)
|
||||||
|
|
||||||
|
$(distdir).tar: distdir
|
||||||
|
$(TAR) chf $@ $(distdir)
|
||||||
|
|
||||||
|
.INTERMEDIATE: $(distdir).tar
|
||||||
|
|
||||||
|
distdir-location:
|
||||||
|
@echo $(distdir)
|
||||||
|
|
||||||
|
distdir:
|
||||||
|
rm -rf $(distdir)* $(dummy)
|
||||||
|
for x in `cd $(top_srcdir) && find . \( -name CVS -prune \) -o \( -name .git -prune \) -o -print`; do \
|
||||||
|
file=`expr X$$x : 'X\./\(.*\)'`; \
|
||||||
|
if test -d "$(top_srcdir)/$$file" ; then \
|
||||||
|
mkdir "$(distdir)/$$file" && chmod 777 "$(distdir)/$$file"; \
|
||||||
|
else \
|
||||||
|
ln "$(top_srcdir)/$$file" "$(distdir)/$$file" >/dev/null 2>&1 \
|
||||||
|
|| cp "$(top_srcdir)/$$file" "$(distdir)/$$file"; \
|
||||||
|
fi || exit; \
|
||||||
|
done
|
||||||
|
$(MAKE) -C $(distdir) distprep
|
||||||
|
$(MAKE) -C $(distdir)/doc/src/sgml/ INSTALL
|
||||||
|
cp $(distdir)/doc/src/sgml/INSTALL $(distdir)/
|
||||||
|
$(MAKE) -C $(distdir) distclean
|
||||||
|
rm -f $(distdir)/README.git
|
||||||
|
|
||||||
|
distcheck: dist
|
||||||
|
rm -rf $(dummy)
|
||||||
|
mkdir $(dummy)
|
||||||
|
$(GZIP) -d -c $(distdir).tar.gz | $(TAR) xf -
|
||||||
|
install_prefix=`cd $(dummy) && pwd`; \
|
||||||
|
cd $(distdir) \
|
||||||
|
&& ./configure --prefix="$$install_prefix"
|
||||||
|
$(MAKE) -C $(distdir) -q distprep
|
||||||
|
$(MAKE) -C $(distdir)
|
||||||
|
$(MAKE) -C $(distdir) install
|
||||||
|
$(MAKE) -C $(distdir) uninstall
|
||||||
|
@echo "checking whether \`$(MAKE) uninstall' works"
|
||||||
|
test `find $(dummy) ! -type d | wc -l` -eq 0
|
||||||
|
$(MAKE) -C $(distdir) dist
|
||||||
|
# Room for improvement: Check here whether this distribution tarball
|
||||||
|
# is sufficiently similar to the original one.
|
||||||
|
rm -rf $(distdir) $(dummy)
|
||||||
|
@echo "Distribution integrity checks out."
|
||||||
|
|
||||||
|
cpluspluscheck: submake-generated-headers
|
||||||
|
$(top_srcdir)/src/tools/pginclude/cpluspluscheck $(top_srcdir) $(abs_top_builddir)
|
||||||
|
|
||||||
|
.PHONY: dist distdir distcheck docs install-docs world check-world install-world installcheck-world
|
|
@ -0,0 +1,5 @@
|
||||||
|
Release notes for all versions of PostgreSQL can be found on-line at
|
||||||
|
https://www.postgresql.org/docs/current/static/release.html
|
||||||
|
|
||||||
|
Distribution file sets include release notes for their version and preceding
|
||||||
|
versions. Visit the file doc/src/sgml/html/release.html in an HTML browser.
|
|
@ -0,0 +1,41 @@
|
||||||
|
# The PostgreSQL make files exploit features of GNU make that other
|
||||||
|
# makes do not have. Because it is a common mistake for users to try
|
||||||
|
# to build Postgres with a different make, we have this make file
|
||||||
|
# that, as a service, will look for a GNU make and invoke it, or show
|
||||||
|
# an error message if none could be found.
|
||||||
|
|
||||||
|
# If the user were using GNU make now, this file would not get used
|
||||||
|
# because GNU make uses a make file named "GNUmakefile" in preference
|
||||||
|
# to "Makefile" if it exists. PostgreSQL is shipped with a
|
||||||
|
# "GNUmakefile". If the user hasn't run the configure script yet, the
|
||||||
|
# GNUmakefile won't exist yet, so we catch that case as well.
|
||||||
|
|
||||||
|
|
||||||
|
# AIX make defaults to building *every* target of the first rule. Start with
|
||||||
|
# a single-target, empty rule to make the other targets non-default.
|
||||||
|
all:
|
||||||
|
|
||||||
|
all check install installdirs installcheck installcheck-parallel uninstall clean distclean maintainer-clean dist distcheck world check-world install-world installcheck-world:
|
||||||
|
@if [ ! -f GNUmakefile ] ; then \
|
||||||
|
echo "You need to run the 'configure' program first. See the file"; \
|
||||||
|
echo "'INSTALL' for installation instructions." ; \
|
||||||
|
false ; \
|
||||||
|
fi
|
||||||
|
@IFS=':' ; \
|
||||||
|
for dir in $$PATH; do \
|
||||||
|
for prog in gmake gnumake make; do \
|
||||||
|
if [ -f $$dir/$$prog ] && ( $$dir/$$prog -f /dev/null --version 2>/dev/null | grep GNU >/dev/null 2>&1 ) ; then \
|
||||||
|
GMAKE=$$dir/$$prog; \
|
||||||
|
break 2; \
|
||||||
|
fi; \
|
||||||
|
done; \
|
||||||
|
done; \
|
||||||
|
\
|
||||||
|
if [ x"$${GMAKE+set}" = xset ]; then \
|
||||||
|
echo "Using GNU make found at $${GMAKE}"; \
|
||||||
|
unset MAKELEVEL; \
|
||||||
|
$${GMAKE} $@ ; \
|
||||||
|
else \
|
||||||
|
echo "You must use GNU make to build PostgreSQL." ; \
|
||||||
|
false; \
|
||||||
|
fi
|
|
@ -0,0 +1,27 @@
|
||||||
|
PostgreSQL Database Management System
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
This directory contains the source code distribution of the PostgreSQL
|
||||||
|
database management system.
|
||||||
|
|
||||||
|
PostgreSQL is an advanced object-relational database management system
|
||||||
|
that supports an extended subset of the SQL standard, including
|
||||||
|
transactions, foreign keys, subqueries, triggers, user-defined types
|
||||||
|
and functions. This distribution also contains C language bindings.
|
||||||
|
|
||||||
|
PostgreSQL has many language interfaces, many of which are listed here:
|
||||||
|
|
||||||
|
https://www.postgresql.org/download
|
||||||
|
|
||||||
|
See the file INSTALL for instructions on how to build and install
|
||||||
|
PostgreSQL. That file also lists supported operating systems and
|
||||||
|
hardware platforms and contains information regarding any other
|
||||||
|
software packages that are required to build or run the PostgreSQL
|
||||||
|
system. Copyright and license information can be found in the
|
||||||
|
file COPYRIGHT. A comprehensive documentation set is included in this
|
||||||
|
distribution; it can be read as described in the installation
|
||||||
|
instructions.
|
||||||
|
|
||||||
|
The latest version of this software may be obtained at
|
||||||
|
https://www.postgresql.org/download/. For more information look at our
|
||||||
|
web site located at https://www.postgresql.org/.
|
|
@ -0,0 +1,16 @@
|
||||||
|
dnl aclocal.m4
|
||||||
|
m4_include([config/ac_func_accept_argtypes.m4])
|
||||||
|
m4_include([config/ax_prog_perl_modules.m4])
|
||||||
|
m4_include([config/ax_pthread.m4])
|
||||||
|
m4_include([config/c-compiler.m4])
|
||||||
|
m4_include([config/c-library.m4])
|
||||||
|
m4_include([config/check_decls.m4])
|
||||||
|
m4_include([config/docbook.m4])
|
||||||
|
m4_include([config/general.m4])
|
||||||
|
m4_include([config/libtool.m4])
|
||||||
|
m4_include([config/llvm.m4])
|
||||||
|
m4_include([config/perl.m4])
|
||||||
|
m4_include([config/pkg.m4])
|
||||||
|
m4_include([config/programs.m4])
|
||||||
|
m4_include([config/python.m4])
|
||||||
|
m4_include([config/tcl.m4])
|
|
@ -0,0 +1,17 @@
|
||||||
|
# config/Makefile
|
||||||
|
|
||||||
|
subdir = config
|
||||||
|
top_builddir = ..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
|
|
||||||
|
install: all installdirs
|
||||||
|
$(INSTALL_SCRIPT) $(srcdir)/install-sh '$(DESTDIR)$(pgxsdir)/config/install-sh'
|
||||||
|
$(INSTALL_SCRIPT) $(srcdir)/missing '$(DESTDIR)$(pgxsdir)/config/missing'
|
||||||
|
|
||||||
|
installdirs:
|
||||||
|
$(MKDIR_P) '$(DESTDIR)$(pgxsdir)/config'
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f '$(DESTDIR)$(pgxsdir)/config/install-sh'
|
||||||
|
rm -f '$(DESTDIR)$(pgxsdir)/config/missing'
|
|
@ -0,0 +1,78 @@
|
||||||
|
# config/ac_func_accept_argtypes.m4
|
||||||
|
# This comes from the official Autoconf macro archive at
|
||||||
|
# <http://research.cys.de/autoconf-archive/>
|
||||||
|
|
||||||
|
|
||||||
|
dnl @synopsis AC_FUNC_ACCEPT_ARGTYPES
|
||||||
|
dnl
|
||||||
|
dnl Checks the data types of the three arguments to accept(). Results are
|
||||||
|
dnl placed into the symbols ACCEPT_TYPE_RETURN and ACCEPT_TYPE_ARG[123],
|
||||||
|
dnl consistent with the following example:
|
||||||
|
dnl
|
||||||
|
dnl #define ACCEPT_TYPE_RETURN int
|
||||||
|
dnl #define ACCEPT_TYPE_ARG1 int
|
||||||
|
dnl #define ACCEPT_TYPE_ARG2 struct sockaddr *
|
||||||
|
dnl #define ACCEPT_TYPE_ARG3 socklen_t
|
||||||
|
dnl
|
||||||
|
dnl NOTE: This is just a modified version of the AC_FUNC_SELECT_ARGTYPES
|
||||||
|
dnl macro. Credit for that one goes to David MacKenzie et. al.
|
||||||
|
dnl
|
||||||
|
dnl @version $Id: ac_func_accept_argtypes.m4,v 1.1 1999/12/03 11:29:29 simons Exp $
|
||||||
|
dnl @author Daniel Richard G. <skunk@mit.edu>
|
||||||
|
dnl
|
||||||
|
|
||||||
|
# PostgreSQL local changes: In the original version ACCEPT_TYPE_ARG3
|
||||||
|
# is a pointer type. That's kind of useless because then you can't
|
||||||
|
# use the macro to define a corresponding variable. We also make the
|
||||||
|
# reasonable(?) assumption that you can use arg3 for getsocktype etc.
|
||||||
|
# as well (i.e., anywhere POSIX.2 has socklen_t).
|
||||||
|
#
|
||||||
|
# arg2 can also be `const' (e.g., RH 4.2). Change the order of tests
|
||||||
|
# for arg3 so that `int' is first, in case there is no prototype at all.
|
||||||
|
#
|
||||||
|
# Solaris 7 and 8 have arg3 as 'void *' (disguised as 'Psocklen_t'
|
||||||
|
# which is *not* 'socklen_t *'). If we detect that, then we assume
|
||||||
|
# 'int' as the result, because that ought to work best.
|
||||||
|
#
|
||||||
|
# On Win32, accept() returns 'unsigned int PASCAL'
|
||||||
|
# Win64 uses SOCKET for return and arg1
|
||||||
|
|
||||||
|
AC_DEFUN([AC_FUNC_ACCEPT_ARGTYPES],
|
||||||
|
[AC_MSG_CHECKING([types of arguments for accept()])
|
||||||
|
AC_CACHE_VAL(ac_cv_func_accept_return,dnl
|
||||||
|
[AC_CACHE_VAL(ac_cv_func_accept_arg1,dnl
|
||||||
|
[AC_CACHE_VAL(ac_cv_func_accept_arg2,dnl
|
||||||
|
[AC_CACHE_VAL(ac_cv_func_accept_arg3,dnl
|
||||||
|
[for ac_cv_func_accept_return in 'int' 'SOCKET WSAAPI' 'unsigned int PASCAL'; do
|
||||||
|
for ac_cv_func_accept_arg1 in 'int' 'SOCKET' 'unsigned int'; do
|
||||||
|
for ac_cv_func_accept_arg2 in 'struct sockaddr *' 'const struct sockaddr *' 'void *'; do
|
||||||
|
for ac_cv_func_accept_arg3 in 'int' 'size_t' 'socklen_t' 'unsigned int' 'void'; do
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
extern $ac_cv_func_accept_return accept ($ac_cv_func_accept_arg1, $ac_cv_func_accept_arg2, $ac_cv_func_accept_arg3 *);])],
|
||||||
|
[ac_not_found=no; break 4], [ac_not_found=yes])
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
if test "$ac_not_found" = yes; then
|
||||||
|
AC_MSG_ERROR([could not determine argument types])
|
||||||
|
fi
|
||||||
|
if test "$ac_cv_func_accept_arg3" = "void"; then
|
||||||
|
ac_cv_func_accept_arg3=int
|
||||||
|
fi
|
||||||
|
])dnl AC_CACHE_VAL
|
||||||
|
])dnl AC_CACHE_VAL
|
||||||
|
])dnl AC_CACHE_VAL
|
||||||
|
])dnl AC_CACHE_VAL
|
||||||
|
AC_MSG_RESULT([$ac_cv_func_accept_return, $ac_cv_func_accept_arg1, $ac_cv_func_accept_arg2, $ac_cv_func_accept_arg3 *])
|
||||||
|
AC_DEFINE_UNQUOTED(ACCEPT_TYPE_RETURN, $ac_cv_func_accept_return,
|
||||||
|
[Define to the return type of 'accept'])
|
||||||
|
AC_DEFINE_UNQUOTED(ACCEPT_TYPE_ARG1, $ac_cv_func_accept_arg1,
|
||||||
|
[Define to the type of arg 1 of 'accept'])
|
||||||
|
AC_DEFINE_UNQUOTED(ACCEPT_TYPE_ARG2, $ac_cv_func_accept_arg2,
|
||||||
|
[Define to the type of arg 2 of 'accept'])
|
||||||
|
AC_DEFINE_UNQUOTED(ACCEPT_TYPE_ARG3, $ac_cv_func_accept_arg3,
|
||||||
|
[Define to the type of arg 3 of 'accept'])
|
||||||
|
])
|
|
@ -0,0 +1,77 @@
|
||||||
|
# ===========================================================================
|
||||||
|
# https://www.gnu.org/software/autoconf-archive/ax_prog_perl_modules.html
|
||||||
|
# ===========================================================================
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
#
|
||||||
|
# AX_PROG_PERL_MODULES([MODULES], [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
#
|
||||||
|
# Checks to see if the given perl modules are available. If true the shell
|
||||||
|
# commands in ACTION-IF-TRUE are executed. If not the shell commands in
|
||||||
|
# ACTION-IF-FALSE are run. Note if $PERL is not set (for example by
|
||||||
|
# calling AC_CHECK_PROG, or AC_PATH_PROG), AC_CHECK_PROG(PERL, perl, perl)
|
||||||
|
# will be run.
|
||||||
|
#
|
||||||
|
# MODULES is a space separated list of module names. To check for a
|
||||||
|
# minimum version of a module, append the version number to the module
|
||||||
|
# name, separated by an equals sign.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# AX_PROG_PERL_MODULES( Text::Wrap Net::LDAP=1.0.3, ,
|
||||||
|
# AC_MSG_WARN(Need some Perl modules)
|
||||||
|
#
|
||||||
|
# LICENSE
|
||||||
|
#
|
||||||
|
# Copyright (c) 2009 Dean Povey <povey@wedgetail.com>
|
||||||
|
#
|
||||||
|
# Copying and distribution of this file, with or without modification, are
|
||||||
|
# permitted in any medium without royalty provided the copyright notice
|
||||||
|
# and this notice are preserved. This file is offered as-is, without any
|
||||||
|
# warranty.
|
||||||
|
|
||||||
|
#serial 8
|
||||||
|
|
||||||
|
AU_ALIAS([AC_PROG_PERL_MODULES], [AX_PROG_PERL_MODULES])
|
||||||
|
AC_DEFUN([AX_PROG_PERL_MODULES],[dnl
|
||||||
|
|
||||||
|
m4_define([ax_perl_modules])
|
||||||
|
m4_foreach([ax_perl_module], m4_split(m4_normalize([$1])),
|
||||||
|
[
|
||||||
|
m4_append([ax_perl_modules],
|
||||||
|
[']m4_bpatsubst(ax_perl_module,=,[ ])[' ])
|
||||||
|
])
|
||||||
|
|
||||||
|
# Make sure we have perl
|
||||||
|
if test -z "$PERL"; then
|
||||||
|
AC_CHECK_PROG(PERL,perl,perl)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$PERL" != x; then
|
||||||
|
ax_perl_modules_failed=0
|
||||||
|
for ax_perl_module in ax_perl_modules; do
|
||||||
|
AC_MSG_CHECKING(for perl module $ax_perl_module)
|
||||||
|
|
||||||
|
# Would be nice to log result here, but can't rely on autoconf internals
|
||||||
|
$PERL -e "use $ax_perl_module; exit" > /dev/null 2>&1
|
||||||
|
if test $? -ne 0; then
|
||||||
|
AC_MSG_RESULT(no);
|
||||||
|
ax_perl_modules_failed=1
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(ok);
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Run optional shell commands
|
||||||
|
if test "$ax_perl_modules_failed" = 0; then
|
||||||
|
:
|
||||||
|
$2
|
||||||
|
else
|
||||||
|
:
|
||||||
|
$3
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
AC_MSG_WARN(could not find perl)
|
||||||
|
fi])dnl
|
|
@ -0,0 +1,485 @@
|
||||||
|
# ===========================================================================
|
||||||
|
# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
|
||||||
|
# ===========================================================================
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
#
|
||||||
|
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
#
|
||||||
|
# This macro figures out how to build C programs using POSIX threads. It
|
||||||
|
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||||
|
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||||
|
# flags that are needed. (The user can also force certain compiler
|
||||||
|
# flags/libs to be tested by setting these environment variables.)
|
||||||
|
#
|
||||||
|
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||||
|
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||||
|
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||||
|
#
|
||||||
|
# NOTE: You are assumed to not only compile your program with these flags,
|
||||||
|
# but also to link with them as well. For example, you might link with
|
||||||
|
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||||
|
#
|
||||||
|
# If you are only building threaded programs, you may wish to use these
|
||||||
|
# variables in your default LIBS, CFLAGS, and CC:
|
||||||
|
#
|
||||||
|
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
|
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
|
# CC="$PTHREAD_CC"
|
||||||
|
#
|
||||||
|
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||||
|
# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
|
||||||
|
# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||||
|
#
|
||||||
|
# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
|
||||||
|
# PTHREAD_PRIO_INHERIT symbol is defined when compiling with
|
||||||
|
# PTHREAD_CFLAGS.
|
||||||
|
#
|
||||||
|
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||||
|
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||||
|
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||||
|
# will define HAVE_PTHREAD.
|
||||||
|
#
|
||||||
|
# Please let the authors know if this macro fails on any platform, or if
|
||||||
|
# you have any other suggestions or comments. This macro was based on work
|
||||||
|
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||||
|
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||||
|
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||||
|
# grateful for the helpful feedback of numerous users.
|
||||||
|
#
|
||||||
|
# Updated for Autoconf 2.68 by Daniel Richard G.
|
||||||
|
#
|
||||||
|
# LICENSE
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||||
|
# Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by the
|
||||||
|
# Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
# Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||||
|
# gives unlimited permission to copy, distribute and modify the configure
|
||||||
|
# scripts that are the output of Autoconf when processing the Macro. You
|
||||||
|
# need not follow the terms of the GNU General Public License when using
|
||||||
|
# or distributing such scripts, even though portions of the text of the
|
||||||
|
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||||
|
# all other use of the material that constitutes the Autoconf Macro.
|
||||||
|
#
|
||||||
|
# This special exception to the GPL applies to versions of the Autoconf
|
||||||
|
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||||
|
# modified version of the Autoconf Macro, you may extend this special
|
||||||
|
# exception to the GPL to apply to your modified version as well.
|
||||||
|
|
||||||
|
#serial 24
|
||||||
|
|
||||||
|
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||||
|
AC_DEFUN([AX_PTHREAD], [
|
||||||
|
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||||
|
AC_REQUIRE([AC_PROG_CC])
|
||||||
|
AC_REQUIRE([AC_PROG_SED])
|
||||||
|
AC_LANG_PUSH([C])
|
||||||
|
ax_pthread_ok=no
|
||||||
|
|
||||||
|
# We used to check for pthread.h first, but this fails if pthread.h
|
||||||
|
# requires special compiler flags (e.g. on Tru64 or Sequent).
|
||||||
|
# It gets checked for in the link test anyway.
|
||||||
|
|
||||||
|
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||||
|
# etcetera environment variables, and if threads linking works using
|
||||||
|
# them:
|
||||||
|
if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
|
||||||
|
ax_pthread_save_CC="$CC"
|
||||||
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
|
ax_pthread_save_LIBS="$LIBS"
|
||||||
|
AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
|
||||||
|
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
|
LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
|
AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
|
||||||
|
AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
|
||||||
|
AC_MSG_RESULT([$ax_pthread_ok])
|
||||||
|
if test "x$ax_pthread_ok" = "xno"; then
|
||||||
|
PTHREAD_LIBS=""
|
||||||
|
PTHREAD_CFLAGS=""
|
||||||
|
fi
|
||||||
|
CC="$ax_pthread_save_CC"
|
||||||
|
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||||
|
LIBS="$ax_pthread_save_LIBS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# We must check for the threads library under a number of different
|
||||||
|
# names; the ordering is very important because some systems
|
||||||
|
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||||
|
# libraries is broken (non-POSIX).
|
||||||
|
|
||||||
|
# Create a list of thread flags to try. Items starting with a "-" are
|
||||||
|
# C compiler flags, and other items are library names, except for "none"
|
||||||
|
# which indicates that we try without any flags at all, and "pthread-config"
|
||||||
|
# which is a program returning the flags for the Pth emulation library.
|
||||||
|
|
||||||
|
ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||||
|
|
||||||
|
# The ordering *is* (sometimes) important. Some notes on the
|
||||||
|
# individual items follow:
|
||||||
|
|
||||||
|
# pthreads: AIX (must check this before -lpthread)
|
||||||
|
# none: in case threads are in libc; should be tried before -Kthread and
|
||||||
|
# other compiler flags to prevent continual compiler warnings
|
||||||
|
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||||
|
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
|
||||||
|
# (Note: HP C rejects this with "bad form for `-t' option")
|
||||||
|
# -pthreads: Solaris/gcc (Note: HP C also rejects)
|
||||||
|
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||||
|
# doesn't hurt to check since this sometimes defines pthreads and
|
||||||
|
# -D_REENTRANT too), HP C (must be checked before -lpthread, which
|
||||||
|
# is present but should not be used directly; and before -mthreads,
|
||||||
|
# because the compiler interprets this as "-mt" + "-hreads")
|
||||||
|
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||||
|
# pthread: Linux, etcetera
|
||||||
|
# --thread-safe: KAI C++
|
||||||
|
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||||
|
|
||||||
|
case $host_os in
|
||||||
|
|
||||||
|
freebsd*)
|
||||||
|
|
||||||
|
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||||
|
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||||
|
|
||||||
|
ax_pthread_flags="-kthread lthread $ax_pthread_flags"
|
||||||
|
;;
|
||||||
|
|
||||||
|
hpux*)
|
||||||
|
|
||||||
|
# From the cc(1) man page: "[-mt] Sets various -D flags to enable
|
||||||
|
# multi-threading and also sets -lpthread."
|
||||||
|
|
||||||
|
ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
|
||||||
|
;;
|
||||||
|
|
||||||
|
openedition*)
|
||||||
|
|
||||||
|
# IBM z/OS requires a feature-test macro to be defined in order to
|
||||||
|
# enable POSIX threads at all, so give the user a hint if this is
|
||||||
|
# not set. (We don't define these ourselves, as they can affect
|
||||||
|
# other portions of the system API in unpredictable ways.)
|
||||||
|
|
||||||
|
AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
|
||||||
|
[
|
||||||
|
# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
|
||||||
|
AX_PTHREAD_ZOS_MISSING
|
||||||
|
# endif
|
||||||
|
],
|
||||||
|
[AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
|
||||||
|
;;
|
||||||
|
|
||||||
|
solaris*)
|
||||||
|
|
||||||
|
# On Solaris (at least, for some versions), libc contains stubbed
|
||||||
|
# (non-functional) versions of the pthreads routines, so link-based
|
||||||
|
# tests will erroneously succeed. (N.B.: The stubs are missing
|
||||||
|
# pthread_cleanup_push, or rather a function called by this macro,
|
||||||
|
# so we could check for that, but who knows whether they'll stub
|
||||||
|
# that too in a future libc.) So we'll check first for the
|
||||||
|
# standard Solaris way of linking pthreads (-mt -lpthread).
|
||||||
|
|
||||||
|
ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
|
||||||
|
|
||||||
|
AS_IF([test "x$GCC" = "xyes"],
|
||||||
|
[ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"])
|
||||||
|
|
||||||
|
# The presence of a feature test macro requesting re-entrant function
|
||||||
|
# definitions is, on some systems, a strong hint that pthreads support is
|
||||||
|
# correctly enabled
|
||||||
|
|
||||||
|
case $host_os in
|
||||||
|
darwin* | hpux* | linux* | osf* | solaris*)
|
||||||
|
ax_pthread_check_macro="_REENTRANT"
|
||||||
|
;;
|
||||||
|
|
||||||
|
aix*)
|
||||||
|
ax_pthread_check_macro="_THREAD_SAFE"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
ax_pthread_check_macro="--"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
AS_IF([test "x$ax_pthread_check_macro" = "x--"],
|
||||||
|
[ax_pthread_check_cond=0],
|
||||||
|
[ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
|
||||||
|
|
||||||
|
# Are we compiling with Clang?
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([whether $CC is Clang],
|
||||||
|
[ax_cv_PTHREAD_CLANG],
|
||||||
|
[ax_cv_PTHREAD_CLANG=no
|
||||||
|
# Note that Autoconf sets GCC=yes for Clang as well as GCC
|
||||||
|
if test "x$GCC" = "xyes"; then
|
||||||
|
AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
|
||||||
|
[/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
|
||||||
|
# if defined(__clang__) && defined(__llvm__)
|
||||||
|
AX_PTHREAD_CC_IS_CLANG
|
||||||
|
# endif
|
||||||
|
],
|
||||||
|
[ax_cv_PTHREAD_CLANG=yes])
|
||||||
|
fi
|
||||||
|
])
|
||||||
|
ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
|
||||||
|
|
||||||
|
ax_pthread_clang_warning=no
|
||||||
|
|
||||||
|
# Clang needs special handling, because older versions handle the -pthread
|
||||||
|
# option in a rather... idiosyncratic way
|
||||||
|
|
||||||
|
if test "x$ax_pthread_clang" = "xyes"; then
|
||||||
|
|
||||||
|
# Clang takes -pthread; it has never supported any other flag
|
||||||
|
|
||||||
|
# (Note 1: This will need to be revisited if a system that Clang
|
||||||
|
# supports has POSIX threads in a separate library. This tends not
|
||||||
|
# to be the way of modern systems, but it's conceivable.)
|
||||||
|
|
||||||
|
# (Note 2: On some systems, notably Darwin, -pthread is not needed
|
||||||
|
# to get POSIX threads support; the API is always present and
|
||||||
|
# active. We could reasonably leave PTHREAD_CFLAGS empty. But
|
||||||
|
# -pthread does define _REENTRANT, and while the Darwin headers
|
||||||
|
# ignore this macro, third-party headers might not.)
|
||||||
|
|
||||||
|
PTHREAD_CFLAGS="-pthread"
|
||||||
|
PTHREAD_LIBS=
|
||||||
|
|
||||||
|
ax_pthread_ok=yes
|
||||||
|
|
||||||
|
# However, older versions of Clang make a point of warning the user
|
||||||
|
# that, in an invocation where only linking and no compilation is
|
||||||
|
# taking place, the -pthread option has no effect ("argument unused
|
||||||
|
# during compilation"). They expect -pthread to be passed in only
|
||||||
|
# when source code is being compiled.
|
||||||
|
#
|
||||||
|
# Problem is, this is at odds with the way Automake and most other
|
||||||
|
# C build frameworks function, which is that the same flags used in
|
||||||
|
# compilation (CFLAGS) are also used in linking. Many systems
|
||||||
|
# supported by AX_PTHREAD require exactly this for POSIX threads
|
||||||
|
# support, and in fact it is often not straightforward to specify a
|
||||||
|
# flag that is used only in the compilation phase and not in
|
||||||
|
# linking. Such a scenario is extremely rare in practice.
|
||||||
|
#
|
||||||
|
# Even though use of the -pthread flag in linking would only print
|
||||||
|
# a warning, this can be a nuisance for well-run software projects
|
||||||
|
# that build with -Werror. So if the active version of Clang has
|
||||||
|
# this misfeature, we search for an option to squash it.
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
|
||||||
|
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
|
||||||
|
[ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
|
||||||
|
# Create an alternate version of $ac_link that compiles and
|
||||||
|
# links in two steps (.c -> .o, .o -> exe) instead of one
|
||||||
|
# (.c -> exe), because the warning occurs only in the second
|
||||||
|
# step
|
||||||
|
ax_pthread_save_ac_link="$ac_link"
|
||||||
|
ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
|
||||||
|
ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"`
|
||||||
|
ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
|
||||||
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
|
for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
|
||||||
|
AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
|
||||||
|
CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
|
||||||
|
ac_link="$ax_pthread_save_ac_link"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||||
|
[ac_link="$ax_pthread_2step_ac_link"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
|
||||||
|
[break])
|
||||||
|
])
|
||||||
|
done
|
||||||
|
ac_link="$ax_pthread_save_ac_link"
|
||||||
|
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||||
|
AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
|
||||||
|
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
|
||||||
|
])
|
||||||
|
|
||||||
|
case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
|
||||||
|
no | unknown) ;;
|
||||||
|
*) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
fi # $ax_pthread_clang = yes
|
||||||
|
|
||||||
|
if test "x$ax_pthread_ok" = "xno"; then
|
||||||
|
for ax_pthread_try_flag in $ax_pthread_flags; do
|
||||||
|
|
||||||
|
case $ax_pthread_try_flag in
|
||||||
|
none)
|
||||||
|
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||||
|
;;
|
||||||
|
|
||||||
|
-mt,pthread)
|
||||||
|
AC_MSG_CHECKING([whether pthreads work with -mt -lpthread])
|
||||||
|
PTHREAD_CFLAGS="-mt"
|
||||||
|
PTHREAD_LIBS="-lpthread"
|
||||||
|
;;
|
||||||
|
|
||||||
|
-*)
|
||||||
|
AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
|
||||||
|
PTHREAD_CFLAGS="$ax_pthread_try_flag"
|
||||||
|
;;
|
||||||
|
|
||||||
|
pthread-config)
|
||||||
|
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
|
||||||
|
AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
|
||||||
|
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||||
|
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
|
||||||
|
PTHREAD_LIBS="-l$ax_pthread_try_flag"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
|
ax_pthread_save_LIBS="$LIBS"
|
||||||
|
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
|
LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
|
|
||||||
|
# Check for various functions. We must include pthread.h,
|
||||||
|
# since some functions may be macros. (On the Sequent, we
|
||||||
|
# need a special flag -Kthread to make this header compile.)
|
||||||
|
# We check for pthread_join because it is in -lpthread on IRIX
|
||||||
|
# while pthread_create is in libc. We check for pthread_attr_init
|
||||||
|
# due to DEC craziness with -lpthreads. We check for
|
||||||
|
# pthread_cleanup_push because it is one of the few pthread
|
||||||
|
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||||
|
# We try pthread_create on general principles.
|
||||||
|
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
|
||||||
|
# if $ax_pthread_check_cond
|
||||||
|
# error "$ax_pthread_check_macro must be defined"
|
||||||
|
# endif
|
||||||
|
static void routine(void *a) { a = 0; }
|
||||||
|
static void *start_routine(void *a) { return a; }],
|
||||||
|
[pthread_t th; pthread_attr_t attr;
|
||||||
|
pthread_create(&th, 0, start_routine, 0);
|
||||||
|
pthread_join(th, 0);
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_cleanup_push(routine, 0);
|
||||||
|
pthread_cleanup_pop(0) /* ; */])],
|
||||||
|
[ax_pthread_ok=yes],
|
||||||
|
[])
|
||||||
|
|
||||||
|
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||||
|
LIBS="$ax_pthread_save_LIBS"
|
||||||
|
|
||||||
|
AC_MSG_RESULT([$ax_pthread_ok])
|
||||||
|
AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
|
||||||
|
|
||||||
|
PTHREAD_LIBS=""
|
||||||
|
PTHREAD_CFLAGS=""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Various other checks:
|
||||||
|
if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
|
ax_pthread_save_CFLAGS="$CFLAGS"
|
||||||
|
ax_pthread_save_LIBS="$LIBS"
|
||||||
|
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||||
|
LIBS="$PTHREAD_LIBS $LIBS"
|
||||||
|
|
||||||
|
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||||
|
AC_CACHE_CHECK([for joinable pthread attribute],
|
||||||
|
[ax_cv_PTHREAD_JOINABLE_ATTR],
|
||||||
|
[ax_cv_PTHREAD_JOINABLE_ATTR=unknown
|
||||||
|
for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
|
||||||
|
[int attr = $ax_pthread_attr; return attr /* ; */])],
|
||||||
|
[ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
|
||||||
|
[])
|
||||||
|
done
|
||||||
|
])
|
||||||
|
AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
|
||||||
|
test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
|
||||||
|
test "x$ax_pthread_joinable_attr_defined" != "xyes"],
|
||||||
|
[AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
|
||||||
|
[$ax_cv_PTHREAD_JOINABLE_ATTR],
|
||||||
|
[Define to necessary symbol if this constant
|
||||||
|
uses a non-standard name on your system.])
|
||||||
|
ax_pthread_joinable_attr_defined=yes
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([whether more special flags are required for pthreads],
|
||||||
|
[ax_cv_PTHREAD_SPECIAL_FLAGS],
|
||||||
|
[ax_cv_PTHREAD_SPECIAL_FLAGS=no
|
||||||
|
case $host_os in
|
||||||
|
solaris*)
|
||||||
|
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
])
|
||||||
|
AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
|
||||||
|
test "x$ax_pthread_special_flags_added" != "xyes"],
|
||||||
|
[PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
|
||||||
|
ax_pthread_special_flags_added=yes])
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
|
||||||
|
[ax_cv_PTHREAD_PRIO_INHERIT],
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
|
||||||
|
[[int i = PTHREAD_PRIO_INHERIT;]])],
|
||||||
|
[ax_cv_PTHREAD_PRIO_INHERIT=yes],
|
||||||
|
[ax_cv_PTHREAD_PRIO_INHERIT=no])
|
||||||
|
])
|
||||||
|
AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
|
||||||
|
test "x$ax_pthread_prio_inherit_defined" != "xyes"],
|
||||||
|
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
|
||||||
|
ax_pthread_prio_inherit_defined=yes
|
||||||
|
])
|
||||||
|
|
||||||
|
CFLAGS="$ax_pthread_save_CFLAGS"
|
||||||
|
LIBS="$ax_pthread_save_LIBS"
|
||||||
|
|
||||||
|
# More AIX lossage: compile with *_r variant
|
||||||
|
if test "x$GCC" != "xyes"; then
|
||||||
|
case $host_os in
|
||||||
|
aix*)
|
||||||
|
AS_CASE(["x/$CC"],
|
||||||
|
[x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
|
||||||
|
[#handle absolute path differently from PATH based program lookup
|
||||||
|
AS_CASE(["x$CC"],
|
||||||
|
[x/*],
|
||||||
|
[AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])],
|
||||||
|
[AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])])
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
|
||||||
|
|
||||||
|
AC_SUBST([PTHREAD_LIBS])
|
||||||
|
AC_SUBST([PTHREAD_CFLAGS])
|
||||||
|
AC_SUBST([PTHREAD_CC])
|
||||||
|
|
||||||
|
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||||
|
if test "x$ax_pthread_ok" = "xyes"; then
|
||||||
|
ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
|
||||||
|
:
|
||||||
|
else
|
||||||
|
ax_pthread_ok=no
|
||||||
|
$2
|
||||||
|
fi
|
||||||
|
AC_LANG_POP
|
||||||
|
])dnl AX_PTHREAD
|
|
@ -0,0 +1,671 @@
|
||||||
|
# Macros to detect C compiler features
|
||||||
|
# config/c-compiler.m4
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_SIGNED
|
||||||
|
# -------------
|
||||||
|
# Check if the C compiler understands signed types.
|
||||||
|
AC_DEFUN([PGAC_C_SIGNED],
|
||||||
|
[AC_CACHE_CHECK(for signed types, pgac_cv_c_signed,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[signed char c; signed short s; signed int i;])],
|
||||||
|
[pgac_cv_c_signed=yes],
|
||||||
|
[pgac_cv_c_signed=no])])
|
||||||
|
if test x"$pgac_cv_c_signed" = xno ; then
|
||||||
|
AC_DEFINE(signed,, [Define to empty if the C compiler does not understand signed types.])
|
||||||
|
fi])# PGAC_C_SIGNED
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_PRINTF_ARCHETYPE
|
||||||
|
# -----------------------
|
||||||
|
# Select the format archetype to be used by gcc to check printf-type functions.
|
||||||
|
# We prefer "gnu_printf", as that most closely matches the features supported
|
||||||
|
# by src/port/snprintf.c (particularly the %m conversion spec). However,
|
||||||
|
# on some NetBSD versions, that doesn't work while "__syslog__" does.
|
||||||
|
# If all else fails, use "printf".
|
||||||
|
AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
|
||||||
|
[AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
|
||||||
|
[pgac_cv_printf_archetype=gnu_printf
|
||||||
|
PGAC_TEST_PRINTF_ARCHETYPE
|
||||||
|
if [[ "$ac_archetype_ok" = no ]]; then
|
||||||
|
pgac_cv_printf_archetype=__syslog__
|
||||||
|
PGAC_TEST_PRINTF_ARCHETYPE
|
||||||
|
if [[ "$ac_archetype_ok" = no ]]; then
|
||||||
|
pgac_cv_printf_archetype=printf
|
||||||
|
fi
|
||||||
|
fi])
|
||||||
|
AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
|
||||||
|
[Define to best printf format archetype, usually gnu_printf if available.])
|
||||||
|
])# PGAC_PRINTF_ARCHETYPE
|
||||||
|
|
||||||
|
# Subroutine: test $pgac_cv_printf_archetype, set $ac_archetype_ok to yes or no
|
||||||
|
AC_DEFUN([PGAC_TEST_PRINTF_ARCHETYPE],
|
||||||
|
[ac_save_c_werror_flag=$ac_c_werror_flag
|
||||||
|
ac_c_werror_flag=yes
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[extern void pgac_write(int ignore, const char *fmt,...)
|
||||||
|
__attribute__((format($pgac_cv_printf_archetype, 2, 3)));],
|
||||||
|
[pgac_write(0, "error %s: %m", "foo");])],
|
||||||
|
[ac_archetype_ok=yes],
|
||||||
|
[ac_archetype_ok=no])
|
||||||
|
ac_c_werror_flag=$ac_save_c_werror_flag
|
||||||
|
])# PGAC_TEST_PRINTF_ARCHETYPE
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_TYPE_64BIT_INT(TYPE)
|
||||||
|
# -------------------------
|
||||||
|
# Check if TYPE is a working 64 bit integer type. Set HAVE_TYPE_64 to
|
||||||
|
# yes or no respectively, and define HAVE_TYPE_64 if yes.
|
||||||
|
AC_DEFUN([PGAC_TYPE_64BIT_INT],
|
||||||
|
[define([Ac_define], [translit([have_$1_64], [a-z *], [A-Z_P])])dnl
|
||||||
|
define([Ac_cachevar], [translit([pgac_cv_type_$1_64], [ *], [_p])])dnl
|
||||||
|
AC_CACHE_CHECK([whether $1 is 64 bits], [Ac_cachevar],
|
||||||
|
[AC_RUN_IFELSE([AC_LANG_SOURCE(
|
||||||
|
[typedef $1 ac_int64;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are globals to discourage the compiler from folding all the
|
||||||
|
* arithmetic tests down to compile-time constants.
|
||||||
|
*/
|
||||||
|
ac_int64 a = 20000001;
|
||||||
|
ac_int64 b = 40000005;
|
||||||
|
|
||||||
|
int does_int64_work()
|
||||||
|
{
|
||||||
|
ac_int64 c,d;
|
||||||
|
|
||||||
|
if (sizeof(ac_int64) != 8)
|
||||||
|
return 0; /* definitely not the right size */
|
||||||
|
|
||||||
|
/* Do perfunctory checks to see if 64-bit arithmetic seems to work */
|
||||||
|
c = a * b;
|
||||||
|
d = (c + b) / b;
|
||||||
|
if (d != a+1)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main() {
|
||||||
|
return (! does_int64_work());
|
||||||
|
}])],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no],
|
||||||
|
[# If cross-compiling, check the size reported by the compiler and
|
||||||
|
# trust that the arithmetic works.
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_BOOL_COMPILE_TRY([], [sizeof($1) == 8])],
|
||||||
|
Ac_cachevar=yes,
|
||||||
|
Ac_cachevar=no)])])
|
||||||
|
|
||||||
|
Ac_define=$Ac_cachevar
|
||||||
|
if test x"$Ac_cachevar" = xyes ; then
|
||||||
|
AC_DEFINE(Ac_define, 1, [Define to 1 if `]$1[' works and is 64 bits.])
|
||||||
|
fi
|
||||||
|
undefine([Ac_define])dnl
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_TYPE_64BIT_INT
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_TYPE_128BIT_INT
|
||||||
|
# ---------------------
|
||||||
|
# Check if __int128 is a working 128 bit integer type, and if so
|
||||||
|
# define PG_INT128_TYPE to that typename, and define ALIGNOF_PG_INT128_TYPE
|
||||||
|
# as its alignment requirement.
|
||||||
|
#
|
||||||
|
# This currently only detects a GCC/clang extension, but support for other
|
||||||
|
# environments may be added in the future.
|
||||||
|
#
|
||||||
|
# For the moment we only test for support for 128bit math; support for
|
||||||
|
# 128bit literals and snprintf is not required.
|
||||||
|
AC_DEFUN([PGAC_TYPE_128BIT_INT],
|
||||||
|
[AC_CACHE_CHECK([for __int128], [pgac_cv__128bit_int],
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([
|
||||||
|
/*
|
||||||
|
* We don't actually run this test, just link it to verify that any support
|
||||||
|
* functions needed for __int128 are present.
|
||||||
|
*
|
||||||
|
* These are globals to discourage the compiler from folding all the
|
||||||
|
* arithmetic tests down to compile-time constants. We do not have
|
||||||
|
* convenient support for 128bit literals at this point...
|
||||||
|
*/
|
||||||
|
__int128 a = 48828125;
|
||||||
|
__int128 b = 97656250;
|
||||||
|
],[
|
||||||
|
__int128 c,d;
|
||||||
|
a = (a << 12) + 1; /* 200000000001 */
|
||||||
|
b = (b << 12) + 5; /* 400000000005 */
|
||||||
|
/* try the most relevant arithmetic ops */
|
||||||
|
c = a * b;
|
||||||
|
d = (c + b) / b;
|
||||||
|
/* must use the results, else compiler may optimize arithmetic away */
|
||||||
|
if (d != a+1)
|
||||||
|
return 1;
|
||||||
|
])],
|
||||||
|
[pgac_cv__128bit_int=yes],
|
||||||
|
[pgac_cv__128bit_int=no])])
|
||||||
|
if test x"$pgac_cv__128bit_int" = xyes ; then
|
||||||
|
# Use of non-default alignment with __int128 tickles bugs in some compilers.
|
||||||
|
# If not cross-compiling, we can test for bugs and disable use of __int128
|
||||||
|
# with buggy compilers. If cross-compiling, hope for the best.
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83925
|
||||||
|
AC_CACHE_CHECK([for __int128 alignment bug], [pgac_cv__128bit_int_bug],
|
||||||
|
[AC_RUN_IFELSE([AC_LANG_PROGRAM([
|
||||||
|
/* This must match the corresponding code in c.h: */
|
||||||
|
#if defined(__GNUC__) || defined(__SUNPRO_C) || defined(__IBMC__)
|
||||||
|
#define pg_attribute_aligned(a) __attribute__((aligned(a)))
|
||||||
|
#endif
|
||||||
|
typedef __int128 int128a
|
||||||
|
#if defined(pg_attribute_aligned)
|
||||||
|
pg_attribute_aligned(8)
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
int128a holder;
|
||||||
|
void pass_by_val(void *buffer, int128a par) { holder = par; }
|
||||||
|
],[
|
||||||
|
long int i64 = 97656225L << 12;
|
||||||
|
int128a q;
|
||||||
|
pass_by_val(main, (int128a) i64);
|
||||||
|
q = (int128a) i64;
|
||||||
|
if (q != holder)
|
||||||
|
return 1;
|
||||||
|
])],
|
||||||
|
[pgac_cv__128bit_int_bug=ok],
|
||||||
|
[pgac_cv__128bit_int_bug=broken],
|
||||||
|
[pgac_cv__128bit_int_bug="assuming ok"])])
|
||||||
|
if test x"$pgac_cv__128bit_int_bug" != xbroken ; then
|
||||||
|
AC_DEFINE(PG_INT128_TYPE, __int128, [Define to the name of a signed 128-bit integer type.])
|
||||||
|
AC_CHECK_ALIGNOF(PG_INT128_TYPE)
|
||||||
|
fi
|
||||||
|
fi])# PGAC_TYPE_128BIT_INT
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_FUNCNAME_SUPPORT
|
||||||
|
# -----------------------
|
||||||
|
# Check if the C compiler understands __func__ (C99) or __FUNCTION__ (gcc).
|
||||||
|
# Define HAVE_FUNCNAME__FUNC or HAVE_FUNCNAME__FUNCTION accordingly.
|
||||||
|
AC_DEFUN([PGAC_C_FUNCNAME_SUPPORT],
|
||||||
|
[AC_CACHE_CHECK(for __func__, pgac_cv_funcname_func_support,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
|
||||||
|
[printf("%s\n", __func__);])],
|
||||||
|
[pgac_cv_funcname_func_support=yes],
|
||||||
|
[pgac_cv_funcname_func_support=no])])
|
||||||
|
if test x"$pgac_cv_funcname_func_support" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE_FUNCNAME__FUNC, 1,
|
||||||
|
[Define to 1 if your compiler understands __func__.])
|
||||||
|
else
|
||||||
|
AC_CACHE_CHECK(for __FUNCTION__, pgac_cv_funcname_function_support,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
|
||||||
|
[printf("%s\n", __FUNCTION__);])],
|
||||||
|
[pgac_cv_funcname_function_support=yes],
|
||||||
|
[pgac_cv_funcname_function_support=no])])
|
||||||
|
if test x"$pgac_cv_funcname_function_support" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE_FUNCNAME__FUNCTION, 1,
|
||||||
|
[Define to 1 if your compiler understands __FUNCTION__.])
|
||||||
|
fi
|
||||||
|
fi])# PGAC_C_FUNCNAME_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_STATIC_ASSERT
|
||||||
|
# --------------------
|
||||||
|
# Check if the C compiler understands _Static_assert(),
|
||||||
|
# and define HAVE__STATIC_ASSERT if so.
|
||||||
|
#
|
||||||
|
# We actually check the syntax ({ _Static_assert(...) }), because we need
|
||||||
|
# gcc-style compound expressions to be able to wrap the thing into macros.
|
||||||
|
AC_DEFUN([PGAC_C_STATIC_ASSERT],
|
||||||
|
[AC_CACHE_CHECK(for _Static_assert, pgac_cv__static_assert,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[({ _Static_assert(1, "foo"); })])],
|
||||||
|
[pgac_cv__static_assert=yes],
|
||||||
|
[pgac_cv__static_assert=no])])
|
||||||
|
if test x"$pgac_cv__static_assert" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE__STATIC_ASSERT, 1,
|
||||||
|
[Define to 1 if your compiler understands _Static_assert.])
|
||||||
|
fi])# PGAC_C_STATIC_ASSERT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_TYPEOF
|
||||||
|
# -------------
|
||||||
|
# Check if the C compiler understands typeof or a variant. Define
|
||||||
|
# HAVE_TYPEOF if so, and define 'typeof' to the actual key word.
|
||||||
|
#
|
||||||
|
AC_DEFUN([PGAC_C_TYPEOF],
|
||||||
|
[AC_CACHE_CHECK(for typeof, pgac_cv_c_typeof,
|
||||||
|
[pgac_cv_c_typeof=no
|
||||||
|
for pgac_kw in typeof __typeof__ decltype; do
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[int x = 0;
|
||||||
|
$pgac_kw(x) y;
|
||||||
|
y = x;
|
||||||
|
return y;])],
|
||||||
|
[pgac_cv_c_typeof=$pgac_kw])
|
||||||
|
test "$pgac_cv_c_typeof" != no && break
|
||||||
|
done])
|
||||||
|
if test "$pgac_cv_c_typeof" != no; then
|
||||||
|
AC_DEFINE(HAVE_TYPEOF, 1,
|
||||||
|
[Define to 1 if your compiler understands `typeof' or something similar.])
|
||||||
|
if test "$pgac_cv_c_typeof" != typeof; then
|
||||||
|
AC_DEFINE_UNQUOTED(typeof, $pgac_cv_c_typeof, [Define to how the compiler spells `typeof'.])
|
||||||
|
fi
|
||||||
|
fi])# PGAC_C_TYPEOF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_TYPES_COMPATIBLE
|
||||||
|
# -----------------------
|
||||||
|
# Check if the C compiler understands __builtin_types_compatible_p,
|
||||||
|
# and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
|
||||||
|
#
|
||||||
|
# We check usage with __typeof__, though it's unlikely any compiler would
|
||||||
|
# have the former and not the latter.
|
||||||
|
AC_DEFUN([PGAC_C_TYPES_COMPATIBLE],
|
||||||
|
[AC_CACHE_CHECK(for __builtin_types_compatible_p, pgac_cv__types_compatible,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[[ int x; static int y[__builtin_types_compatible_p(__typeof__(x), int)]; ]])],
|
||||||
|
[pgac_cv__types_compatible=yes],
|
||||||
|
[pgac_cv__types_compatible=no])])
|
||||||
|
if test x"$pgac_cv__types_compatible" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE__BUILTIN_TYPES_COMPATIBLE_P, 1,
|
||||||
|
[Define to 1 if your compiler understands __builtin_types_compatible_p.])
|
||||||
|
fi])# PGAC_C_TYPES_COMPATIBLE
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_BUILTIN_CONSTANT_P
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __builtin_constant_p(),
|
||||||
|
# and define HAVE__BUILTIN_CONSTANT_P if so.
|
||||||
|
# We need __builtin_constant_p("string literal") to be true, but some older
|
||||||
|
# compilers don't think that, so test for that case explicitly.
|
||||||
|
AC_DEFUN([PGAC_C_BUILTIN_CONSTANT_P],
|
||||||
|
[AC_CACHE_CHECK(for __builtin_constant_p, pgac_cv__builtin_constant_p,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||||
|
[[static int x;
|
||||||
|
static int y[__builtin_constant_p(x) ? x : 1];
|
||||||
|
static int z[__builtin_constant_p("string literal") ? 1 : x];
|
||||||
|
]]
|
||||||
|
)],
|
||||||
|
[pgac_cv__builtin_constant_p=yes],
|
||||||
|
[pgac_cv__builtin_constant_p=no])])
|
||||||
|
if test x"$pgac_cv__builtin_constant_p" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE__BUILTIN_CONSTANT_P, 1,
|
||||||
|
[Define to 1 if your compiler understands __builtin_constant_p.])
|
||||||
|
fi])# PGAC_C_BUILTIN_CONSTANT_P
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_BUILTIN_OP_OVERFLOW
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __builtin_$op_overflow(),
|
||||||
|
# and define HAVE__BUILTIN_OP_OVERFLOW if so.
|
||||||
|
#
|
||||||
|
# Check for the most complicated case, 64 bit multiplication, as a
|
||||||
|
# proxy for all of the operations. To detect the case where the compiler
|
||||||
|
# knows the function but library support is missing, we must link not just
|
||||||
|
# compile, and store the results in global variables so the compiler doesn't
|
||||||
|
# optimize away the call.
|
||||||
|
AC_DEFUN([PGAC_C_BUILTIN_OP_OVERFLOW],
|
||||||
|
[AC_CACHE_CHECK(for __builtin_mul_overflow, pgac_cv__builtin_op_overflow,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([
|
||||||
|
PG_INT64_TYPE a = 1;
|
||||||
|
PG_INT64_TYPE b = 1;
|
||||||
|
PG_INT64_TYPE result;
|
||||||
|
int oflo;
|
||||||
|
],
|
||||||
|
[oflo = __builtin_mul_overflow(a, b, &result);])],
|
||||||
|
[pgac_cv__builtin_op_overflow=yes],
|
||||||
|
[pgac_cv__builtin_op_overflow=no])])
|
||||||
|
if test x"$pgac_cv__builtin_op_overflow" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE__BUILTIN_OP_OVERFLOW, 1,
|
||||||
|
[Define to 1 if your compiler understands __builtin_$op_overflow.])
|
||||||
|
fi])# PGAC_C_BUILTIN_OP_OVERFLOW
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_BUILTIN_UNREACHABLE
|
||||||
|
# --------------------------
|
||||||
|
# Check if the C compiler understands __builtin_unreachable(),
|
||||||
|
# and define HAVE__BUILTIN_UNREACHABLE if so.
|
||||||
|
#
|
||||||
|
# NB: Don't get the idea of putting a for(;;); or such before the
|
||||||
|
# __builtin_unreachable() call. Some compilers would remove it before linking
|
||||||
|
# and only a warning instead of an error would be produced.
|
||||||
|
AC_DEFUN([PGAC_C_BUILTIN_UNREACHABLE],
|
||||||
|
[AC_CACHE_CHECK(for __builtin_unreachable, pgac_cv__builtin_unreachable,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[__builtin_unreachable();])],
|
||||||
|
[pgac_cv__builtin_unreachable=yes],
|
||||||
|
[pgac_cv__builtin_unreachable=no])])
|
||||||
|
if test x"$pgac_cv__builtin_unreachable" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE__BUILTIN_UNREACHABLE, 1,
|
||||||
|
[Define to 1 if your compiler understands __builtin_unreachable.])
|
||||||
|
fi])# PGAC_C_BUILTIN_UNREACHABLE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_C_COMPUTED_GOTO
|
||||||
|
# -----------------------
|
||||||
|
# Check if the C compiler knows computed gotos (gcc extension, also
|
||||||
|
# available in at least clang). If so, define HAVE_COMPUTED_GOTO.
|
||||||
|
#
|
||||||
|
# Checking whether computed gotos are supported syntax-wise ought to
|
||||||
|
# be enough, as the syntax is otherwise illegal.
|
||||||
|
AC_DEFUN([PGAC_C_COMPUTED_GOTO],
|
||||||
|
[AC_CACHE_CHECK(for computed goto support, pgac_cv_computed_goto,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[[void *labeladdrs[] = {&&my_label};
|
||||||
|
goto *labeladdrs[0];
|
||||||
|
my_label:
|
||||||
|
return 1;
|
||||||
|
]])],
|
||||||
|
[pgac_cv_computed_goto=yes],
|
||||||
|
[pgac_cv_computed_goto=no])])
|
||||||
|
if test x"$pgac_cv_computed_goto" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE_COMPUTED_GOTO, 1,
|
||||||
|
[Define to 1 if your compiler handles computed gotos.])
|
||||||
|
fi])# PGAC_C_COMPUTED_GOTO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_BUILTIN_FUNC
|
||||||
|
# -----------------------
|
||||||
|
# This is similar to AC_CHECK_FUNCS(), except that it will work for compiler
|
||||||
|
# builtin functions, as that usually fails to.
|
||||||
|
# The first argument is the function name, eg [__builtin_clzl], and the
|
||||||
|
# second is its argument list, eg [unsigned long x]. The current coding
|
||||||
|
# works only for a single argument named x; we might generalize that later.
|
||||||
|
# It's assumed that the function's result type is coercible to int.
|
||||||
|
# On success, we define "HAVEfuncname" (there's usually more than enough
|
||||||
|
# underscores already, so we don't add another one).
|
||||||
|
AC_DEFUN([PGAC_CHECK_BUILTIN_FUNC],
|
||||||
|
[AC_CACHE_CHECK(for $1, pgac_cv$1,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([
|
||||||
|
int
|
||||||
|
call$1($2)
|
||||||
|
{
|
||||||
|
return $1(x);
|
||||||
|
}], [])],
|
||||||
|
[pgac_cv$1=yes],
|
||||||
|
[pgac_cv$1=no])])
|
||||||
|
if test x"${pgac_cv$1}" = xyes ; then
|
||||||
|
AC_DEFINE_UNQUOTED(AS_TR_CPP([HAVE$1]), 1,
|
||||||
|
[Define to 1 if your compiler understands $1.])
|
||||||
|
fi])# PGAC_CHECK_BUILTIN_FUNC
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_VARCC_VARFLAGS_OPT
|
||||||
|
# -----------------------
|
||||||
|
# Given a compiler, variable name and a string, check if the compiler
|
||||||
|
# supports the string as a command-line option. If it does, add the
|
||||||
|
# string to the given variable.
|
||||||
|
AC_DEFUN([PGAC_PROG_VARCC_VARFLAGS_OPT],
|
||||||
|
[define([Ac_cachevar], [AS_TR_SH([pgac_cv_prog_$1_cflags_$3])])dnl
|
||||||
|
AC_CACHE_CHECK([whether ${$1} supports $3, for $2], [Ac_cachevar],
|
||||||
|
[pgac_save_CFLAGS=$CFLAGS
|
||||||
|
pgac_save_CC=$CC
|
||||||
|
CC=${$1}
|
||||||
|
CFLAGS="${$2} $3"
|
||||||
|
ac_save_c_werror_flag=$ac_c_werror_flag
|
||||||
|
ac_c_werror_flag=yes
|
||||||
|
_AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no])
|
||||||
|
ac_c_werror_flag=$ac_save_c_werror_flag
|
||||||
|
CFLAGS="$pgac_save_CFLAGS"
|
||||||
|
CC="$pgac_save_CC"])
|
||||||
|
if test x"$Ac_cachevar" = x"yes"; then
|
||||||
|
$2="${$2} $3"
|
||||||
|
fi
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_PROG_VARCC_VARFLAGS_OPT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_CC_CFLAGS_OPT
|
||||||
|
# -----------------------
|
||||||
|
# Given a string, check if the compiler supports the string as a
|
||||||
|
# command-line option. If it does, add the string to CFLAGS.
|
||||||
|
AC_DEFUN([PGAC_PROG_CC_CFLAGS_OPT], [
|
||||||
|
PGAC_PROG_VARCC_VARFLAGS_OPT(CC, CFLAGS, $1)
|
||||||
|
])# PGAC_PROG_CC_CFLAGS_OPT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_CC_VAR_OPT
|
||||||
|
# -----------------------
|
||||||
|
# Given a variable name and a string, check if the compiler supports
|
||||||
|
# the string as a command-line option. If it does, add the string to
|
||||||
|
# the given variable.
|
||||||
|
AC_DEFUN([PGAC_PROG_CC_VAR_OPT],
|
||||||
|
[PGAC_PROG_VARCC_VARFLAGS_OPT(CC, $1, $2)
|
||||||
|
])# PGAC_PROG_CC_VAR_OPT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_VARCXX_VARFLAGS_OPT
|
||||||
|
# -----------------------
|
||||||
|
# Given a compiler, variable name and a string, check if the compiler
|
||||||
|
# supports the string as a command-line option. If it does, add the
|
||||||
|
# string to the given variable.
|
||||||
|
AC_DEFUN([PGAC_PROG_VARCXX_VARFLAGS_OPT],
|
||||||
|
[define([Ac_cachevar], [AS_TR_SH([pgac_cv_prog_$1_cxxflags_$3])])dnl
|
||||||
|
AC_CACHE_CHECK([whether ${$1} supports $3, for $2], [Ac_cachevar],
|
||||||
|
[pgac_save_CXXFLAGS=$CXXFLAGS
|
||||||
|
pgac_save_CXX=$CXX
|
||||||
|
CXX=${$1}
|
||||||
|
CXXFLAGS="${$2} $3"
|
||||||
|
ac_save_cxx_werror_flag=$ac_cxx_werror_flag
|
||||||
|
ac_cxx_werror_flag=yes
|
||||||
|
AC_LANG_PUSH(C++)
|
||||||
|
_AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no])
|
||||||
|
AC_LANG_POP([])
|
||||||
|
ac_cxx_werror_flag=$ac_save_cxx_werror_flag
|
||||||
|
CXXFLAGS="$pgac_save_CXXFLAGS"
|
||||||
|
CXX="$pgac_save_CXX"])
|
||||||
|
if test x"$Ac_cachevar" = x"yes"; then
|
||||||
|
$2="${$2} $3"
|
||||||
|
fi
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_PROG_VARCXX_VARFLAGS_OPT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_CXX_CFLAGS_OPT
|
||||||
|
# -----------------------
|
||||||
|
# Given a string, check if the compiler supports the string as a
|
||||||
|
# command-line option. If it does, add the string to CXXFLAGS.
|
||||||
|
AC_DEFUN([PGAC_PROG_CXX_CFLAGS_OPT],
|
||||||
|
[PGAC_PROG_VARCXX_VARFLAGS_OPT(CXX, CXXFLAGS, $1)
|
||||||
|
])# PGAC_PROG_CXX_VAR_OPT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PROG_CC_LDFLAGS_OPT
|
||||||
|
# ------------------------
|
||||||
|
# Given a string, check if the compiler supports the string as a
|
||||||
|
# command-line option. If it does, add the string to LDFLAGS.
|
||||||
|
# For reasons you'd really rather not know about, this checks whether
|
||||||
|
# you can link to a particular function, not just whether you can link.
|
||||||
|
# In fact, we must actually check that the resulting program runs :-(
|
||||||
|
AC_DEFUN([PGAC_PROG_CC_LDFLAGS_OPT],
|
||||||
|
[define([Ac_cachevar], [AS_TR_SH([pgac_cv_prog_cc_ldflags_$1])])dnl
|
||||||
|
AC_CACHE_CHECK([whether $CC supports $1], [Ac_cachevar],
|
||||||
|
[pgac_save_LDFLAGS=$LDFLAGS
|
||||||
|
LDFLAGS="$pgac_save_LDFLAGS $1"
|
||||||
|
AC_RUN_IFELSE([AC_LANG_PROGRAM([extern void $2 (); void (*fptr) () = $2;],[])],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no],
|
||||||
|
[Ac_cachevar="assuming no"])
|
||||||
|
LDFLAGS="$pgac_save_LDFLAGS"])
|
||||||
|
if test x"$Ac_cachevar" = x"yes"; then
|
||||||
|
LDFLAGS="$LDFLAGS $1"
|
||||||
|
fi
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_PROG_CC_LDFLAGS_OPT
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__SYNC_CHAR_TAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __sync_lock_test_and_set(char),
|
||||||
|
# and define HAVE_GCC__SYNC_CHAR_TAS
|
||||||
|
#
|
||||||
|
# NB: There are platforms where test_and_set is available but compare_and_swap
|
||||||
|
# is not, so test this separately.
|
||||||
|
# NB: Some platforms only do 32bit tas, others only do 8bit tas. Test both.
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__SYNC_CHAR_TAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __sync char locking functions, pgac_cv_gcc_sync_char_tas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[char lock = 0;
|
||||||
|
__sync_lock_test_and_set(&lock, 1);
|
||||||
|
__sync_lock_release(&lock);])],
|
||||||
|
[pgac_cv_gcc_sync_char_tas="yes"],
|
||||||
|
[pgac_cv_gcc_sync_char_tas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_sync_char_tas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__SYNC_CHAR_TAS, 1, [Define to 1 if you have __sync_lock_test_and_set(char *) and friends.])
|
||||||
|
fi])# PGAC_HAVE_GCC__SYNC_CHAR_TAS
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__SYNC_INT32_TAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __sync_lock_test_and_set(),
|
||||||
|
# and define HAVE_GCC__SYNC_INT32_TAS
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT32_TAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __sync int32 locking functions, pgac_cv_gcc_sync_int32_tas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[int lock = 0;
|
||||||
|
__sync_lock_test_and_set(&lock, 1);
|
||||||
|
__sync_lock_release(&lock);])],
|
||||||
|
[pgac_cv_gcc_sync_int32_tas="yes"],
|
||||||
|
[pgac_cv_gcc_sync_int32_tas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_sync_int32_tas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__SYNC_INT32_TAS, 1, [Define to 1 if you have __sync_lock_test_and_set(int *) and friends.])
|
||||||
|
fi])# PGAC_HAVE_GCC__SYNC_INT32_TAS
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__SYNC_INT32_CAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __sync_compare_and_swap() for 32bit
|
||||||
|
# types, and define HAVE_GCC__SYNC_INT32_CAS if so.
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT32_CAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __sync int32 atomic operations, pgac_cv_gcc_sync_int32_cas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[int val = 0;
|
||||||
|
__sync_val_compare_and_swap(&val, 0, 37);])],
|
||||||
|
[pgac_cv_gcc_sync_int32_cas="yes"],
|
||||||
|
[pgac_cv_gcc_sync_int32_cas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_sync_int32_cas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__SYNC_INT32_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int *, int, int).])
|
||||||
|
fi])# PGAC_HAVE_GCC__SYNC_INT32_CAS
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__SYNC_INT64_CAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __sync_compare_and_swap() for 64bit
|
||||||
|
# types, and define HAVE_GCC__SYNC_INT64_CAS if so.
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__SYNC_INT64_CAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __sync int64 atomic operations, pgac_cv_gcc_sync_int64_cas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[PG_INT64_TYPE lock = 0;
|
||||||
|
__sync_val_compare_and_swap(&lock, 0, (PG_INT64_TYPE) 37);])],
|
||||||
|
[pgac_cv_gcc_sync_int64_cas="yes"],
|
||||||
|
[pgac_cv_gcc_sync_int64_cas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_sync_int64_cas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__SYNC_INT64_CAS, 1, [Define to 1 if you have __sync_val_compare_and_swap(int64 *, int64, int64).])
|
||||||
|
fi])# PGAC_HAVE_GCC__SYNC_INT64_CAS
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __atomic_compare_exchange_n() for 32bit
|
||||||
|
# types, and define HAVE_GCC__ATOMIC_INT32_CAS if so.
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT32_CAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __atomic int32 atomic operations, pgac_cv_gcc_atomic_int32_cas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[int val = 0;
|
||||||
|
int expect = 0;
|
||||||
|
__atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
|
||||||
|
[pgac_cv_gcc_atomic_int32_cas="yes"],
|
||||||
|
[pgac_cv_gcc_atomic_int32_cas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_atomic_int32_cas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__ATOMIC_INT32_CAS, 1, [Define to 1 if you have __atomic_compare_exchange_n(int *, int *, int).])
|
||||||
|
fi])# PGAC_HAVE_GCC__ATOMIC_INT32_CAS
|
||||||
|
|
||||||
|
# PGAC_HAVE_GCC__ATOMIC_INT64_CAS
|
||||||
|
# -------------------------
|
||||||
|
# Check if the C compiler understands __atomic_compare_exchange_n() for 64bit
|
||||||
|
# types, and define HAVE_GCC__ATOMIC_INT64_CAS if so.
|
||||||
|
AC_DEFUN([PGAC_HAVE_GCC__ATOMIC_INT64_CAS],
|
||||||
|
[AC_CACHE_CHECK(for builtin __atomic int64 atomic operations, pgac_cv_gcc_atomic_int64_cas,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||||
|
[PG_INT64_TYPE val = 0;
|
||||||
|
PG_INT64_TYPE expect = 0;
|
||||||
|
__atomic_compare_exchange_n(&val, &expect, 37, 0, __ATOMIC_SEQ_CST, __ATOMIC_RELAXED);])],
|
||||||
|
[pgac_cv_gcc_atomic_int64_cas="yes"],
|
||||||
|
[pgac_cv_gcc_atomic_int64_cas="no"])])
|
||||||
|
if test x"$pgac_cv_gcc_atomic_int64_cas" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_GCC__ATOMIC_INT64_CAS, 1, [Define to 1 if you have __atomic_compare_exchange_n(int64 *, int64 *, int64).])
|
||||||
|
fi])# PGAC_HAVE_GCC__ATOMIC_INT64_CAS
|
||||||
|
|
||||||
|
# PGAC_SSE42_CRC32_INTRINSICS
|
||||||
|
# -----------------------
|
||||||
|
# Check if the compiler supports the x86 CRC instructions added in SSE 4.2,
|
||||||
|
# using the _mm_crc32_u8 and _mm_crc32_u32 intrinsic functions. (We don't
|
||||||
|
# test the 8-byte variant, _mm_crc32_u64, but it is assumed to be present if
|
||||||
|
# the other ones are, on x86-64 platforms)
|
||||||
|
#
|
||||||
|
# An optional compiler flag can be passed as argument (e.g. -msse4.2). If the
|
||||||
|
# intrinsics are supported, sets pgac_sse42_crc32_intrinsics, and CFLAGS_SSE42.
|
||||||
|
AC_DEFUN([PGAC_SSE42_CRC32_INTRINSICS],
|
||||||
|
[define([Ac_cachevar], [AS_TR_SH([pgac_cv_sse42_crc32_intrinsics_$1])])dnl
|
||||||
|
AC_CACHE_CHECK([for _mm_crc32_u8 and _mm_crc32_u32 with CFLAGS=$1], [Ac_cachevar],
|
||||||
|
[pgac_save_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$pgac_save_CFLAGS $1"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <nmmintrin.h>],
|
||||||
|
[unsigned int crc = 0;
|
||||||
|
crc = _mm_crc32_u8(crc, 0);
|
||||||
|
crc = _mm_crc32_u32(crc, 0);
|
||||||
|
/* return computed value, to prevent the above being optimized away */
|
||||||
|
return crc == 0;])],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no])
|
||||||
|
CFLAGS="$pgac_save_CFLAGS"])
|
||||||
|
if test x"$Ac_cachevar" = x"yes"; then
|
||||||
|
CFLAGS_SSE42="$1"
|
||||||
|
pgac_sse42_crc32_intrinsics=yes
|
||||||
|
fi
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_SSE42_CRC32_INTRINSICS
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_ARMV8_CRC32C_INTRINSICS
|
||||||
|
# -----------------------
|
||||||
|
# Check if the compiler supports the CRC32C instructions using the __crc32cb,
|
||||||
|
# __crc32ch, __crc32cw, and __crc32cd intrinsic functions. These instructions
|
||||||
|
# were first introduced in ARMv8 in the optional CRC Extension, and became
|
||||||
|
# mandatory in ARMv8.1.
|
||||||
|
#
|
||||||
|
# An optional compiler flag can be passed as argument (e.g.
|
||||||
|
# -march=armv8-a+crc). If the intrinsics are supported, sets
|
||||||
|
# pgac_armv8_crc32c_intrinsics, and CFLAGS_ARMV8_CRC32C.
|
||||||
|
AC_DEFUN([PGAC_ARMV8_CRC32C_INTRINSICS],
|
||||||
|
[define([Ac_cachevar], [AS_TR_SH([pgac_cv_armv8_crc32c_intrinsics_$1])])dnl
|
||||||
|
AC_CACHE_CHECK([for __crc32cb, __crc32ch, __crc32cw, and __crc32cd with CFLAGS=$1], [Ac_cachevar],
|
||||||
|
[pgac_save_CFLAGS=$CFLAGS
|
||||||
|
CFLAGS="$pgac_save_CFLAGS $1"
|
||||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <arm_acle.h>],
|
||||||
|
[unsigned int crc = 0;
|
||||||
|
crc = __crc32cb(crc, 0);
|
||||||
|
crc = __crc32ch(crc, 0);
|
||||||
|
crc = __crc32cw(crc, 0);
|
||||||
|
crc = __crc32cd(crc, 0);
|
||||||
|
/* return computed value, to prevent the above being optimized away */
|
||||||
|
return crc == 0;])],
|
||||||
|
[Ac_cachevar=yes],
|
||||||
|
[Ac_cachevar=no])
|
||||||
|
CFLAGS="$pgac_save_CFLAGS"])
|
||||||
|
if test x"$Ac_cachevar" = x"yes"; then
|
||||||
|
CFLAGS_ARMV8_CRC32C="$1"
|
||||||
|
pgac_armv8_crc32c_intrinsics=yes
|
||||||
|
fi
|
||||||
|
undefine([Ac_cachevar])dnl
|
||||||
|
])# PGAC_ARMV8_CRC32C_INTRINSICS
|
|
@ -0,0 +1,230 @@
|
||||||
|
# Macros that test various C library quirks
|
||||||
|
# config/c-library.m4
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_VAR_INT_TIMEZONE
|
||||||
|
# ---------------------
|
||||||
|
# Check if the global variable `timezone' exists. If so, define
|
||||||
|
# HAVE_INT_TIMEZONE.
|
||||||
|
AC_DEFUN([PGAC_VAR_INT_TIMEZONE],
|
||||||
|
[AC_CACHE_CHECK(for int timezone, pgac_cv_var_int_timezone,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <time.h>
|
||||||
|
int res;],
|
||||||
|
[#ifndef __CYGWIN__
|
||||||
|
res = timezone / 60;
|
||||||
|
#else
|
||||||
|
res = _timezone / 60;
|
||||||
|
#endif])],
|
||||||
|
[pgac_cv_var_int_timezone=yes],
|
||||||
|
[pgac_cv_var_int_timezone=no])])
|
||||||
|
if test x"$pgac_cv_var_int_timezone" = xyes ; then
|
||||||
|
AC_DEFINE(HAVE_INT_TIMEZONE, 1,
|
||||||
|
[Define to 1 if you have the global variable 'int timezone'.])
|
||||||
|
fi])# PGAC_VAR_INT_TIMEZONE
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_STRUCT_TIMEZONE
|
||||||
|
# ------------------
|
||||||
|
# Figure out how to get the current timezone. If `struct tm' has a
|
||||||
|
# `tm_zone' member, define `HAVE_TM_ZONE'. Also, if the
|
||||||
|
# external array `tzname' is found, define `HAVE_TZNAME'.
|
||||||
|
# This is the same as the standard macro AC_STRUCT_TIMEZONE, except that
|
||||||
|
# tzname[] is checked for regardless of whether we find tm_zone.
|
||||||
|
AC_DEFUN([PGAC_STRUCT_TIMEZONE],
|
||||||
|
[AC_REQUIRE([AC_STRUCT_TM])dnl
|
||||||
|
AC_CHECK_MEMBERS([struct tm.tm_zone],,,[#include <sys/types.h>
|
||||||
|
#include <$ac_cv_struct_tm>
|
||||||
|
])
|
||||||
|
if test "$ac_cv_member_struct_tm_tm_zone" = yes; then
|
||||||
|
AC_DEFINE(HAVE_TM_ZONE, 1,
|
||||||
|
[Define to 1 if your `struct tm' has `tm_zone'. Deprecated, use
|
||||||
|
`HAVE_STRUCT_TM_TM_ZONE' instead.])
|
||||||
|
fi
|
||||||
|
AC_CACHE_CHECK(for tzname, ac_cv_var_tzname,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[[#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#ifndef tzname /* For SGI. */
|
||||||
|
extern char *tzname[]; /* RS6000 and others reject char **tzname. */
|
||||||
|
#endif
|
||||||
|
]],
|
||||||
|
[atoi(*tzname);])], ac_cv_var_tzname=yes, ac_cv_var_tzname=no)])
|
||||||
|
if test $ac_cv_var_tzname = yes; then
|
||||||
|
AC_DEFINE(HAVE_TZNAME, 1,
|
||||||
|
[Define to 1 if you have the external array `tzname'.])
|
||||||
|
fi
|
||||||
|
])# PGAC_STRUCT_TIMEZONE
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_FUNC_GETTIMEOFDAY_1ARG
|
||||||
|
# ---------------------------
|
||||||
|
# Check if gettimeofday() has only one arguments. (Normal is two.)
|
||||||
|
# If so, define GETTIMEOFDAY_1ARG.
|
||||||
|
AC_DEFUN([PGAC_FUNC_GETTIMEOFDAY_1ARG],
|
||||||
|
[AC_CACHE_CHECK(whether gettimeofday takes only one argument,
|
||||||
|
pgac_cv_func_gettimeofday_1arg,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <sys/time.h>],
|
||||||
|
[struct timeval *tp;
|
||||||
|
struct timezone *tzp;
|
||||||
|
gettimeofday(tp,tzp);])],
|
||||||
|
[pgac_cv_func_gettimeofday_1arg=no],
|
||||||
|
[pgac_cv_func_gettimeofday_1arg=yes])])
|
||||||
|
if test x"$pgac_cv_func_gettimeofday_1arg" = xyes ; then
|
||||||
|
AC_DEFINE(GETTIMEOFDAY_1ARG, 1,
|
||||||
|
[Define to 1 if gettimeofday() takes only 1 argument.])
|
||||||
|
fi
|
||||||
|
AH_VERBATIM(GETTIMEOFDAY_1ARG_,
|
||||||
|
[@%:@ifdef GETTIMEOFDAY_1ARG
|
||||||
|
@%:@ define gettimeofday(a,b) gettimeofday(a)
|
||||||
|
@%:@endif])dnl
|
||||||
|
])# PGAC_FUNC_GETTIMEOFDAY_1ARG
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_FUNC_STRERROR_R_INT
|
||||||
|
# ---------------------------
|
||||||
|
# Check if strerror_r() returns int (POSIX) rather than char * (GNU libc).
|
||||||
|
# If so, define STRERROR_R_INT.
|
||||||
|
# The result is uncertain if strerror_r() isn't provided,
|
||||||
|
# but we don't much care.
|
||||||
|
AC_DEFUN([PGAC_FUNC_STRERROR_R_INT],
|
||||||
|
[AC_CACHE_CHECK(whether strerror_r returns int,
|
||||||
|
pgac_cv_func_strerror_r_int,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <string.h>],
|
||||||
|
[[char buf[100];
|
||||||
|
switch (strerror_r(1, buf, sizeof(buf)))
|
||||||
|
{ case 0: break; default: break; }
|
||||||
|
]])],
|
||||||
|
[pgac_cv_func_strerror_r_int=yes],
|
||||||
|
[pgac_cv_func_strerror_r_int=no])])
|
||||||
|
if test x"$pgac_cv_func_strerror_r_int" = xyes ; then
|
||||||
|
AC_DEFINE(STRERROR_R_INT, 1,
|
||||||
|
[Define to 1 if strerror_r() returns int.])
|
||||||
|
fi
|
||||||
|
])# PGAC_FUNC_STRERROR_R_INT
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_UNION_SEMUN
|
||||||
|
# ----------------
|
||||||
|
# Check if `union semun' exists. Define HAVE_UNION_SEMUN if so.
|
||||||
|
# If it doesn't then one could define it as
|
||||||
|
# union semun { int val; struct semid_ds *buf; unsigned short *array; }
|
||||||
|
AC_DEFUN([PGAC_UNION_SEMUN],
|
||||||
|
[AC_CHECK_TYPES([union semun], [], [],
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_IPC_H
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_SEM_H
|
||||||
|
#include <sys/sem.h>
|
||||||
|
#endif])])# PGAC_UNION_SEMUN
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_STRUCT_SOCKADDR_UN
|
||||||
|
# -----------------------
|
||||||
|
# If `struct sockaddr_un' exists, define HAVE_UNIX_SOCKETS.
|
||||||
|
# (Requires test for <sys/un.h>!)
|
||||||
|
AC_DEFUN([PGAC_STRUCT_SOCKADDR_UN],
|
||||||
|
[AC_CHECK_TYPE([struct sockaddr_un], [AC_DEFINE(HAVE_UNIX_SOCKETS, 1, [Define to 1 if you have unix sockets.])], [],
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#ifdef HAVE_SYS_UN_H
|
||||||
|
#include <sys/un.h>
|
||||||
|
#endif
|
||||||
|
])])# PGAC_STRUCT_SOCKADDR_UN
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_STRUCT_SOCKADDR_STORAGE
|
||||||
|
# ----------------------------
|
||||||
|
# If `struct sockaddr_storage' exists, define HAVE_STRUCT_SOCKADDR_STORAGE.
|
||||||
|
# If it is missing then one could define it.
|
||||||
|
AC_DEFUN([PGAC_STRUCT_SOCKADDR_STORAGE],
|
||||||
|
[AC_CHECK_TYPES([struct sockaddr_storage], [], [],
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
])])# PGAC_STRUCT_SOCKADDR_STORAGE
|
||||||
|
|
||||||
|
# PGAC_STRUCT_SOCKADDR_STORAGE_MEMBERS
|
||||||
|
# --------------------------------------
|
||||||
|
# Check the members of `struct sockaddr_storage'. We need to know about
|
||||||
|
# ss_family and ss_len. (Some platforms follow RFC 2553 and call them
|
||||||
|
# __ss_family and __ss_len.) We also check struct sockaddr's sa_len;
|
||||||
|
# if we have to define our own `struct sockaddr_storage', this tells us
|
||||||
|
# whether we need to provide an ss_len field.
|
||||||
|
AC_DEFUN([PGAC_STRUCT_SOCKADDR_STORAGE_MEMBERS],
|
||||||
|
[AC_CHECK_MEMBERS([struct sockaddr_storage.ss_family,
|
||||||
|
struct sockaddr_storage.__ss_family,
|
||||||
|
struct sockaddr_storage.ss_len,
|
||||||
|
struct sockaddr_storage.__ss_len,
|
||||||
|
struct sockaddr.sa_len], [], [],
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
])])# PGAC_STRUCT_SOCKADDR_STORAGE_MEMBERS
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_STRUCT_ADDRINFO
|
||||||
|
# -----------------------
|
||||||
|
# If `struct addrinfo' exists, define HAVE_STRUCT_ADDRINFO.
|
||||||
|
AC_DEFUN([PGAC_STRUCT_ADDRINFO],
|
||||||
|
[AC_CHECK_TYPES([struct addrinfo], [], [],
|
||||||
|
[#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
])])# PGAC_STRUCT_ADDRINFO
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_TYPE_LOCALE_T
|
||||||
|
# ------------------
|
||||||
|
# Check for the locale_t type and find the right header file. macOS
|
||||||
|
# needs xlocale.h; standard is locale.h, but glibc also has an
|
||||||
|
# xlocale.h file that we should not use.
|
||||||
|
#
|
||||||
|
AC_DEFUN([PGAC_TYPE_LOCALE_T],
|
||||||
|
[AC_CACHE_CHECK([for locale_t], pgac_cv_type_locale_t,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <locale.h>
|
||||||
|
locale_t x;],
|
||||||
|
[])],
|
||||||
|
[pgac_cv_type_locale_t=yes],
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <xlocale.h>
|
||||||
|
locale_t x;],
|
||||||
|
[])],
|
||||||
|
[pgac_cv_type_locale_t='yes (in xlocale.h)'],
|
||||||
|
[pgac_cv_type_locale_t=no])])])
|
||||||
|
if test "$pgac_cv_type_locale_t" != no; then
|
||||||
|
AC_DEFINE(HAVE_LOCALE_T, 1,
|
||||||
|
[Define to 1 if the system has the type `locale_t'.])
|
||||||
|
fi
|
||||||
|
if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then
|
||||||
|
AC_DEFINE(LOCALE_T_IN_XLOCALE, 1,
|
||||||
|
[Define to 1 if `locale_t' requires <xlocale.h>.])
|
||||||
|
fi])# PGAC_TYPE_LOCALE_T
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_FUNC_WCSTOMBS_L
|
||||||
|
# --------------------
|
||||||
|
# Try to find a declaration for wcstombs_l(). It might be in stdlib.h
|
||||||
|
# (following the POSIX requirement for wcstombs()), or in locale.h, or in
|
||||||
|
# xlocale.h. If it's in the latter, define WCSTOMBS_L_IN_XLOCALE.
|
||||||
|
#
|
||||||
|
AC_DEFUN([PGAC_FUNC_WCSTOMBS_L],
|
||||||
|
[AC_CACHE_CHECK([for wcstombs_l declaration], pgac_cv_func_wcstombs_l,
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <stdlib.h>
|
||||||
|
#include <locale.h>],
|
||||||
|
[#ifndef wcstombs_l
|
||||||
|
(void) wcstombs_l;
|
||||||
|
#endif])],
|
||||||
|
[pgac_cv_func_wcstombs_l='yes'],
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <stdlib.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <xlocale.h>],
|
||||||
|
[#ifndef wcstombs_l
|
||||||
|
(void) wcstombs_l;
|
||||||
|
#endif])],
|
||||||
|
[pgac_cv_func_wcstombs_l='yes (in xlocale.h)'],
|
||||||
|
[pgac_cv_func_wcstombs_l='no'])])])
|
||||||
|
if test "$pgac_cv_func_wcstombs_l" = 'yes (in xlocale.h)'; then
|
||||||
|
AC_DEFINE(WCSTOMBS_L_IN_XLOCALE, 1,
|
||||||
|
[Define to 1 if `wcstombs_l' requires <xlocale.h>.])
|
||||||
|
fi])# PGAC_FUNC_WCSTOMBS_L
|
|
@ -0,0 +1,116 @@
|
||||||
|
# config/check_decls.m4
|
||||||
|
|
||||||
|
# This file redefines the standard Autoconf macro _AC_CHECK_DECL_BODY,
|
||||||
|
# and adds a supporting function _AC_UNDECLARED_WARNING, to make
|
||||||
|
# AC_CHECK_DECLS behave correctly when checking for built-in library
|
||||||
|
# functions with clang.
|
||||||
|
|
||||||
|
# This is based on commit 82ef7805faffa151e724aa76c245ec590d174580
|
||||||
|
# in the Autoconf git repository. We can drop it if they ever get
|
||||||
|
# around to releasing a new version of Autoconf. In the meantime,
|
||||||
|
# it's distributed under Autoconf's license:
|
||||||
|
|
||||||
|
# This file is part of Autoconf. This program is free
|
||||||
|
# software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU General Public License as published by the
|
||||||
|
# Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# Under Section 7 of GPL version 3, you are granted additional
|
||||||
|
# permissions described in the Autoconf Configure Script Exception,
|
||||||
|
# version 3.0, as published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# and a copy of the Autoconf Configure Script Exception along with
|
||||||
|
# this program; see the files COPYINGv3 and COPYING.EXCEPTION
|
||||||
|
# respectively. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Written by David MacKenzie, with help from
|
||||||
|
# Franc,ois Pinard, Karl Berry, Richard Pixley, Ian Lance Taylor,
|
||||||
|
# Roland McGrath, Noah Friedman, david d zuhn, and many others.
|
||||||
|
|
||||||
|
|
||||||
|
# _AC_UNDECLARED_WARNING
|
||||||
|
# ----------------------
|
||||||
|
# Set ac_[]_AC_LANG_ABBREV[]_decl_warn_flag=yes if the compiler uses a warning,
|
||||||
|
# not a more-customary error, to report some undeclared identifiers. Fail when
|
||||||
|
# an affected compiler warns also on valid input. _AC_PROG_PREPROC_WORKS_IFELSE
|
||||||
|
# solves a related problem.
|
||||||
|
AC_DEFUN([_AC_UNDECLARED_WARNING],
|
||||||
|
[# The Clang compiler raises a warning for an undeclared identifier that matches
|
||||||
|
# a compiler builtin function. All extant Clang versions are affected, as of
|
||||||
|
# Clang 3.6.0. Test a builtin known to every version. This problem affects the
|
||||||
|
# C and Objective C languages, but Clang does report an error under C++ and
|
||||||
|
# Objective C++.
|
||||||
|
#
|
||||||
|
# Passing -fno-builtin to the compiler would suppress this problem. That
|
||||||
|
# strategy would have the advantage of being insensitive to stray warnings, but
|
||||||
|
# it would make tests less realistic.
|
||||||
|
AC_CACHE_CHECK([how $[]_AC_CC[] reports undeclared, standard C functions],
|
||||||
|
[ac_cv_[]_AC_LANG_ABBREV[]_decl_report],
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [(void) strchr;])],
|
||||||
|
[AS_IF([test -s conftest.err], [dnl
|
||||||
|
# For AC_CHECK_DECL to react to warnings, the compiler must be silent on
|
||||||
|
# valid AC_CHECK_DECL input. No library function is consistently available
|
||||||
|
# on freestanding implementations, so test against a dummy declaration.
|
||||||
|
# Include always-available headers on the off chance that they somehow
|
||||||
|
# elicit warnings.
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([dnl
|
||||||
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
extern void ac_decl (int, char *);],
|
||||||
|
[@%:@ifdef __cplusplus
|
||||||
|
(void) ac_decl ((int) 0, (char *) 0);
|
||||||
|
(void) ac_decl;
|
||||||
|
@%:@else
|
||||||
|
(void) ac_decl;
|
||||||
|
@%:@endif
|
||||||
|
])],
|
||||||
|
[AS_IF([test -s conftest.err],
|
||||||
|
[AC_MSG_FAILURE([cannot detect from compiler exit status or warnings])],
|
||||||
|
[ac_cv_[]_AC_LANG_ABBREV[]_decl_report=warning])],
|
||||||
|
[AC_MSG_FAILURE([cannot compile a simple declaration test])])],
|
||||||
|
[AC_MSG_FAILURE([compiler does not report undeclared identifiers])])],
|
||||||
|
[ac_cv_[]_AC_LANG_ABBREV[]_decl_report=error])])
|
||||||
|
|
||||||
|
case $ac_cv_[]_AC_LANG_ABBREV[]_decl_report in
|
||||||
|
warning) ac_[]_AC_LANG_ABBREV[]_decl_warn_flag=yes ;;
|
||||||
|
*) ac_[]_AC_LANG_ABBREV[]_decl_warn_flag= ;;
|
||||||
|
esac
|
||||||
|
])# _AC_UNDECLARED_WARNING
|
||||||
|
|
||||||
|
# _AC_CHECK_DECL_BODY
|
||||||
|
# -------------------
|
||||||
|
# Shell function body for AC_CHECK_DECL.
|
||||||
|
m4_define([_AC_CHECK_DECL_BODY],
|
||||||
|
[ AS_LINENO_PUSH([$[]1])
|
||||||
|
# Initialize each $ac_[]_AC_LANG_ABBREV[]_decl_warn_flag once.
|
||||||
|
AC_DEFUN([_AC_UNDECLARED_WARNING_]_AC_LANG_ABBREV,
|
||||||
|
[_AC_UNDECLARED_WARNING])dnl
|
||||||
|
AC_REQUIRE([_AC_UNDECLARED_WARNING_]_AC_LANG_ABBREV)dnl
|
||||||
|
[as_decl_name=`echo $][2|sed 's/ *(.*//'`]
|
||||||
|
[as_decl_use=`echo $][2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`]
|
||||||
|
AC_CACHE_CHECK([whether $as_decl_name is declared], [$[]3],
|
||||||
|
[ac_save_werror_flag=$ac_[]_AC_LANG_ABBREV[]_werror_flag
|
||||||
|
ac_[]_AC_LANG_ABBREV[]_werror_flag="$ac_[]_AC_LANG_ABBREV[]_decl_warn_flag$ac_[]_AC_LANG_ABBREV[]_werror_flag"
|
||||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$[]4],
|
||||||
|
[@%:@ifndef $[]as_decl_name
|
||||||
|
@%:@ifdef __cplusplus
|
||||||
|
(void) $[]as_decl_use;
|
||||||
|
@%:@else
|
||||||
|
(void) $[]as_decl_name;
|
||||||
|
@%:@endif
|
||||||
|
@%:@endif
|
||||||
|
])],
|
||||||
|
[AS_VAR_SET([$[]3], [yes])],
|
||||||
|
[AS_VAR_SET([$[]3], [no])])
|
||||||
|
ac_[]_AC_LANG_ABBREV[]_werror_flag=$ac_save_werror_flag])
|
||||||
|
AS_LINENO_POP
|
||||||
|
])# _AC_CHECK_DECL_BODY
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
||||||
|
# config/docbook.m4
|
||||||
|
|
||||||
|
# PGAC_PATH_XMLLINT
|
||||||
|
# -----------------
|
||||||
|
AC_DEFUN([PGAC_PATH_XMLLINT],
|
||||||
|
[PGAC_PATH_PROGS(XMLLINT, xmllint)])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_DOCBOOK(VERSION)
|
||||||
|
# ---------------------------
|
||||||
|
AC_DEFUN([PGAC_CHECK_DOCBOOK],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_XMLLINT])
|
||||||
|
AC_CACHE_CHECK([for DocBook XML V$1], [pgac_cv_check_docbook],
|
||||||
|
[cat >conftest.xml <<EOF
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V$1//EN" "http://www.oasis-open.org/docbook/xml/$1/docbookx.dtd">
|
||||||
|
<book>
|
||||||
|
<title>test</title>
|
||||||
|
<chapter>
|
||||||
|
<title>random</title>
|
||||||
|
<sect1>
|
||||||
|
<title>testsect</title>
|
||||||
|
<para>text</para>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
</book>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
pgac_cv_check_docbook=no
|
||||||
|
|
||||||
|
if test -n "$XMLLINT"; then
|
||||||
|
$XMLLINT --noout --valid conftest.xml 1>&AS_MESSAGE_LOG_FD 2>&1
|
||||||
|
if test $? -eq 0; then
|
||||||
|
pgac_cv_check_docbook=yes
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
rm -f conftest.xml])
|
||||||
|
|
||||||
|
have_docbook=$pgac_cv_check_docbook
|
||||||
|
AC_SUBST([have_docbook])
|
||||||
|
])# PGAC_CHECK_DOCBOOK
|
|
@ -0,0 +1,149 @@
|
||||||
|
# config/general.m4
|
||||||
|
|
||||||
|
# This file defines new macros to process configure command line
|
||||||
|
# arguments, to replace the brain-dead AC_ARG_WITH and AC_ARG_ENABLE.
|
||||||
|
# The flaw in these is particularly that they only differentiate
|
||||||
|
# between "given" and "not given" and do not provide enough help to
|
||||||
|
# process arguments that only accept "yes/no", that require an
|
||||||
|
# argument (other than "yes/no"), etc.
|
||||||
|
#
|
||||||
|
# The point of this implementation is to reduce code size and
|
||||||
|
# redundancy in configure.in and to improve robustness and consistency
|
||||||
|
# in the option evaluation code.
|
||||||
|
|
||||||
|
|
||||||
|
# Convert type and name to shell variable name (e.g., "enable_long_strings")
|
||||||
|
m4_define([pgac_arg_to_variable],
|
||||||
|
[$1[]_[]patsubst($2, -, _)])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_ARG(TYPE, NAME, HELP-STRING-LHS-EXTRA, HELP-STRING-RHS,
|
||||||
|
# [ACTION-IF-YES], [ACTION-IF-NO], [ACTION-IF-ARG],
|
||||||
|
# [ACTION-IF-OMITTED])
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# This is the base layer. TYPE is either "with" or "enable", depending
|
||||||
|
# on what you like. NAME is the rest of the option name.
|
||||||
|
# HELP-STRING-LHS-EXTRA is a string to append to the option name on
|
||||||
|
# the left-hand side of the help output, e.g., an argument name. If
|
||||||
|
# set to "-", append nothing, but let the option appear in the
|
||||||
|
# negative form (disable/without). HELP-STRING-RHS is the option
|
||||||
|
# description, for the right-hand side of the help output.
|
||||||
|
# ACTION-IF-YES is executed if the option is given without an argument
|
||||||
|
# (or "yes", which is the same); similar for ACTION-IF-NO.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_ARG],
|
||||||
|
[
|
||||||
|
m4_case([$1],
|
||||||
|
|
||||||
|
enable, [
|
||||||
|
AC_ARG_ENABLE([$2], [AS_HELP_STRING([--]m4_if($3, -, disable, enable)[-$2]m4_if($3, -, , $3), [$4])], [
|
||||||
|
case [$]enableval in
|
||||||
|
yes)
|
||||||
|
m4_default([$5], :)
|
||||||
|
;;
|
||||||
|
no)
|
||||||
|
m4_default([$6], :)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$7
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
],
|
||||||
|
[$8])[]dnl AC_ARG_ENABLE
|
||||||
|
],
|
||||||
|
|
||||||
|
with, [
|
||||||
|
AC_ARG_WITH([$2], [AS_HELP_STRING([--]m4_if($3, -, without, with)[-$2]m4_if($3, -, , $3), [$4])], [
|
||||||
|
case [$]withval in
|
||||||
|
yes)
|
||||||
|
m4_default([$5], :)
|
||||||
|
;;
|
||||||
|
no)
|
||||||
|
m4_default([$6], :)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
$7
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
],
|
||||||
|
[$8])[]dnl AC_ARG_WITH
|
||||||
|
],
|
||||||
|
|
||||||
|
[m4_fatal([first argument of $0 must be 'enable' or 'with', not '$1'])]
|
||||||
|
)
|
||||||
|
])# PGAC_ARG
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_ARG_BOOL(TYPE, NAME, DEFAULT, HELP-STRING-RHS,
|
||||||
|
# [ACTION-IF-YES], [ACTION-IF-NO])
|
||||||
|
# ---------------------------------------------------
|
||||||
|
# Accept a boolean option, that is, one that only takes yes or no.
|
||||||
|
# ("no" is equivalent to "disable" or "without"). DEFAULT is what
|
||||||
|
# should be done if the option is omitted; it should be "yes" or "no".
|
||||||
|
# (Consequently, one of ACTION-IF-YES and ACTION-IF-NO will always
|
||||||
|
# execute.)
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_ARG_BOOL],
|
||||||
|
[dnl The following hack is necessary because in a few instances this
|
||||||
|
dnl macro is called twice for the same option with different default
|
||||||
|
dnl values. But we only want it to appear once in the help. We achieve
|
||||||
|
dnl that by making the help string look the same, which is why we need to
|
||||||
|
dnl save the default that was passed in previously.
|
||||||
|
m4_define([_pgac_helpdefault], m4_ifdef([pgac_defined_$1_$2_bool], [m4_defn([pgac_defined_$1_$2_bool])], [$3]))dnl
|
||||||
|
PGAC_ARG([$1], [$2], [m4_if(_pgac_helpdefault, yes, -)], [$4], [$5], [$6],
|
||||||
|
[AC_MSG_ERROR([no argument expected for --$1-$2 option])],
|
||||||
|
[m4_case([$3],
|
||||||
|
yes, [pgac_arg_to_variable([$1], [$2])=yes
|
||||||
|
$5],
|
||||||
|
no, [pgac_arg_to_variable([$1], [$2])=no
|
||||||
|
$6],
|
||||||
|
[m4_fatal([third argument of $0 must be 'yes' or 'no', not '$3'])])])[]dnl
|
||||||
|
m4_define([pgac_defined_$1_$2_bool], [$3])dnl
|
||||||
|
])# PGAC_ARG_BOOL
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_ARG_REQ(TYPE, NAME, HELP-ARGNAME, HELP-STRING-RHS,
|
||||||
|
# [ACTION-IF-GIVEN], [ACTION-IF-NOT-GIVEN])
|
||||||
|
# -------------------------------------------------------
|
||||||
|
# This option will require an argument; "yes" or "no" will not be
|
||||||
|
# accepted. HELP-ARGNAME is a name for the argument for the help output.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_ARG_REQ],
|
||||||
|
[PGAC_ARG([$1], [$2], [=$3], [$4],
|
||||||
|
[AC_MSG_ERROR([argument required for --$1-$2 option])],
|
||||||
|
[AC_MSG_ERROR([argument required for --$1-$2 option])],
|
||||||
|
[$5],
|
||||||
|
[$6])])# PGAC_ARG_REQ
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_ARG_OPTARG(TYPE, NAME, HELP-ARGNAME, HELP-STRING-RHS,
|
||||||
|
# [DEFAULT-ACTION], [ARG-ACTION],
|
||||||
|
# [ACTION-ENABLED], [ACTION-DISABLED])
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# This will create an option that behaves as follows: If omitted, or
|
||||||
|
# called with "no", then set the enable_variable to "no" and do
|
||||||
|
# nothing else. If called with "yes", then execute DEFAULT-ACTION. If
|
||||||
|
# called with argument, set enable_variable to "yes" and execute
|
||||||
|
# ARG-ACTION. Additionally, execute ACTION-ENABLED if we ended up with
|
||||||
|
# "yes" either way, else ACTION-DISABLED.
|
||||||
|
#
|
||||||
|
# The intent is to allow enabling a feature, and optionally pass an
|
||||||
|
# additional piece of information.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_ARG_OPTARG],
|
||||||
|
[PGAC_ARG([$1], [$2], [@<:@=$3@:>@], [$4], [$5], [],
|
||||||
|
[pgac_arg_to_variable([$1], [$2])=yes
|
||||||
|
$6],
|
||||||
|
[pgac_arg_to_variable([$1], [$2])=no])
|
||||||
|
dnl Add this code only if there's a ACTION-ENABLED or ACTION-DISABLED.
|
||||||
|
m4_ifval([$7[]$8],
|
||||||
|
[
|
||||||
|
if test "[$]pgac_arg_to_variable([$1], [$2])" = yes; then
|
||||||
|
m4_default([$7], :)
|
||||||
|
m4_ifval([$8],
|
||||||
|
[else
|
||||||
|
$8
|
||||||
|
])[]dnl
|
||||||
|
fi
|
||||||
|
])[]dnl
|
||||||
|
])# PGAC_ARG_OPTARG
|
|
@ -0,0 +1,527 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# install - install a program, script, or datafile
|
||||||
|
|
||||||
|
scriptversion=2011-11-20.07; # UTC
|
||||||
|
|
||||||
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||||
|
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||||
|
# following copyright and license.
|
||||||
|
#
|
||||||
|
# Copyright (C) 1994 X Consortium
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to
|
||||||
|
# deal in the Software without restriction, including without limitation the
|
||||||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
# sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||||
|
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
#
|
||||||
|
# Except as contained in this notice, the name of the X Consortium shall not
|
||||||
|
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||||
|
# ings in this Software without prior written authorization from the X Consor-
|
||||||
|
# tium.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# FSF changes to this file are in the public domain.
|
||||||
|
#
|
||||||
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
|
# 'make' implicit rules from creating a file called install from it
|
||||||
|
# when there is no Makefile.
|
||||||
|
#
|
||||||
|
# This script is compatible with the BSD install script, but was written
|
||||||
|
# from scratch.
|
||||||
|
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
IFS=" "" $nl"
|
||||||
|
|
||||||
|
# set DOITPROG to echo to test this script
|
||||||
|
|
||||||
|
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||||
|
doit=${DOITPROG-}
|
||||||
|
if test -z "$doit"; then
|
||||||
|
doit_exec=exec
|
||||||
|
else
|
||||||
|
doit_exec=$doit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Put in absolute file names if you don't have them in your path;
|
||||||
|
# or use environment vars.
|
||||||
|
|
||||||
|
chgrpprog=${CHGRPPROG-chgrp}
|
||||||
|
chmodprog=${CHMODPROG-chmod}
|
||||||
|
chownprog=${CHOWNPROG-chown}
|
||||||
|
cmpprog=${CMPPROG-cmp}
|
||||||
|
cpprog=${CPPROG-cp}
|
||||||
|
mkdirprog=${MKDIRPROG-mkdir}
|
||||||
|
mvprog=${MVPROG-mv}
|
||||||
|
rmprog=${RMPROG-rm}
|
||||||
|
stripprog=${STRIPPROG-strip}
|
||||||
|
|
||||||
|
posix_glob='?'
|
||||||
|
initialize_posix_glob='
|
||||||
|
test "$posix_glob" != "?" || {
|
||||||
|
if (set -f) 2>/dev/null; then
|
||||||
|
posix_glob=
|
||||||
|
else
|
||||||
|
posix_glob=:
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
posix_mkdir=
|
||||||
|
|
||||||
|
# Desired mode of installed file.
|
||||||
|
mode=0755
|
||||||
|
|
||||||
|
chgrpcmd=
|
||||||
|
chmodcmd=$chmodprog
|
||||||
|
chowncmd=
|
||||||
|
mvcmd=$mvprog
|
||||||
|
rmcmd="$rmprog -f"
|
||||||
|
stripcmd=
|
||||||
|
|
||||||
|
src=
|
||||||
|
dst=
|
||||||
|
dir_arg=
|
||||||
|
dst_arg=
|
||||||
|
|
||||||
|
copy_on_change=false
|
||||||
|
no_target_directory=
|
||||||
|
|
||||||
|
usage="\
|
||||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||||
|
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||||
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||||
|
or: $0 [OPTION]... -d DIRECTORIES...
|
||||||
|
|
||||||
|
In the 1st form, copy SRCFILE to DSTFILE.
|
||||||
|
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||||
|
In the 4th, create DIRECTORIES.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help display this help and exit.
|
||||||
|
--version display version info and exit.
|
||||||
|
|
||||||
|
-c (ignored)
|
||||||
|
-C install only if different (preserve the last data modification time)
|
||||||
|
-d create directories instead of installing files.
|
||||||
|
-g GROUP $chgrpprog installed files to GROUP.
|
||||||
|
-m MODE $chmodprog installed files to MODE.
|
||||||
|
-o USER $chownprog installed files to USER.
|
||||||
|
-s $stripprog installed files.
|
||||||
|
-t DIRECTORY install into DIRECTORY.
|
||||||
|
-T report an error if DSTFILE is a directory.
|
||||||
|
|
||||||
|
Environment variables override the default commands:
|
||||||
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||||
|
RMPROG STRIPPROG
|
||||||
|
"
|
||||||
|
|
||||||
|
while test $# -ne 0; do
|
||||||
|
case $1 in
|
||||||
|
-c) ;;
|
||||||
|
|
||||||
|
-C) copy_on_change=true;;
|
||||||
|
|
||||||
|
-d) dir_arg=true;;
|
||||||
|
|
||||||
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
--help) echo "$usage"; exit $?;;
|
||||||
|
|
||||||
|
-m) mode=$2
|
||||||
|
case $mode in
|
||||||
|
*' '* | *' '* | *'
|
||||||
|
'* | *'*'* | *'?'* | *'['*)
|
||||||
|
echo "$0: invalid mode: $mode" >&2
|
||||||
|
exit 1;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-o) chowncmd="$chownprog $2"
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-s) stripcmd=$stripprog;;
|
||||||
|
|
||||||
|
-t) dst_arg=$2
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
|
-T) no_target_directory=true;;
|
||||||
|
|
||||||
|
--version) echo "$0 $scriptversion"; exit $?;;
|
||||||
|
|
||||||
|
--) shift
|
||||||
|
break;;
|
||||||
|
|
||||||
|
-*) echo "$0: invalid option: $1" >&2
|
||||||
|
exit 1;;
|
||||||
|
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||||
|
# When -d is used, all remaining arguments are directories to create.
|
||||||
|
# When -t is used, the destination is already specified.
|
||||||
|
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$dst_arg"; then
|
||||||
|
# $@ is not empty: it contains at least $arg.
|
||||||
|
set fnord "$@" "$dst_arg"
|
||||||
|
shift # fnord
|
||||||
|
fi
|
||||||
|
shift # arg
|
||||||
|
dst_arg=$arg
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
echo "$0: no input file specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# It's OK to call 'install-sh -d' without argument.
|
||||||
|
# This can happen when creating conditional directories.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dir_arg"; then
|
||||||
|
do_exit='(exit $ret); exit $ret'
|
||||||
|
trap "ret=129; $do_exit" 1
|
||||||
|
trap "ret=130; $do_exit" 2
|
||||||
|
trap "ret=141; $do_exit" 13
|
||||||
|
trap "ret=143; $do_exit" 15
|
||||||
|
|
||||||
|
# Set umask so as not to create temps with too-generous modes.
|
||||||
|
# However, 'strip' requires both read and write access to temps.
|
||||||
|
case $mode in
|
||||||
|
# Optimize common cases.
|
||||||
|
*644) cp_umask=133;;
|
||||||
|
*755) cp_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw='% 200'
|
||||||
|
fi
|
||||||
|
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||||
|
*)
|
||||||
|
if test -z "$stripcmd"; then
|
||||||
|
u_plus_rw=
|
||||||
|
else
|
||||||
|
u_plus_rw=,u+rw
|
||||||
|
fi
|
||||||
|
cp_umask=$mode$u_plus_rw;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
for src
|
||||||
|
do
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $src in
|
||||||
|
-* | [=\(\)!]) src=./$src;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
dst=$src
|
||||||
|
dstdir=$dst
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
else
|
||||||
|
|
||||||
|
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||||
|
# might cause directories to be created, which would be especially bad
|
||||||
|
# if $src (and thus $dsttmp) contains '*'.
|
||||||
|
if test ! -f "$src" && test ! -d "$src"; then
|
||||||
|
echo "$0: $src does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$dst_arg"; then
|
||||||
|
echo "$0: no destination specified." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dst=$dst_arg
|
||||||
|
|
||||||
|
# If destination is a directory, append the input filename; won't work
|
||||||
|
# if double slashes aren't ignored.
|
||||||
|
if test -d "$dst"; then
|
||||||
|
if test -n "$no_target_directory"; then
|
||||||
|
echo "$0: $dst_arg: Is a directory" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
dstdir=$dst
|
||||||
|
dst=$dstdir/`basename "$src"`
|
||||||
|
dstdir_status=0
|
||||||
|
else
|
||||||
|
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||||
|
dstdir=`
|
||||||
|
(dirname "$dst") 2>/dev/null ||
|
||||||
|
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||||
|
X"$dst" : 'X\(//\)[^/]' \| \
|
||||||
|
X"$dst" : 'X\(//\)$' \| \
|
||||||
|
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||||
|
echo X"$dst" |
|
||||||
|
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\/\)[^/].*/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\/\)$/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
/^X\(\/\).*/{
|
||||||
|
s//\1/
|
||||||
|
q
|
||||||
|
}
|
||||||
|
s/.*/./; q'
|
||||||
|
`
|
||||||
|
|
||||||
|
test -d "$dstdir"
|
||||||
|
dstdir_status=$?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
obsolete_mkdir_used=false
|
||||||
|
|
||||||
|
if test $dstdir_status != 0; then
|
||||||
|
case $posix_mkdir in
|
||||||
|
'')
|
||||||
|
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||||
|
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||||
|
umask=`umask`
|
||||||
|
case $stripcmd.$umask in
|
||||||
|
# Optimize common cases.
|
||||||
|
*[2367][2367]) mkdir_umask=$umask;;
|
||||||
|
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||||
|
|
||||||
|
*[0-7])
|
||||||
|
mkdir_umask=`expr $umask + 22 \
|
||||||
|
- $umask % 100 % 40 + $umask % 20 \
|
||||||
|
- $umask % 10 % 4 + $umask % 2
|
||||||
|
`;;
|
||||||
|
*) mkdir_umask=$umask,go-w;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# With -d, create the new directory with the user-specified mode.
|
||||||
|
# Otherwise, rely on $mkdir_umask.
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
mkdir_mode=-m$mode
|
||||||
|
else
|
||||||
|
mkdir_mode=
|
||||||
|
fi
|
||||||
|
|
||||||
|
posix_mkdir=false
|
||||||
|
case $umask in
|
||||||
|
*[123567][0-7][0-7])
|
||||||
|
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||||
|
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||||
|
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||||
|
|
||||||
|
if (umask $mkdir_umask &&
|
||||||
|
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
if test -z "$dir_arg" || {
|
||||||
|
# Check for POSIX incompatibilities with -m.
|
||||||
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||||
|
# other-writable bit of parent directory when it shouldn't.
|
||||||
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||||
|
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||||
|
case $ls_ld_tmpdir in
|
||||||
|
d????-?r-*) different_mode=700;;
|
||||||
|
d????-?--*) different_mode=755;;
|
||||||
|
*) false;;
|
||||||
|
esac &&
|
||||||
|
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||||
|
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||||
|
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then posix_mkdir=:
|
||||||
|
fi
|
||||||
|
rmdir "$tmpdir/d" "$tmpdir"
|
||||||
|
else
|
||||||
|
# Remove any dirs left behind by ancient mkdir implementations.
|
||||||
|
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||||
|
fi
|
||||||
|
trap '' 0;;
|
||||||
|
esac;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if
|
||||||
|
$posix_mkdir && (
|
||||||
|
umask $mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||||
|
)
|
||||||
|
then :
|
||||||
|
else
|
||||||
|
|
||||||
|
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||||
|
# or it failed possibly due to a race condition. Create the
|
||||||
|
# directory the slow way, step by step, checking for races as we go.
|
||||||
|
|
||||||
|
case $dstdir in
|
||||||
|
/*) prefix='/';;
|
||||||
|
[-=\(\)!]*) prefix='./';;
|
||||||
|
*) prefix='';;
|
||||||
|
esac
|
||||||
|
|
||||||
|
eval "$initialize_posix_glob"
|
||||||
|
|
||||||
|
oIFS=$IFS
|
||||||
|
IFS=/
|
||||||
|
$posix_glob set -f
|
||||||
|
set fnord $dstdir
|
||||||
|
shift
|
||||||
|
$posix_glob set +f
|
||||||
|
IFS=$oIFS
|
||||||
|
|
||||||
|
prefixes=
|
||||||
|
|
||||||
|
for d
|
||||||
|
do
|
||||||
|
test X"$d" = X && continue
|
||||||
|
|
||||||
|
prefix=$prefix$d
|
||||||
|
if test -d "$prefix"; then
|
||||||
|
prefixes=
|
||||||
|
else
|
||||||
|
if $posix_mkdir; then
|
||||||
|
(umask=$mkdir_umask &&
|
||||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
test -d "$prefix" || exit 1
|
||||||
|
else
|
||||||
|
case $prefix in
|
||||||
|
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||||
|
*) qprefix=$prefix;;
|
||||||
|
esac
|
||||||
|
prefixes="$prefixes '$qprefix'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
prefix=$prefix/
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -n "$prefixes"; then
|
||||||
|
# Don't fail if two instances are running concurrently.
|
||||||
|
(umask $mkdir_umask &&
|
||||||
|
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||||
|
test -d "$dstdir" || exit 1
|
||||||
|
obsolete_mkdir_used=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -n "$dir_arg"; then
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||||
|
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||||
|
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||||
|
else
|
||||||
|
|
||||||
|
# Make a couple of temp file names in the proper directory.
|
||||||
|
dsttmp=$dstdir/_inst.$$_
|
||||||
|
rmtmp=$dstdir/_rm.$$_
|
||||||
|
|
||||||
|
# Trap to clean up those temp files at exit.
|
||||||
|
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||||
|
|
||||||
|
# Copy the file name to the temp name.
|
||||||
|
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||||
|
|
||||||
|
# and set any options; do chmod last to preserve setuid bits.
|
||||||
|
#
|
||||||
|
# If any of these fail, we abort the whole thing. If we want to
|
||||||
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
|
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||||
|
#
|
||||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||||
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||||
|
|
||||||
|
# If -C, don't bother to copy if it wouldn't change the file.
|
||||||
|
if $copy_on_change &&
|
||||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||||
|
|
||||||
|
eval "$initialize_posix_glob" &&
|
||||||
|
$posix_glob set -f &&
|
||||||
|
set X $old && old=:$2:$4:$5:$6 &&
|
||||||
|
set X $new && new=:$2:$4:$5:$6 &&
|
||||||
|
$posix_glob set +f &&
|
||||||
|
|
||||||
|
test "$old" = "$new" &&
|
||||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
rm -f "$dsttmp"
|
||||||
|
else
|
||||||
|
# Rename the file to the real destination.
|
||||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||||
|
|
||||||
|
# The rename failed, perhaps because mv can't rename something else
|
||||||
|
# to itself, or perhaps because mv is so ancient that it does not
|
||||||
|
# support -f.
|
||||||
|
{
|
||||||
|
# Now remove or move aside any old file at destination location.
|
||||||
|
# We try this two ways since rm can't unlink itself on some
|
||||||
|
# systems and the destination file might be busy for other
|
||||||
|
# reasons. In this case, the final cleanup might fail but the new
|
||||||
|
# file should still install successfully.
|
||||||
|
{
|
||||||
|
test ! -f "$dst" ||
|
||||||
|
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||||
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||||
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||||
|
} ||
|
||||||
|
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||||
|
(exit 1); exit 1
|
||||||
|
}
|
||||||
|
} &&
|
||||||
|
|
||||||
|
# Now rename the file to the real destination.
|
||||||
|
$doit $mvcmd "$dsttmp" "$dst"
|
||||||
|
}
|
||||||
|
fi || exit 1
|
||||||
|
|
||||||
|
trap '' 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Local variables:
|
||||||
|
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||||
|
# time-stamp-start: "scriptversion="
|
||||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
|
# time-stamp-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
|
@ -0,0 +1,119 @@
|
||||||
|
## libtool.m4 - Configure libtool for the host system. -*-Shell-script-*-
|
||||||
|
## Copyright (C) 1996-1999,2000 Free Software Foundation, Inc.
|
||||||
|
## Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
|
||||||
|
##
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful, but
|
||||||
|
## WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
## General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License
|
||||||
|
## along with this program; if not, write to the Free Software
|
||||||
|
## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
##
|
||||||
|
## As a special exception to the GNU General Public License, if you
|
||||||
|
## distribute this file as part of a program that contains a
|
||||||
|
## configuration script generated by Autoconf, you may include it under
|
||||||
|
## the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
|
# No, PostgreSQL doesn't use libtool (yet), we just borrow stuff from it.
|
||||||
|
# This file was taken on 2000-10-20 from the multi-language branch (since
|
||||||
|
# that is the branch that PostgreSQL would most likely adopt anyway).
|
||||||
|
# --petere
|
||||||
|
|
||||||
|
# ... bunch of stuff removed here ...
|
||||||
|
|
||||||
|
# PGAC_PROG_LD - find the path to the GNU or non-GNU linker
|
||||||
|
AC_DEFUN([PGAC_PROG_LD],
|
||||||
|
[AC_ARG_WITH(gnu-ld,
|
||||||
|
[ --with-gnu-ld assume the C compiler uses GNU ld [[default=no]]],
|
||||||
|
test "$withval" = no || with_gnu_ld=yes, with_gnu_ld=no)
|
||||||
|
AC_REQUIRE([AC_PROG_CC])dnl
|
||||||
|
AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||||
|
dnl ###not for PostgreSQL### AC_REQUIRE([AC_CANONICAL_BUILD])dnl
|
||||||
|
ac_prog=ld
|
||||||
|
if test "$GCC" = yes; then
|
||||||
|
# Check if gcc -print-prog-name=ld gives a path.
|
||||||
|
AC_MSG_CHECKING([for ld used by GCC])
|
||||||
|
case $host in
|
||||||
|
*-*-mingw*)
|
||||||
|
# gcc leaves a trailing carriage return which upsets mingw
|
||||||
|
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
|
||||||
|
*)
|
||||||
|
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
|
||||||
|
esac
|
||||||
|
case "$ac_prog" in
|
||||||
|
# Accept absolute paths.
|
||||||
|
changequote(,)dnl
|
||||||
|
[\\/]* | [A-Za-z]:[\\/]*)
|
||||||
|
re_direlt='/[^/][^/]*/\.\./'
|
||||||
|
changequote([,])dnl
|
||||||
|
# Canonicalize the path of ld
|
||||||
|
ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'`
|
||||||
|
while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do
|
||||||
|
ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"`
|
||||||
|
done
|
||||||
|
test -z "$LD" && LD="$ac_prog"
|
||||||
|
;;
|
||||||
|
"")
|
||||||
|
# If it fails, then pretend we aren't using GCC.
|
||||||
|
ac_prog=ld
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# If it is relative, then search for the first ld in PATH.
|
||||||
|
with_gnu_ld=unknown
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif test "$with_gnu_ld" = yes; then
|
||||||
|
AC_MSG_CHECKING([for GNU ld])
|
||||||
|
else
|
||||||
|
AC_MSG_CHECKING([for non-GNU ld])
|
||||||
|
fi
|
||||||
|
AC_CACHE_VAL(ac_cv_path_LD,
|
||||||
|
[if test -z "$LD"; then
|
||||||
|
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}"
|
||||||
|
for ac_dir in $PATH; do
|
||||||
|
test -z "$ac_dir" && ac_dir=.
|
||||||
|
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
|
||||||
|
ac_cv_path_LD="$ac_dir/$ac_prog"
|
||||||
|
# Check to see if the program is GNU ld. I'd rather use --version,
|
||||||
|
# but apparently some GNU ld's only accept -v.
|
||||||
|
# Break only if it was the GNU/non-GNU ld that we prefer.
|
||||||
|
if "$ac_cv_path_LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then
|
||||||
|
test "$with_gnu_ld" != no && break
|
||||||
|
else
|
||||||
|
test "$with_gnu_ld" != yes && break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS="$ac_save_ifs"
|
||||||
|
else
|
||||||
|
ac_cv_path_LD="$LD" # Let the user override the test with a path.
|
||||||
|
fi])
|
||||||
|
LD="$ac_cv_path_LD"
|
||||||
|
if test -n "$LD"; then
|
||||||
|
AC_MSG_RESULT($LD)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
fi
|
||||||
|
test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
|
||||||
|
PGAC_PROG_LD_GNU
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_PROG_LD_GNU],
|
||||||
|
[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], ac_cv_prog_gnu_ld,
|
||||||
|
[# I'd rather use --version here, but apparently some GNU ld's only accept -v.
|
||||||
|
if $LD -v 2>&1 </dev/null | egrep '(GNU|with BFD)' 1>&5; then
|
||||||
|
ac_cv_prog_gnu_ld=yes
|
||||||
|
else
|
||||||
|
ac_cv_prog_gnu_ld=no
|
||||||
|
fi])
|
||||||
|
with_gnu_ld=$ac_cv_prog_gnu_ld
|
||||||
|
])
|
||||||
|
|
||||||
|
# ... more stuff removed ...
|
|
@ -0,0 +1,120 @@
|
||||||
|
# config/llvm.m4
|
||||||
|
|
||||||
|
# PGAC_LLVM_SUPPORT
|
||||||
|
# -----------------
|
||||||
|
#
|
||||||
|
# Look for the LLVM installation, check that it's new enough, set the
|
||||||
|
# corresponding LLVM_{CFLAGS,CXXFLAGS,BINPATH} and LDFLAGS
|
||||||
|
# variables. Also verify that CLANG is available, to transform C
|
||||||
|
# into bitcode.
|
||||||
|
#
|
||||||
|
AC_DEFUN([PGAC_LLVM_SUPPORT],
|
||||||
|
[
|
||||||
|
AC_REQUIRE([AC_PROG_AWK])
|
||||||
|
|
||||||
|
AC_ARG_VAR(LLVM_CONFIG, [path to llvm-config command])
|
||||||
|
PGAC_PATH_PROGS(LLVM_CONFIG, llvm-config llvm-config-7 llvm-config-6.0 llvm-config-5.0 llvm-config-4.0 llvm-config-3.9)
|
||||||
|
|
||||||
|
# no point continuing if llvm wasn't found
|
||||||
|
if test -z "$LLVM_CONFIG"; then
|
||||||
|
AC_MSG_ERROR([llvm-config not found, but required when compiling --with-llvm, specify with LLVM_CONFIG=])
|
||||||
|
fi
|
||||||
|
# check if detected $LLVM_CONFIG is executable
|
||||||
|
pgac_llvm_version="$($LLVM_CONFIG --version 2> /dev/null || echo no)"
|
||||||
|
if test "x$pgac_llvm_version" = "xno"; then
|
||||||
|
AC_MSG_ERROR([$LLVM_CONFIG does not work])
|
||||||
|
fi
|
||||||
|
# and whether the version is supported
|
||||||
|
if echo $pgac_llvm_version | $AWK -F '.' '{ if ([$]1 >= 4 || ([$]1 == 3 && [$]2 >= 9)) exit 1; else exit 0;}';then
|
||||||
|
AC_MSG_ERROR([$LLVM_CONFIG version is $pgac_llvm_version but at least 3.9 is required])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# need clang to create some bitcode files
|
||||||
|
AC_ARG_VAR(CLANG, [path to clang compiler to generate bitcode])
|
||||||
|
PGAC_PATH_PROGS(CLANG, clang clang-7 clang-6.0 clang-5.0 clang-4.0 clang-3.9)
|
||||||
|
if test -z "$CLANG"; then
|
||||||
|
AC_MSG_ERROR([clang not found, but required when compiling --with-llvm, specify with CLANG=])
|
||||||
|
fi
|
||||||
|
# make sure clang is executable
|
||||||
|
if test "x$($CLANG --version 2> /dev/null || echo no)" = "xno"; then
|
||||||
|
AC_MSG_ERROR([$CLANG does not work])
|
||||||
|
fi
|
||||||
|
# Could check clang version, but it doesn't seem that
|
||||||
|
# important. Systems with a new enough LLVM version are usually
|
||||||
|
# going to have a decent clang version too. It's also not entirely
|
||||||
|
# clear what the minimum version is.
|
||||||
|
|
||||||
|
# Collect compiler flags necessary to build the LLVM dependent
|
||||||
|
# shared library.
|
||||||
|
for pgac_option in `$LLVM_CONFIG --cppflags`; do
|
||||||
|
case $pgac_option in
|
||||||
|
-I*|-D*) LLVM_CPPFLAGS="$pgac_option $LLVM_CPPFLAGS";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
for pgac_option in `$LLVM_CONFIG --ldflags`; do
|
||||||
|
case $pgac_option in
|
||||||
|
-L*) LDFLAGS="$LDFLAGS $pgac_option";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ABI influencing options, standard influencing options
|
||||||
|
for pgac_option in `$LLVM_CONFIG --cxxflags`; do
|
||||||
|
case $pgac_option in
|
||||||
|
-fno-rtti*) LLVM_CXXFLAGS="$LLVM_CXXFLAGS $pgac_option";;
|
||||||
|
-std=*) LLVM_CXXFLAGS="$LLVM_CXXFLAGS $pgac_option";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Look for components we're interested in, collect necessary
|
||||||
|
# libs. As some components are optional, we can't just list all of
|
||||||
|
# them as it'd raise an error.
|
||||||
|
pgac_components='';
|
||||||
|
for pgac_component in `$LLVM_CONFIG --components`; do
|
||||||
|
case $pgac_component in
|
||||||
|
engine) pgac_components="$pgac_components $pgac_component";;
|
||||||
|
debuginfodwarf) pgac_components="$pgac_components $pgac_component";;
|
||||||
|
orcjit) pgac_components="$pgac_components $pgac_component";;
|
||||||
|
passes) pgac_components="$pgac_components $pgac_component";;
|
||||||
|
perfjitevents) pgac_components="$pgac_components $pgac_component";;
|
||||||
|
esac
|
||||||
|
done;
|
||||||
|
|
||||||
|
# And then get the libraries that need to be linked in for the
|
||||||
|
# selected components. They're large libraries, we only want to
|
||||||
|
# link them into the LLVM using shared library.
|
||||||
|
for pgac_option in `$LLVM_CONFIG --libs --system-libs $pgac_components`; do
|
||||||
|
case $pgac_option in
|
||||||
|
-l*) LLVM_LIBS="$LLVM_LIBS $pgac_option";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
LLVM_BINPATH=`$LLVM_CONFIG --bindir`
|
||||||
|
|
||||||
|
dnl LLVM_CONFIG, CLANG are already output via AC_ARG_VAR
|
||||||
|
AC_SUBST(LLVM_LIBS)
|
||||||
|
AC_SUBST(LLVM_CPPFLAGS)
|
||||||
|
AC_SUBST(LLVM_CFLAGS)
|
||||||
|
AC_SUBST(LLVM_CXXFLAGS)
|
||||||
|
AC_SUBST(LLVM_BINPATH)
|
||||||
|
|
||||||
|
])# PGAC_LLVM_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_LLVM_FUNCTIONS
|
||||||
|
# -------------------------
|
||||||
|
#
|
||||||
|
# Check presence of some optional LLVM functions.
|
||||||
|
# (This shouldn't happen until we're ready to run AC_CHECK_DECLS tests;
|
||||||
|
# because PGAC_LLVM_SUPPORT runs very early, it's not an appropriate place.)
|
||||||
|
#
|
||||||
|
AC_DEFUN([PGAC_CHECK_LLVM_FUNCTIONS],
|
||||||
|
[
|
||||||
|
# Check which functionality is present
|
||||||
|
SAVE_CPPFLAGS="$CPPFLAGS"
|
||||||
|
CPPFLAGS="$CPPFLAGS $LLVM_CPPFLAGS"
|
||||||
|
AC_CHECK_DECLS([LLVMOrcGetSymbolAddressIn], [], [], [[#include <llvm-c/OrcBindings.h>]])
|
||||||
|
AC_CHECK_DECLS([LLVMGetHostCPUName, LLVMGetHostCPUFeatures], [], [], [[#include <llvm-c/TargetMachine.h>]])
|
||||||
|
AC_CHECK_DECLS([LLVMCreateGDBRegistrationListener, LLVMCreatePerfJITEventListener], [], [], [[#include <llvm-c/ExecutionEngine.h>]])
|
||||||
|
CPPFLAGS="$SAVE_CPPFLAGS"
|
||||||
|
])# PGAC_CHECK_LLVM_FUNCTIONS
|
|
@ -0,0 +1,54 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
# config/missing
|
||||||
|
|
||||||
|
# This is *not* the GNU `missing' script, although it is similar in
|
||||||
|
# concept. You can call it from the makefiles to get consistent
|
||||||
|
# behavior when certain utility programs are missing.
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
flex|bison)
|
||||||
|
# `missing flex|bison <input> <output>'
|
||||||
|
input=$2
|
||||||
|
output=$3
|
||||||
|
if test -f "$output"; then
|
||||||
|
echo "\
|
||||||
|
***
|
||||||
|
WARNING: \`$1' is missing on your system. You should only need it
|
||||||
|
if you changed the file \`$input'; these changes will not take effect.
|
||||||
|
You can get $1 from a GNU mirror site.
|
||||||
|
***" >&2
|
||||||
|
echo "touch $output"
|
||||||
|
touch "$output"
|
||||||
|
exit 0
|
||||||
|
else # ! test -f $output
|
||||||
|
echo "\
|
||||||
|
***
|
||||||
|
ERROR: \`$1' is missing on your system. It is needed to create the
|
||||||
|
file \`$output'. You can either get $1 from a GNU mirror site
|
||||||
|
or download an official distribution of PostgreSQL, which contains
|
||||||
|
pre-packaged $1 output.
|
||||||
|
***" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
perl)
|
||||||
|
# `missing perl'
|
||||||
|
echo "\
|
||||||
|
***
|
||||||
|
ERROR: Perl is missing on your system. It is needed unless you are building
|
||||||
|
from an unmodified official distribution of PostgreSQL.
|
||||||
|
***" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
# `missing something-or-other'
|
||||||
|
echo "\
|
||||||
|
***
|
||||||
|
ERROR: \`$1' is missing on your system.
|
||||||
|
***" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
|
@ -0,0 +1,114 @@
|
||||||
|
# config/perl.m4
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_PERL
|
||||||
|
# --------------
|
||||||
|
AC_DEFUN([PGAC_PATH_PERL],
|
||||||
|
[PGAC_PATH_PROGS(PERL, perl)
|
||||||
|
AC_ARG_VAR(PERL, [Perl program])dnl
|
||||||
|
|
||||||
|
if test "$PERL"; then
|
||||||
|
pgac_perl_version=`$PERL -v 2>/dev/null | sed -n ['s/This is perl.*v[a-z ]*\([0-9]\.[0-9][0-9.]*\).*$/\1/p']`
|
||||||
|
AC_MSG_NOTICE([using perl $pgac_perl_version])
|
||||||
|
if echo "$pgac_perl_version" | sed ['s/[.a-z_]/ /g'] | \
|
||||||
|
$AWK '{ if ([$]1 == 5 && [$]2 >= 8) exit 1; else exit 0;}'
|
||||||
|
then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** The installed version of Perl, $PERL, is too old to use with PostgreSQL.
|
||||||
|
*** Perl version 5.8 or later is required, but this is $pgac_perl_version.])
|
||||||
|
PERL=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$PERL"; then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** Without Perl you will not be able to build PostgreSQL from Git.
|
||||||
|
*** You can obtain Perl from any CPAN mirror site.
|
||||||
|
*** (If you are using the official distribution of PostgreSQL then you do not
|
||||||
|
*** need to worry about this, because the Perl output is pre-generated.)])
|
||||||
|
fi
|
||||||
|
])# PGAC_PATH_PERL
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_PERL_CONFIG(NAME)
|
||||||
|
# ----------------------------
|
||||||
|
AC_DEFUN([PGAC_CHECK_PERL_CONFIG],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_PERL])
|
||||||
|
AC_MSG_CHECKING([for Perl $1])
|
||||||
|
perl_$1=`$PERL -MConfig -e 'print $Config{$1}'`
|
||||||
|
test "$PORTNAME" = "win32" && perl_$1=`echo $perl_$1 | sed 's,\\\\,/,g'`
|
||||||
|
AC_SUBST(perl_$1)dnl
|
||||||
|
AC_MSG_RESULT([$perl_$1])])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_PERL_CONFIGS(NAMES)
|
||||||
|
# ------------------------------
|
||||||
|
AC_DEFUN([PGAC_CHECK_PERL_CONFIGS],
|
||||||
|
[m4_foreach([pgac_item], [$1], [PGAC_CHECK_PERL_CONFIG(pgac_item)])])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_PERL_EMBED_CCFLAGS
|
||||||
|
# -----------------------------
|
||||||
|
# We selectively extract stuff from $Config{ccflags}. For debugging purposes,
|
||||||
|
# let's have the configure output report the raw ccflags value as well as the
|
||||||
|
# set of flags we chose to adopt. We don't really need anything except -D
|
||||||
|
# switches, and other sorts of compiler switches can actively break things if
|
||||||
|
# Perl was compiled with a different compiler. Moreover, although Perl likes
|
||||||
|
# to put stuff like -D_LARGEFILE_SOURCE and -D_FILE_OFFSET_BITS=64 here, it
|
||||||
|
# would be fatal to try to compile PL/Perl to a different libc ABI than core
|
||||||
|
# Postgres uses. The available information says that most symbols that affect
|
||||||
|
# Perl's own ABI begin with letters, so it's almost sufficient to adopt -D
|
||||||
|
# switches for symbols not beginning with underscore. Some exceptions are the
|
||||||
|
# Windows-specific -D_USE_32BIT_TIME_T and -D__MINGW_USE_VC2005_COMPAT; see
|
||||||
|
# Mkvcbuild.pm for details. We absorb the former when Perl reports it. Perl
|
||||||
|
# never reports the latter, and we don't attempt to deduce when it's needed.
|
||||||
|
# Consequently, we don't support using MinGW to link to MSVC-built Perl. As
|
||||||
|
# of 2017, all supported ActivePerl and Strawberry Perl are MinGW-built. If
|
||||||
|
# that changes or an MSVC-built Perl distribution becomes prominent, we can
|
||||||
|
# revisit this limitation.
|
||||||
|
AC_DEFUN([PGAC_CHECK_PERL_EMBED_CCFLAGS],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_PERL])
|
||||||
|
AC_MSG_CHECKING([for CFLAGS recommended by Perl])
|
||||||
|
perl_ccflags=`$PERL -MConfig -e ['print $Config{ccflags}']`
|
||||||
|
AC_MSG_RESULT([$perl_ccflags])
|
||||||
|
AC_MSG_CHECKING([for CFLAGS to compile embedded Perl])
|
||||||
|
perl_embed_ccflags=`$PERL -MConfig -e ['foreach $f (split(" ",$Config{ccflags})) {print $f, " " if ($f =~ /^-D[^_]/ || $f =~ /^-D_USE_32BIT_TIME_T/)}']`
|
||||||
|
AC_SUBST(perl_embed_ccflags)dnl
|
||||||
|
AC_MSG_RESULT([$perl_embed_ccflags])
|
||||||
|
])# PGAC_CHECK_PERL_EMBED_CCFLAGS
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_PERL_EMBED_LDFLAGS
|
||||||
|
# -----------------------------
|
||||||
|
# We are after Embed's ldopts, but without the subset mentioned in
|
||||||
|
# Config's ccdlflags; and also without any -arch flags, which recent
|
||||||
|
# Apple releases put in unhelpfully. (If you want a multiarch build
|
||||||
|
# you'd better be specifying it in more places than plperl's final link.)
|
||||||
|
AC_DEFUN([PGAC_CHECK_PERL_EMBED_LDFLAGS],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_PERL])
|
||||||
|
AC_MSG_CHECKING(for flags to link embedded Perl)
|
||||||
|
if test "$PORTNAME" = "win32" ; then
|
||||||
|
perl_lib=`basename $perl_archlibexp/CORE/perl[[5-9]]*.lib .lib`
|
||||||
|
if test -e "$perl_archlibexp/CORE/$perl_lib.lib"; then
|
||||||
|
perl_embed_ldflags="-L$perl_archlibexp/CORE -l$perl_lib"
|
||||||
|
else
|
||||||
|
perl_lib=`basename $perl_archlibexp/CORE/libperl[[5-9]]*.a .a | sed 's/^lib//'`
|
||||||
|
if test -e "$perl_archlibexp/CORE/lib$perl_lib.a"; then
|
||||||
|
perl_embed_ldflags="-L$perl_archlibexp/CORE -l$perl_lib"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
pgac_tmp1=`$PERL -MExtUtils::Embed -e ldopts`
|
||||||
|
pgac_tmp2=`$PERL -MConfig -e 'print $Config{ccdlflags}'`
|
||||||
|
perl_embed_ldflags=`echo X"$pgac_tmp1" | sed -e "s/^X//" -e "s%$pgac_tmp2%%" -e ["s/ -arch [-a-zA-Z0-9_]*//g"]`
|
||||||
|
fi
|
||||||
|
AC_SUBST(perl_embed_ldflags)dnl
|
||||||
|
if test -z "$perl_embed_ldflags" ; then
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR([could not determine flags for linking embedded Perl.
|
||||||
|
This probably means that ExtUtils::Embed or ExtUtils::MakeMaker is not
|
||||||
|
installed.])
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([$perl_embed_ldflags])
|
||||||
|
fi
|
||||||
|
])# PGAC_CHECK_PERL_EMBED_LDFLAGS
|
|
@ -0,0 +1,275 @@
|
||||||
|
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||||
|
# serial 12 (pkg-config-0.29.2)
|
||||||
|
|
||||||
|
dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||||
|
dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
|
||||||
|
dnl
|
||||||
|
dnl This program is free software; you can redistribute it and/or modify
|
||||||
|
dnl it under the terms of the GNU General Public License as published by
|
||||||
|
dnl the Free Software Foundation; either version 2 of the License, or
|
||||||
|
dnl (at your option) any later version.
|
||||||
|
dnl
|
||||||
|
dnl This program is distributed in the hope that it will be useful, but
|
||||||
|
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
dnl General Public License for more details.
|
||||||
|
dnl
|
||||||
|
dnl You should have received a copy of the GNU General Public License
|
||||||
|
dnl along with this program; if not, write to the Free Software
|
||||||
|
dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
||||||
|
dnl 02111-1307, USA.
|
||||||
|
dnl
|
||||||
|
dnl As a special exception to the GNU General Public License, if you
|
||||||
|
dnl distribute this file as part of a program that contains a
|
||||||
|
dnl configuration script generated by Autoconf, you may include it under
|
||||||
|
dnl the same distribution terms that you use for the rest of that
|
||||||
|
dnl program.
|
||||||
|
|
||||||
|
dnl PKG_PREREQ(MIN-VERSION)
|
||||||
|
dnl -----------------------
|
||||||
|
dnl Since: 0.29
|
||||||
|
dnl
|
||||||
|
dnl Verify that the version of the pkg-config macros are at least
|
||||||
|
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
|
||||||
|
dnl installed version of pkg-config, this checks the developer's version
|
||||||
|
dnl of pkg.m4 when generating configure.
|
||||||
|
dnl
|
||||||
|
dnl To ensure that this macro is defined, also add:
|
||||||
|
dnl m4_ifndef([PKG_PREREQ],
|
||||||
|
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
|
||||||
|
dnl
|
||||||
|
dnl See the "Since" comment for each macro you use to see what version
|
||||||
|
dnl of the macros you require.
|
||||||
|
m4_defun([PKG_PREREQ],
|
||||||
|
[m4_define([PKG_MACROS_VERSION], [0.29.2])
|
||||||
|
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
|
||||||
|
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
|
||||||
|
])dnl PKG_PREREQ
|
||||||
|
|
||||||
|
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||||
|
dnl ----------------------------------
|
||||||
|
dnl Since: 0.16
|
||||||
|
dnl
|
||||||
|
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
|
||||||
|
dnl first found in the path. Checks that the version of pkg-config found
|
||||||
|
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
|
||||||
|
dnl used since that's the first version where most current features of
|
||||||
|
dnl pkg-config existed.
|
||||||
|
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||||
|
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||||
|
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
|
||||||
|
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
|
||||||
|
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
|
||||||
|
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
|
||||||
|
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
|
||||||
|
|
||||||
|
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||||
|
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||||
|
fi
|
||||||
|
if test -n "$PKG_CONFIG"; then
|
||||||
|
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||||
|
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||||
|
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||||
|
AC_MSG_RESULT([yes])
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
PKG_CONFIG=""
|
||||||
|
fi
|
||||||
|
fi[]dnl
|
||||||
|
])dnl PKG_PROG_PKG_CONFIG
|
||||||
|
|
||||||
|
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||||
|
dnl -------------------------------------------------------------------
|
||||||
|
dnl Since: 0.18
|
||||||
|
dnl
|
||||||
|
dnl Check to see whether a particular set of modules exists. Similar to
|
||||||
|
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||||
|
dnl
|
||||||
|
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||||
|
dnl only at the first occurence in configure.ac, so if the first place
|
||||||
|
dnl it's called might be skipped (such as if it is within an "if", you
|
||||||
|
dnl have to call PKG_CHECK_EXISTS manually
|
||||||
|
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||||
|
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||||
|
if test -n "$PKG_CONFIG" && \
|
||||||
|
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||||
|
m4_default([$2], [:])
|
||||||
|
m4_ifvaln([$3], [else
|
||||||
|
$3])dnl
|
||||||
|
fi])
|
||||||
|
|
||||||
|
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||||
|
dnl ---------------------------------------------
|
||||||
|
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
|
||||||
|
dnl pkg_failed based on the result.
|
||||||
|
m4_define([_PKG_CONFIG],
|
||||||
|
[if test -n "$$1"; then
|
||||||
|
pkg_cv_[]$1="$$1"
|
||||||
|
elif test -n "$PKG_CONFIG"; then
|
||||||
|
PKG_CHECK_EXISTS([$3],
|
||||||
|
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
|
||||||
|
test "x$?" != "x0" && pkg_failed=yes ],
|
||||||
|
[pkg_failed=yes])
|
||||||
|
else
|
||||||
|
pkg_failed=untried
|
||||||
|
fi[]dnl
|
||||||
|
])dnl _PKG_CONFIG
|
||||||
|
|
||||||
|
dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||||
|
dnl ---------------------------
|
||||||
|
dnl Internal check to see if pkg-config supports short errors.
|
||||||
|
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||||
|
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||||
|
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||||
|
_pkg_short_errors_supported=yes
|
||||||
|
else
|
||||||
|
_pkg_short_errors_supported=no
|
||||||
|
fi[]dnl
|
||||||
|
])dnl _PKG_SHORT_ERRORS_SUPPORTED
|
||||||
|
|
||||||
|
|
||||||
|
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||||
|
dnl [ACTION-IF-NOT-FOUND])
|
||||||
|
dnl --------------------------------------------------------------
|
||||||
|
dnl Since: 0.4.0
|
||||||
|
dnl
|
||||||
|
dnl Note that if there is a possibility the first call to
|
||||||
|
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||||
|
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||||
|
AC_DEFUN([PKG_CHECK_MODULES],
|
||||||
|
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||||
|
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||||
|
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||||
|
|
||||||
|
pkg_failed=no
|
||||||
|
AC_MSG_CHECKING([for $2])
|
||||||
|
|
||||||
|
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||||
|
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||||
|
|
||||||
|
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||||
|
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||||
|
See the pkg-config man page for more details.])
|
||||||
|
|
||||||
|
if test $pkg_failed = yes; then
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
_PKG_SHORT_ERRORS_SUPPORTED
|
||||||
|
if test $_pkg_short_errors_supported = yes; then
|
||||||
|
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
|
||||||
|
else
|
||||||
|
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
|
||||||
|
fi
|
||||||
|
# Put the nasty error message in config.log where it belongs
|
||||||
|
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||||
|
|
||||||
|
m4_default([$4], [AC_MSG_ERROR(
|
||||||
|
[Package requirements ($2) were not met:
|
||||||
|
|
||||||
|
$$1_PKG_ERRORS
|
||||||
|
|
||||||
|
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||||
|
installed software in a non-standard prefix.
|
||||||
|
|
||||||
|
_PKG_TEXT])[]dnl
|
||||||
|
])
|
||||||
|
elif test $pkg_failed = untried; then
|
||||||
|
AC_MSG_RESULT([no])
|
||||||
|
m4_default([$4], [AC_MSG_FAILURE(
|
||||||
|
[The pkg-config script could not be found or is too old. Make sure it
|
||||||
|
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||||
|
path to pkg-config.
|
||||||
|
|
||||||
|
_PKG_TEXT
|
||||||
|
|
||||||
|
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
|
||||||
|
])
|
||||||
|
else
|
||||||
|
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||||
|
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||||
|
AC_MSG_RESULT([yes])
|
||||||
|
$3
|
||||||
|
fi[]dnl
|
||||||
|
])dnl PKG_CHECK_MODULES
|
||||||
|
|
||||||
|
|
||||||
|
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||||
|
dnl [ACTION-IF-NOT-FOUND])
|
||||||
|
dnl ---------------------------------------------------------------------
|
||||||
|
dnl Since: 0.29
|
||||||
|
dnl
|
||||||
|
dnl Checks for existence of MODULES and gathers its build flags with
|
||||||
|
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
|
||||||
|
dnl and VARIABLE-PREFIX_LIBS from --libs.
|
||||||
|
dnl
|
||||||
|
dnl Note that if there is a possibility the first call to
|
||||||
|
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
|
||||||
|
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
|
||||||
|
dnl configure.ac.
|
||||||
|
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
|
||||||
|
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||||
|
_save_PKG_CONFIG=$PKG_CONFIG
|
||||||
|
PKG_CONFIG="$PKG_CONFIG --static"
|
||||||
|
PKG_CHECK_MODULES($@)
|
||||||
|
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
|
||||||
|
])dnl PKG_CHECK_MODULES_STATIC
|
||||||
|
|
||||||
|
|
||||||
|
dnl PKG_INSTALLDIR([DIRECTORY])
|
||||||
|
dnl -------------------------
|
||||||
|
dnl Since: 0.27
|
||||||
|
dnl
|
||||||
|
dnl Substitutes the variable pkgconfigdir as the location where a module
|
||||||
|
dnl should install pkg-config .pc files. By default the directory is
|
||||||
|
dnl $libdir/pkgconfig, but the default can be changed by passing
|
||||||
|
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
|
||||||
|
dnl parameter.
|
||||||
|
AC_DEFUN([PKG_INSTALLDIR],
|
||||||
|
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
|
||||||
|
m4_pushdef([pkg_description],
|
||||||
|
[pkg-config installation directory @<:@]pkg_default[@:>@])
|
||||||
|
AC_ARG_WITH([pkgconfigdir],
|
||||||
|
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
|
||||||
|
[with_pkgconfigdir=]pkg_default)
|
||||||
|
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
|
||||||
|
m4_popdef([pkg_default])
|
||||||
|
m4_popdef([pkg_description])
|
||||||
|
])dnl PKG_INSTALLDIR
|
||||||
|
|
||||||
|
|
||||||
|
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
|
||||||
|
dnl --------------------------------
|
||||||
|
dnl Since: 0.27
|
||||||
|
dnl
|
||||||
|
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
|
||||||
|
dnl module should install arch-independent pkg-config .pc files. By
|
||||||
|
dnl default the directory is $datadir/pkgconfig, but the default can be
|
||||||
|
dnl changed by passing DIRECTORY. The user can override through the
|
||||||
|
dnl --with-noarch-pkgconfigdir parameter.
|
||||||
|
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
|
||||||
|
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
|
||||||
|
m4_pushdef([pkg_description],
|
||||||
|
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
|
||||||
|
AC_ARG_WITH([noarch-pkgconfigdir],
|
||||||
|
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
|
||||||
|
[with_noarch_pkgconfigdir=]pkg_default)
|
||||||
|
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
|
||||||
|
m4_popdef([pkg_default])
|
||||||
|
m4_popdef([pkg_description])
|
||||||
|
])dnl PKG_NOARCH_INSTALLDIR
|
||||||
|
|
||||||
|
|
||||||
|
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
|
||||||
|
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||||
|
dnl -------------------------------------------
|
||||||
|
dnl Since: 0.28
|
||||||
|
dnl
|
||||||
|
dnl Retrieves the value of the pkg-config variable for the given module.
|
||||||
|
AC_DEFUN([PKG_CHECK_VAR],
|
||||||
|
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||||
|
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
|
||||||
|
|
||||||
|
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
|
||||||
|
AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||||
|
|
||||||
|
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||||
|
])dnl PKG_CHECK_VAR
|
|
@ -0,0 +1,45 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
# This script prepares a PostgreSQL build tree. It is intended
|
||||||
|
# to be run by the configure script.
|
||||||
|
|
||||||
|
me=`basename $0`
|
||||||
|
|
||||||
|
help="\
|
||||||
|
Usage: $me sourcetree [buildtree]"
|
||||||
|
|
||||||
|
if test -z "$1"; then
|
||||||
|
echo "$help" 1>&2
|
||||||
|
exit 1
|
||||||
|
elif test x"$1" = x"--help"; then
|
||||||
|
echo "$help"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset CDPATH
|
||||||
|
|
||||||
|
sourcetree=`cd $1 && pwd`
|
||||||
|
|
||||||
|
buildtree=`cd ${2:-'.'} && pwd`
|
||||||
|
|
||||||
|
# We must not auto-create the subdirectories holding built documentation.
|
||||||
|
# If we did, it would interfere with installation of prebuilt docs from
|
||||||
|
# the source tree, if a VPATH build is done from a distribution tarball.
|
||||||
|
# See bug #5595.
|
||||||
|
for item in `find "$sourcetree" -type d \( \( -name CVS -prune \) -o \( -name .git -prune \) -o -print \) | grep -v "$sourcetree/doc/src/sgml/\+"`; do
|
||||||
|
subdir=`expr "$item" : "$sourcetree\(.*\)"`
|
||||||
|
if test ! -d "$buildtree/$subdir"; then
|
||||||
|
mkdir -p "$buildtree/$subdir" || exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for item in `find "$sourcetree" -name Makefile -print -o -name GNUmakefile -print | grep -v "$sourcetree/doc/src/sgml/images/"`; do
|
||||||
|
filename=`expr "$item" : "$sourcetree\(.*\)"`
|
||||||
|
if test ! -f "${item}.in"; then
|
||||||
|
if cmp "$item" "$buildtree/$filename" >/dev/null 2>&1; then : ; else
|
||||||
|
ln -fs "$item" "$buildtree/$filename" || exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
|
@ -0,0 +1,294 @@
|
||||||
|
# config/programs.m4
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_PROGS
|
||||||
|
# ---------------
|
||||||
|
# This wrapper for AC_PATH_PROGS behaves like that macro except when
|
||||||
|
# VARIABLE is already set; in that case we just accept the value verbatim.
|
||||||
|
# (AC_PATH_PROGS would accept it only if it looks like an absolute path.)
|
||||||
|
# A desirable future improvement would be to convert a non-absolute-path
|
||||||
|
# input into absolute form.
|
||||||
|
AC_DEFUN([PGAC_PATH_PROGS],
|
||||||
|
[if test -z "$$1"; then
|
||||||
|
AC_PATH_PROGS($@)
|
||||||
|
else
|
||||||
|
# Report the value of $1 in configure's output in all cases.
|
||||||
|
AC_MSG_CHECKING([for $1])
|
||||||
|
AC_MSG_RESULT([$$1])
|
||||||
|
fi
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_BISON
|
||||||
|
# ---------------
|
||||||
|
# Look for Bison, set the output variable BISON to its path if found.
|
||||||
|
# Reject versions before 1.875 (they have bugs or capacity limits).
|
||||||
|
# Note we do not accept other implementations of yacc.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_PATH_BISON],
|
||||||
|
[PGAC_PATH_PROGS(BISON, bison)
|
||||||
|
|
||||||
|
if test "$BISON"; then
|
||||||
|
pgac_bison_version=`$BISON --version 2>/dev/null | sed q`
|
||||||
|
AC_MSG_NOTICE([using $pgac_bison_version])
|
||||||
|
if echo "$pgac_bison_version" | $AWK '{ if ([$]4 < 1.875) exit 0; else exit 1;}'
|
||||||
|
then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** The installed version of Bison, $BISON, is too old to use with PostgreSQL.
|
||||||
|
*** Bison version 1.875 or later is required, but this is $pgac_bison_version.])
|
||||||
|
BISON=""
|
||||||
|
fi
|
||||||
|
# Bison >=3.0 issues warnings about %name-prefix="base_yy", instead
|
||||||
|
# of the now preferred %name-prefix "base_yy", but the latter
|
||||||
|
# doesn't work with Bison 2.3 or less. So for now we silence the
|
||||||
|
# deprecation warnings.
|
||||||
|
if echo "$pgac_bison_version" | $AWK '{ if ([$]4 >= 3) exit 0; else exit 1;}'
|
||||||
|
then
|
||||||
|
BISONFLAGS="$BISONFLAGS -Wno-deprecated"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$BISON"; then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** Without Bison you will not be able to build PostgreSQL from Git nor
|
||||||
|
*** change any of the parser definition files. You can obtain Bison from
|
||||||
|
*** a GNU mirror site. (If you are using the official distribution of
|
||||||
|
*** PostgreSQL then you do not need to worry about this, because the Bison
|
||||||
|
*** output is pre-generated.)])
|
||||||
|
fi
|
||||||
|
dnl We don't need AC_SUBST(BISON) because PGAC_PATH_PROGS did it
|
||||||
|
AC_SUBST(BISONFLAGS)
|
||||||
|
])# PGAC_PATH_BISON
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_FLEX
|
||||||
|
# --------------
|
||||||
|
# Look for Flex, set the output variable FLEX to its path if found.
|
||||||
|
# Reject versions before 2.5.31, as we need a reasonably non-buggy reentrant
|
||||||
|
# scanner. (Note: the well-publicized security problem in 2.5.31 does not
|
||||||
|
# affect Postgres, and there are still distros shipping patched 2.5.31,
|
||||||
|
# so allow it.) Also find Flex if its installed under `lex', but do not
|
||||||
|
# accept other Lex programs.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_PATH_FLEX],
|
||||||
|
[AC_CACHE_CHECK([for flex], pgac_cv_path_flex,
|
||||||
|
[# Let the user override the test
|
||||||
|
if test -n "$FLEX"; then
|
||||||
|
pgac_cv_path_flex=$FLEX
|
||||||
|
else
|
||||||
|
pgac_save_IFS=$IFS
|
||||||
|
IFS=$PATH_SEPARATOR
|
||||||
|
for pgac_dir in $PATH; do
|
||||||
|
IFS=$pgac_save_IFS
|
||||||
|
if test -z "$pgac_dir" || test x"$pgac_dir" = x"."; then
|
||||||
|
pgac_dir=`pwd`
|
||||||
|
fi
|
||||||
|
for pgac_prog in flex lex; do
|
||||||
|
pgac_candidate="$pgac_dir/$pgac_prog"
|
||||||
|
if test -f "$pgac_candidate" \
|
||||||
|
&& $pgac_candidate --version </dev/null >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
echo '%%' > conftest.l
|
||||||
|
if $pgac_candidate -t conftest.l 2>/dev/null | grep FLEX_SCANNER >/dev/null 2>&1; then
|
||||||
|
pgac_flex_version=`$pgac_candidate --version 2>/dev/null`
|
||||||
|
if echo "$pgac_flex_version" | sed ['s/[.a-z]/ /g'] | $AWK '{ if ([$]1 == 2 && ([$]2 > 5 || ([$]2 == 5 && [$]3 >= 31))) exit 0; else exit 1;}'
|
||||||
|
then
|
||||||
|
pgac_cv_path_flex=$pgac_candidate
|
||||||
|
break 2
|
||||||
|
else
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** The installed version of Flex, $pgac_candidate, is too old to use with PostgreSQL.
|
||||||
|
*** Flex version 2.5.31 or later is required, but this is $pgac_flex_version.])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
rm -f conftest.l lex.yy.c
|
||||||
|
: ${pgac_cv_path_flex=no}
|
||||||
|
fi
|
||||||
|
])[]dnl AC_CACHE_CHECK
|
||||||
|
|
||||||
|
if test x"$pgac_cv_path_flex" = x"no"; then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** Without Flex you will not be able to build PostgreSQL from Git nor
|
||||||
|
*** change any of the scanner definition files. You can obtain Flex from
|
||||||
|
*** a GNU mirror site. (If you are using the official distribution of
|
||||||
|
*** PostgreSQL then you do not need to worry about this because the Flex
|
||||||
|
*** output is pre-generated.)])
|
||||||
|
|
||||||
|
FLEX=
|
||||||
|
else
|
||||||
|
FLEX=$pgac_cv_path_flex
|
||||||
|
pgac_flex_version=`$FLEX --version 2>/dev/null`
|
||||||
|
AC_MSG_NOTICE([using $pgac_flex_version])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST(FLEX)
|
||||||
|
AC_SUBST(FLEXFLAGS)
|
||||||
|
])# PGAC_PATH_FLEX
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_LDAP_SAFE
|
||||||
|
# --------------
|
||||||
|
# PostgreSQL sometimes loads libldap_r and plain libldap into the same
|
||||||
|
# process. Check for OpenLDAP versions known not to tolerate doing so; assume
|
||||||
|
# non-OpenLDAP implementations are safe. The dblink test suite exercises the
|
||||||
|
# hazardous interaction directly.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_LDAP_SAFE],
|
||||||
|
[AC_CACHE_CHECK([for compatible LDAP implementation], [pgac_cv_ldap_safe],
|
||||||
|
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||||
|
[#include <ldap.h>
|
||||||
|
#if !defined(LDAP_VENDOR_VERSION) || \
|
||||||
|
(defined(LDAP_API_FEATURE_X_OPENLDAP) && \
|
||||||
|
LDAP_VENDOR_VERSION >= 20424 && LDAP_VENDOR_VERSION <= 20431)
|
||||||
|
choke me
|
||||||
|
#endif], [])],
|
||||||
|
[pgac_cv_ldap_safe=yes],
|
||||||
|
[pgac_cv_ldap_safe=no])])
|
||||||
|
|
||||||
|
if test "$pgac_cv_ldap_safe" != yes; then
|
||||||
|
AC_MSG_WARN([
|
||||||
|
*** With OpenLDAP versions 2.4.24 through 2.4.31, inclusive, each backend
|
||||||
|
*** process that loads libpq (via WAL receiver, dblink, or postgres_fdw) and
|
||||||
|
*** also uses LDAP will crash on exit.])
|
||||||
|
fi])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_READLINE
|
||||||
|
# -------------------
|
||||||
|
# Check for the readline library and dependent libraries, either
|
||||||
|
# termcap or curses. Also try libedit, since NetBSD's is compatible.
|
||||||
|
# Add the required flags to LIBS, define HAVE_LIBREADLINE.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_CHECK_READLINE],
|
||||||
|
[AC_REQUIRE([AC_CANONICAL_HOST])
|
||||||
|
|
||||||
|
AC_CACHE_CHECK([for library containing readline], [pgac_cv_check_readline],
|
||||||
|
[pgac_cv_check_readline=no
|
||||||
|
pgac_save_LIBS=$LIBS
|
||||||
|
if test x"$with_libedit_preferred" != x"yes"
|
||||||
|
then READLINE_ORDER="-lreadline -ledit"
|
||||||
|
else READLINE_ORDER="-ledit -lreadline"
|
||||||
|
fi
|
||||||
|
for pgac_rllib in $READLINE_ORDER ; do
|
||||||
|
for pgac_lib in "" " -ltermcap" " -lncurses" " -lcurses" ; do
|
||||||
|
LIBS="${pgac_rllib}${pgac_lib} $pgac_save_LIBS"
|
||||||
|
AC_TRY_LINK_FUNC([readline], [[
|
||||||
|
# Older NetBSD, OpenBSD, and Irix have a broken linker that does not
|
||||||
|
# recognize dependent libraries; assume curses is needed if we didn't
|
||||||
|
# find any dependency.
|
||||||
|
case $host_os in
|
||||||
|
netbsd* | openbsd* | irix*)
|
||||||
|
if test x"$pgac_lib" = x"" ; then
|
||||||
|
pgac_lib=" -lcurses"
|
||||||
|
fi ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
pgac_cv_check_readline="${pgac_rllib}${pgac_lib}"
|
||||||
|
break
|
||||||
|
]])
|
||||||
|
done
|
||||||
|
if test "$pgac_cv_check_readline" != no ; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
LIBS=$pgac_save_LIBS
|
||||||
|
])[]dnl AC_CACHE_CHECK
|
||||||
|
|
||||||
|
if test "$pgac_cv_check_readline" != no ; then
|
||||||
|
LIBS="$pgac_cv_check_readline $LIBS"
|
||||||
|
AC_DEFINE(HAVE_LIBREADLINE, 1, [Define if you have a function readline library])
|
||||||
|
fi
|
||||||
|
|
||||||
|
])# PGAC_CHECK_READLINE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
|
||||||
|
# ---------------------------------------
|
||||||
|
# Readline versions < 2.1 don't have rl_completion_append_character
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER],
|
||||||
|
[AC_CACHE_CHECK([for rl_completion_append_character], pgac_cv_var_rl_completion_append_character,
|
||||||
|
[AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>
|
||||||
|
#ifdef HAVE_READLINE_READLINE_H
|
||||||
|
# include <readline/readline.h>
|
||||||
|
#elif defined(HAVE_READLINE_H)
|
||||||
|
# include <readline.h>
|
||||||
|
#endif
|
||||||
|
],
|
||||||
|
[rl_completion_append_character = 'x';])],
|
||||||
|
[pgac_cv_var_rl_completion_append_character=yes],
|
||||||
|
[pgac_cv_var_rl_completion_append_character=no])])
|
||||||
|
if test x"$pgac_cv_var_rl_completion_append_character" = x"yes"; then
|
||||||
|
AC_DEFINE(HAVE_RL_COMPLETION_APPEND_CHARACTER, 1,
|
||||||
|
[Define to 1 if you have the global variable 'rl_completion_append_character'.])
|
||||||
|
fi])# PGAC_VAR_RL_COMPLETION_APPEND_CHARACTER
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_GETTEXT
|
||||||
|
# ------------------
|
||||||
|
# We check for bind_textdomain_codeset() not just gettext(). GNU gettext
|
||||||
|
# before 0.10.36 does not have that function, and is generally too incomplete
|
||||||
|
# to be usable.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_CHECK_GETTEXT],
|
||||||
|
[
|
||||||
|
AC_SEARCH_LIBS(bind_textdomain_codeset, intl, [],
|
||||||
|
[AC_MSG_ERROR([a gettext implementation is required for NLS])])
|
||||||
|
AC_CHECK_HEADER([libintl.h], [],
|
||||||
|
[AC_MSG_ERROR([header file <libintl.h> is required for NLS])])
|
||||||
|
PGAC_PATH_PROGS(MSGFMT, msgfmt)
|
||||||
|
AC_ARG_VAR(MSGFMT, [msgfmt program for NLS])dnl
|
||||||
|
if test -z "$MSGFMT"; then
|
||||||
|
AC_MSG_ERROR([msgfmt is required for NLS])
|
||||||
|
fi
|
||||||
|
AC_CACHE_CHECK([for msgfmt flags], pgac_cv_msgfmt_flags,
|
||||||
|
[if test x"$MSGFMT" != x"" && "$MSGFMT" --version 2>&1 | grep "GNU" >/dev/null; then
|
||||||
|
pgac_cv_msgfmt_flags=-c
|
||||||
|
fi])
|
||||||
|
AC_SUBST(MSGFMT_FLAGS, $pgac_cv_msgfmt_flags)
|
||||||
|
PGAC_PATH_PROGS(MSGMERGE, msgmerge)
|
||||||
|
PGAC_PATH_PROGS(XGETTEXT, xgettext)
|
||||||
|
])# PGAC_CHECK_GETTEXT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_STRIP
|
||||||
|
# ----------------
|
||||||
|
# Check for a 'strip' program, and figure out if that program can
|
||||||
|
# strip libraries.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_CHECK_STRIP],
|
||||||
|
[
|
||||||
|
AC_CHECK_TOOL(STRIP, strip, :)
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([whether it is possible to strip libraries])
|
||||||
|
if test x"$STRIP" != x"" && "$STRIP" -V 2>&1 | grep "GNU strip" >/dev/null; then
|
||||||
|
STRIP_STATIC_LIB="$STRIP -x"
|
||||||
|
STRIP_SHARED_LIB="$STRIP --strip-unneeded"
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
case $host_os in
|
||||||
|
darwin*)
|
||||||
|
STRIP="$STRIP -x"
|
||||||
|
STRIP_STATIC_LIB=$STRIP
|
||||||
|
STRIP_SHARED_LIB=$STRIP
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
STRIP_STATIC_LIB=:
|
||||||
|
STRIP_SHARED_LIB=:
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
AC_SUBST(STRIP_STATIC_LIB)
|
||||||
|
AC_SUBST(STRIP_SHARED_LIB)
|
||||||
|
])# PGAC_CHECK_STRIP
|
|
@ -0,0 +1,184 @@
|
||||||
|
#
|
||||||
|
# Autoconf macros for configuring the build of Python extension modules
|
||||||
|
#
|
||||||
|
# config/python.m4
|
||||||
|
#
|
||||||
|
|
||||||
|
# PGAC_PATH_PYTHON
|
||||||
|
# ----------------
|
||||||
|
# Look for Python and set the output variable 'PYTHON' if found,
|
||||||
|
# fail otherwise.
|
||||||
|
#
|
||||||
|
# As the Python 3 transition happens and PEP 394 isn't updated, we
|
||||||
|
# need to cater to systems that don't have unversioned "python" by
|
||||||
|
# default. Some systems ship with "python3" by default and perhaps
|
||||||
|
# have "python" in an optional package. Some systems only have
|
||||||
|
# "python2" and "python3", in which case it's reasonable to prefer the
|
||||||
|
# newer version.
|
||||||
|
AC_DEFUN([PGAC_PATH_PYTHON],
|
||||||
|
[PGAC_PATH_PROGS(PYTHON, [python python3 python2])
|
||||||
|
AC_ARG_VAR(PYTHON, [Python program])dnl
|
||||||
|
if test x"$PYTHON" = x""; then
|
||||||
|
AC_MSG_ERROR([Python not found])
|
||||||
|
fi
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# _PGAC_CHECK_PYTHON_DIRS
|
||||||
|
# -----------------------
|
||||||
|
# Determine the name of various directories of a given Python installation,
|
||||||
|
# as well as the Python version.
|
||||||
|
AC_DEFUN([_PGAC_CHECK_PYTHON_DIRS],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_PYTHON])
|
||||||
|
python_fullversion=`${PYTHON} -c "import sys; print(sys.version)" | sed q`
|
||||||
|
AC_MSG_NOTICE([using python $python_fullversion])
|
||||||
|
# python_fullversion is typically n.n.n plus some trailing junk
|
||||||
|
python_majorversion=`echo "$python_fullversion" | sed '[s/^\([0-9]*\).*/\1/]'`
|
||||||
|
python_minorversion=`echo "$python_fullversion" | sed '[s/^[0-9]*\.\([0-9]*\).*/\1/]'`
|
||||||
|
python_version=`echo "$python_fullversion" | sed '[s/^\([0-9]*\.[0-9]*\).*/\1/]'`
|
||||||
|
# Reject unsupported Python versions as soon as practical.
|
||||||
|
if test "$python_majorversion" -lt 3 -a "$python_minorversion" -lt 4; then
|
||||||
|
AC_MSG_ERROR([Python version $python_version is too old (version 2.4 or later is required)])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([for Python distutils module])
|
||||||
|
if "${PYTHON}" -c 'import distutils' 2>&AS_MESSAGE_LOG_FD
|
||||||
|
then
|
||||||
|
AC_MSG_RESULT(yes)
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR([distutils module not found])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([Python configuration directory])
|
||||||
|
python_configdir=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBPL'))))"`
|
||||||
|
AC_MSG_RESULT([$python_configdir])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([Python include directories])
|
||||||
|
python_includespec=`${PYTHON} -c "
|
||||||
|
import distutils.sysconfig
|
||||||
|
a = '-I' + distutils.sysconfig.get_python_inc(False)
|
||||||
|
b = '-I' + distutils.sysconfig.get_python_inc(True)
|
||||||
|
if a == b:
|
||||||
|
print(a)
|
||||||
|
else:
|
||||||
|
print(a + ' ' + b)"`
|
||||||
|
if test "$PORTNAME" = win32 ; then
|
||||||
|
python_includespec=`echo $python_includespec | sed 's,[[\]],/,g'`
|
||||||
|
fi
|
||||||
|
AC_MSG_RESULT([$python_includespec])
|
||||||
|
|
||||||
|
AC_SUBST(python_majorversion)[]dnl
|
||||||
|
AC_SUBST(python_version)[]dnl
|
||||||
|
AC_SUBST(python_includespec)[]dnl
|
||||||
|
])# _PGAC_CHECK_PYTHON_DIRS
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_CHECK_PYTHON_EMBED_SETUP
|
||||||
|
# -----------------------------
|
||||||
|
#
|
||||||
|
# Set python_libdir to the path of the directory containing the Python shared
|
||||||
|
# library. Set python_libspec to the -L/-l linker switches needed to link it.
|
||||||
|
# Set python_additional_libs to contain any additional linker switches needed
|
||||||
|
# for subsidiary libraries.
|
||||||
|
#
|
||||||
|
# In modern, well-configured Python installations, LIBDIR gives the correct
|
||||||
|
# directory name and LDLIBRARY is the file name of the shlib. But in older
|
||||||
|
# installations LDLIBRARY is frequently a useless path fragment, and it's also
|
||||||
|
# possible that the shlib is in a standard library directory such as /usr/lib
|
||||||
|
# so that LIBDIR is irrelevant. Also, some packagers put the .so symlink for
|
||||||
|
# the shlib in ${python_configdir} even though Python itself never does.
|
||||||
|
# We must also check that what we found is a shared library not a plain
|
||||||
|
# library, which we do by checking its extension. (We used to rely on
|
||||||
|
# Py_ENABLE_SHARED, but that only tells us that a shlib exists, not that
|
||||||
|
# we found it.)
|
||||||
|
AC_DEFUN([PGAC_CHECK_PYTHON_EMBED_SETUP],
|
||||||
|
[AC_REQUIRE([_PGAC_CHECK_PYTHON_DIRS])
|
||||||
|
AC_MSG_CHECKING([how to link an embedded Python application])
|
||||||
|
|
||||||
|
python_libdir=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBDIR'))))"`
|
||||||
|
python_ldlibrary=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LDLIBRARY'))))"`
|
||||||
|
|
||||||
|
# If LDLIBRARY exists and has a shlib extension, use it verbatim.
|
||||||
|
ldlibrary=`echo "${python_ldlibrary}" | sed -e 's/\.so$//' -e 's/\.dll$//' -e 's/\.dylib$//' -e 's/\.sl$//'`
|
||||||
|
if test -e "${python_libdir}/${python_ldlibrary}" -a x"${python_ldlibrary}" != x"${ldlibrary}"
|
||||||
|
then
|
||||||
|
ldlibrary=`echo "${ldlibrary}" | sed "s/^lib//"`
|
||||||
|
found_shlib=1
|
||||||
|
else
|
||||||
|
# Otherwise, guess the base name of the shlib.
|
||||||
|
# LDVERSION was added in Python 3.2, before that use VERSION,
|
||||||
|
# or failing that, $python_version from _PGAC_CHECK_PYTHON_DIRS.
|
||||||
|
python_ldversion=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LDVERSION'))))"`
|
||||||
|
if test x"${python_ldversion}" != x""; then
|
||||||
|
ldlibrary="python${python_ldversion}"
|
||||||
|
else
|
||||||
|
python_version_var=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('VERSION'))))"`
|
||||||
|
if test x"${python_version_var}" != x""; then
|
||||||
|
ldlibrary="python${python_version_var}"
|
||||||
|
else
|
||||||
|
ldlibrary="python${python_version}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Search for a likely-looking file.
|
||||||
|
found_shlib=0
|
||||||
|
for d in "${python_libdir}" "${python_configdir}" /usr/lib64 /usr/lib
|
||||||
|
do
|
||||||
|
# We don't know the platform DLSUFFIX here, so check 'em all.
|
||||||
|
for e in .so .dll .dylib .sl; do
|
||||||
|
if test -e "$d/lib${ldlibrary}$e"; then
|
||||||
|
python_libdir="$d"
|
||||||
|
found_shlib=1
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
# Some platforms (OpenBSD) require us to accept a bare versioned shlib
|
||||||
|
# (".so.n.n") as well. However, check this only after failing to find
|
||||||
|
# ".so" anywhere, because yet other platforms (Debian) put the .so
|
||||||
|
# symlink in a different directory from the underlying versioned shlib.
|
||||||
|
if test "$found_shlib" != 1; then
|
||||||
|
for d in "${python_libdir}" "${python_configdir}" /usr/lib64 /usr/lib
|
||||||
|
do
|
||||||
|
for f in "$d/lib${ldlibrary}.so."* ; do
|
||||||
|
if test -e "$f"; then
|
||||||
|
python_libdir="$d"
|
||||||
|
found_shlib=1
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
# As usual, Windows has its own ideas. Possible default library
|
||||||
|
# locations include c:/Windows/System32 and (for Cygwin) /usr/bin,
|
||||||
|
# and the "lib" prefix might not be there.
|
||||||
|
if test "$found_shlib" != 1 -a \( "$PORTNAME" = win32 -o "$PORTNAME" = cygwin \); then
|
||||||
|
for d in "${python_libdir}" "${python_configdir}" c:/Windows/System32 /usr/bin
|
||||||
|
do
|
||||||
|
for f in "$d/lib${ldlibrary}.dll" "$d/${ldlibrary}.dll" ; do
|
||||||
|
if test -e "$f"; then
|
||||||
|
python_libdir="$d"
|
||||||
|
found_shlib=1
|
||||||
|
break 2
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if test "$found_shlib" != 1; then
|
||||||
|
AC_MSG_ERROR([could not find shared library for Python
|
||||||
|
You might have to rebuild your Python installation. Refer to the
|
||||||
|
documentation for details. Use --without-python to disable building
|
||||||
|
PL/Python.])
|
||||||
|
fi
|
||||||
|
python_libspec="-L${python_libdir} -l${ldlibrary}"
|
||||||
|
|
||||||
|
python_additional_libs=`${PYTHON} -c "import distutils.sysconfig; print(' '.join(filter(None,distutils.sysconfig.get_config_vars('LIBS','LIBC','LIBM','BASEMODLIBS'))))"`
|
||||||
|
|
||||||
|
AC_MSG_RESULT([${python_libspec} ${python_additional_libs}])
|
||||||
|
|
||||||
|
AC_SUBST(python_libdir)[]dnl
|
||||||
|
AC_SUBST(python_libspec)[]dnl
|
||||||
|
AC_SUBST(python_additional_libs)[]dnl
|
||||||
|
|
||||||
|
])# PGAC_CHECK_PYTHON_EMBED_SETUP
|
|
@ -0,0 +1,105 @@
|
||||||
|
# config/tcl.m4
|
||||||
|
|
||||||
|
# Autoconf macros to check for Tcl related things
|
||||||
|
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_PATH_TCLSH],
|
||||||
|
[PGAC_PATH_PROGS(TCLSH, [tclsh tcl tclsh8.6 tclsh86 tclsh8.5 tclsh85 tclsh8.4 tclsh84])
|
||||||
|
AC_ARG_VAR(TCLSH, [Tcl interpreter program (tclsh)])dnl
|
||||||
|
if test x"$TCLSH" = x""; then
|
||||||
|
AC_MSG_ERROR([Tcl shell not found])
|
||||||
|
fi
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_TCLCONFIGSH([SEARCH-PATH])
|
||||||
|
# ------------------------------------
|
||||||
|
# If the user doesn't specify $TCL_CONFIG_SH directly, search for it in
|
||||||
|
# the list of directories passed as parameter (from --with-tclconfig).
|
||||||
|
# If no list is given, try the Tcl shell's $auto_path.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_PATH_TCLCONFIGSH],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_TCLSH])[]dnl
|
||||||
|
AC_BEFORE([$0], [PGAC_PATH_TKCONFIGSH])[]dnl
|
||||||
|
AC_MSG_CHECKING([for tclConfig.sh])
|
||||||
|
# Let user override test
|
||||||
|
if test -z "$TCL_CONFIG_SH"; then
|
||||||
|
pgac_test_dirs="$1"
|
||||||
|
|
||||||
|
set X $pgac_test_dirs; shift
|
||||||
|
if test $[#] -eq 0; then
|
||||||
|
test -z "$TCLSH" && AC_MSG_ERROR([unable to locate tclConfig.sh because no Tcl shell was found])
|
||||||
|
pgac_test_dirs=`echo 'puts $auto_path' | $TCLSH`
|
||||||
|
# On newer macOS, $auto_path frequently doesn't include the place
|
||||||
|
# where tclConfig.sh actually lives. Append that to the end, so as not
|
||||||
|
# to break cases where a non-default Tcl installation is being used.
|
||||||
|
if test -d "$PG_SYSROOT/System/Library/Frameworks/Tcl.framework" ; then
|
||||||
|
pgac_test_dirs="$pgac_test_dirs $PG_SYSROOT/System/Library/Frameworks/Tcl.framework"
|
||||||
|
fi
|
||||||
|
set X $pgac_test_dirs; shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pgac_dir do
|
||||||
|
if test -r "$pgac_dir/tclConfig.sh"; then
|
||||||
|
TCL_CONFIG_SH=$pgac_dir/tclConfig.sh
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$TCL_CONFIG_SH"; then
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR([file 'tclConfig.sh' is required for Tcl])
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([$TCL_CONFIG_SH])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST([TCL_CONFIG_SH])
|
||||||
|
])# PGAC_PATH_TCLCONFIGSH
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_PATH_TKCONFIGSH([SEARCH-PATH])
|
||||||
|
# ------------------------------------
|
||||||
|
AC_DEFUN([PGAC_PATH_TKCONFIGSH],
|
||||||
|
[AC_REQUIRE([PGAC_PATH_TCLSH])[]dnl
|
||||||
|
AC_MSG_CHECKING([for tkConfig.sh])
|
||||||
|
# Let user override test
|
||||||
|
if test -z "$TK_CONFIG_SH"; then
|
||||||
|
pgac_test_dirs="$1"
|
||||||
|
|
||||||
|
set X $pgac_test_dirs; shift
|
||||||
|
if test $[#] -eq 0; then
|
||||||
|
test -z "$TCLSH" && AC_MSG_ERROR([unable to locate tkConfig.sh because no Tcl shell was found])
|
||||||
|
set X `echo 'puts $auto_path' | $TCLSH`; shift
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pgac_dir do
|
||||||
|
if test -r "$pgac_dir/tkConfig.sh"; then
|
||||||
|
TK_CONFIG_SH=$pgac_dir/tkConfig.sh
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test -z "$TK_CONFIG_SH"; then
|
||||||
|
AC_MSG_RESULT(no)
|
||||||
|
AC_MSG_ERROR([file 'tkConfig.sh' is required for Tk])
|
||||||
|
else
|
||||||
|
AC_MSG_RESULT([$TK_CONFIG_SH])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AC_SUBST([TK_CONFIG_SH])
|
||||||
|
])# PGAC_PATH_TKCONFIGSH
|
||||||
|
|
||||||
|
|
||||||
|
# PGAC_EVAL_TCLCONFIGSH(FILE, WANTED-VARS)
|
||||||
|
# ----------------------------------------
|
||||||
|
# Assigns variables listed in WANTED-VARS by reading FILE and
|
||||||
|
# evaluating it according to the quoting scheme of tclConfig.sh and
|
||||||
|
# tkConfig.sh. Calls AC_SUBST for each variable.
|
||||||
|
|
||||||
|
AC_DEFUN([PGAC_EVAL_TCLCONFIGSH],
|
||||||
|
[. "$1"
|
||||||
|
m4_foreach([pgac_item], [$2],
|
||||||
|
[eval pgac_item=\"[$]pgac_item\"
|
||||||
|
AC_SUBST(pgac_item)])])
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
||||||
|
# contrib/Makefile
|
||||||
|
|
||||||
|
subdir = contrib
|
||||||
|
top_builddir = ..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
|
SUBDIRS = \
|
||||||
|
adminpack \
|
||||||
|
amcheck \
|
||||||
|
auth_delay \
|
||||||
|
auto_explain \
|
||||||
|
bloom \
|
||||||
|
btree_gin \
|
||||||
|
btree_gist \
|
||||||
|
citext \
|
||||||
|
cube \
|
||||||
|
dblink \
|
||||||
|
dict_int \
|
||||||
|
dict_xsyn \
|
||||||
|
earthdistance \
|
||||||
|
file_fdw \
|
||||||
|
fuzzystrmatch \
|
||||||
|
hstore \
|
||||||
|
intagg \
|
||||||
|
intarray \
|
||||||
|
isn \
|
||||||
|
lo \
|
||||||
|
ltree \
|
||||||
|
oid2name \
|
||||||
|
pageinspect \
|
||||||
|
passwordcheck \
|
||||||
|
pg_buffercache \
|
||||||
|
pg_freespacemap \
|
||||||
|
pg_prewarm \
|
||||||
|
pg_standby \
|
||||||
|
pg_stat_statements \
|
||||||
|
pg_trgm \
|
||||||
|
pgcrypto \
|
||||||
|
pgrowlocks \
|
||||||
|
pgstattuple \
|
||||||
|
pg_visibility \
|
||||||
|
postgres_fdw \
|
||||||
|
seg \
|
||||||
|
spi \
|
||||||
|
tablefunc \
|
||||||
|
tcn \
|
||||||
|
test_decoding \
|
||||||
|
tsm_system_rows \
|
||||||
|
tsm_system_time \
|
||||||
|
unaccent \
|
||||||
|
vacuumlo
|
||||||
|
|
||||||
|
ifeq ($(with_openssl),yes)
|
||||||
|
SUBDIRS += sslinfo
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += sslinfo
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(with_uuid),no)
|
||||||
|
SUBDIRS += uuid-ossp
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += uuid-ossp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(with_libxml),yes)
|
||||||
|
SUBDIRS += xml2
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += xml2
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(with_selinux),yes)
|
||||||
|
SUBDIRS += sepgsql
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += sepgsql
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(with_perl),yes)
|
||||||
|
SUBDIRS += hstore_plperl jsonb_plperl
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += hstore_plperl jsonb_plperl
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(with_python),yes)
|
||||||
|
SUBDIRS += hstore_plpython jsonb_plpython ltree_plpython
|
||||||
|
else
|
||||||
|
ALWAYS_SUBDIRS += hstore_plpython jsonb_plpython ltree_plpython
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Missing:
|
||||||
|
# start-scripts \ (does not have a makefile)
|
||||||
|
|
||||||
|
|
||||||
|
$(recurse)
|
||||||
|
$(recurse_always)
|
|
@ -0,0 +1,28 @@
|
||||||
|
The PostgreSQL contrib tree
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
This subtree contains porting tools, analysis utilities, and plug-in
|
||||||
|
features that are not part of the core PostgreSQL system, mainly
|
||||||
|
because they address a limited audience or are too experimental to be
|
||||||
|
part of the main source tree. This does not preclude their
|
||||||
|
usefulness.
|
||||||
|
|
||||||
|
User documentation for each module appears in the main SGML
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
When building from the source distribution, these modules are not
|
||||||
|
built automatically, unless you build the "world" target. You can
|
||||||
|
also build and install them all by running "make all" and "make
|
||||||
|
install" in this directory; or to build and install just one selected
|
||||||
|
module, do the same in that module's subdirectory.
|
||||||
|
|
||||||
|
Some directories supply new user-defined functions, operators, or
|
||||||
|
types. To make use of one of these modules, after you have installed
|
||||||
|
the code you need to register the new SQL objects in the database
|
||||||
|
system by executing a CREATE EXTENSION command. In a fresh database,
|
||||||
|
you can simply do
|
||||||
|
|
||||||
|
CREATE EXTENSION module_name;
|
||||||
|
|
||||||
|
See the PostgreSQL documentation for more information about this
|
||||||
|
procedure.
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Generated subdirectories
|
||||||
|
/log/
|
||||||
|
/results/
|
||||||
|
/tmp_check/
|
|
@ -0,0 +1,22 @@
|
||||||
|
# contrib/adminpack/Makefile
|
||||||
|
|
||||||
|
MODULE_big = adminpack
|
||||||
|
OBJS = adminpack.o $(WIN32RES)
|
||||||
|
PG_CPPFLAGS = -I$(libpq_srcdir)
|
||||||
|
|
||||||
|
EXTENSION = adminpack
|
||||||
|
DATA = adminpack--1.0.sql adminpack--1.0--1.1.sql adminpack--1.1--2.0.sql
|
||||||
|
PGFILEDESC = "adminpack - support functions for pgAdmin"
|
||||||
|
|
||||||
|
REGRESS = adminpack
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/adminpack
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,6 @@
|
||||||
|
/* contrib/adminpack/adminpack--1.0--1.1.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION adminpack UPDATE TO '1.1'" to load this file. \quit
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_logfile_rotate() FROM PUBLIC;
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* contrib/adminpack/adminpack--1.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "CREATE EXTENSION adminpack" to load this file. \quit
|
||||||
|
|
||||||
|
/* ***********************************************
|
||||||
|
* Administrative functions for PostgreSQL
|
||||||
|
* *********************************************** */
|
||||||
|
|
||||||
|
/* generic file access functions */
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_write(text, text, bool)
|
||||||
|
RETURNS bigint
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_write'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_rename(text, text, text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_rename'
|
||||||
|
LANGUAGE C VOLATILE;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_rename(text, text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'SELECT pg_catalog.pg_file_rename($1, $2, NULL::pg_catalog.text);'
|
||||||
|
LANGUAGE SQL VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_unlink(text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_unlink'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_logdir_ls()
|
||||||
|
RETURNS setof record
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_logdir_ls'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
|
||||||
|
/* Renaming of existing backend functions for pgAdmin compatibility */
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint)
|
||||||
|
RETURNS text
|
||||||
|
AS 'pg_read_file'
|
||||||
|
LANGUAGE INTERNAL VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_file_length(text)
|
||||||
|
RETURNS bigint
|
||||||
|
AS 'SELECT size FROM pg_catalog.pg_stat_file($1)'
|
||||||
|
LANGUAGE SQL VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE FUNCTION pg_catalog.pg_logfile_rotate()
|
||||||
|
RETURNS int4
|
||||||
|
AS 'pg_rotate_logfile'
|
||||||
|
LANGUAGE INTERNAL VOLATILE STRICT;
|
|
@ -0,0 +1,51 @@
|
||||||
|
/* contrib/adminpack/adminpack--1.1--2.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via ALTER EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION adminpack UPDATE TO '2.0'" to load this file. \quit
|
||||||
|
|
||||||
|
/* ***********************************************
|
||||||
|
* Administrative functions for PostgreSQL
|
||||||
|
* *********************************************** */
|
||||||
|
|
||||||
|
/* generic file access functions */
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_write(text, text, bool)
|
||||||
|
RETURNS bigint
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_write_v1_1'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_file_write(text, text, bool) FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_rename(text, text, text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_rename_v1_1'
|
||||||
|
LANGUAGE C VOLATILE;
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_file_rename(text, text, text) FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_rename(text, text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'SELECT pg_catalog.pg_file_rename($1, $2, NULL::pg_catalog.text);'
|
||||||
|
LANGUAGE SQL VOLATILE STRICT;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.pg_file_unlink(text)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_file_unlink_v1_1'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_file_unlink(text) FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION pg_catalog.pg_logdir_ls()
|
||||||
|
RETURNS setof record
|
||||||
|
AS 'MODULE_PATHNAME', 'pg_logdir_ls_v1_1'
|
||||||
|
LANGUAGE C VOLATILE STRICT;
|
||||||
|
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_catalog.pg_logdir_ls() FROM PUBLIC;
|
||||||
|
|
||||||
|
/* These functions are now in the backend and callers should update to use those */
|
||||||
|
|
||||||
|
DROP FUNCTION pg_file_read(text, bigint, bigint);
|
||||||
|
|
||||||
|
DROP FUNCTION pg_file_length(text);
|
||||||
|
|
||||||
|
DROP FUNCTION pg_logfile_rotate();
|
|
@ -0,0 +1,575 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* adminpack.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2002-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* Author: Andreas Pflug <pgadmin@pse-consulting.de>
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/adminpack/adminpack.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "catalog/pg_authid.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "funcapi.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "postmaster/syslogger.h"
|
||||||
|
#include "storage/fd.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/datetime.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
|
||||||
|
#ifdef rename
|
||||||
|
#undef rename
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef unlink
|
||||||
|
#undef unlink
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_write);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_write_v1_1);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_rename);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_rename_v1_1);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_unlink);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_file_unlink_v1_1);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_logdir_ls);
|
||||||
|
PG_FUNCTION_INFO_V1(pg_logdir_ls_v1_1);
|
||||||
|
|
||||||
|
static int64 pg_file_write_internal(text *file, text *data, bool replace);
|
||||||
|
static bool pg_file_rename_internal(text *file1, text *file2, text *file3);
|
||||||
|
static Datum pg_logdir_ls_internal(FunctionCallInfo fcinfo);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *location;
|
||||||
|
DIR *dirdesc;
|
||||||
|
} directory_fctx;
|
||||||
|
|
||||||
|
/*-----------------------
|
||||||
|
* some helper functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert a "text" filename argument to C string, and check it's allowable.
|
||||||
|
*
|
||||||
|
* Filename may be absolute or relative to the DataDir, but we only allow
|
||||||
|
* absolute paths that match DataDir or Log_directory.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
convert_and_check_filename(text *arg, bool logAllowed)
|
||||||
|
{
|
||||||
|
char *filename = text_to_cstring(arg);
|
||||||
|
|
||||||
|
canonicalize_path(filename); /* filename can change length here */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Members of the 'pg_write_server_files' role are allowed to access any
|
||||||
|
* files on the server as the PG user, so no need to do any further checks
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
if (is_member_of_role(GetUserId(), DEFAULT_ROLE_WRITE_SERVER_FILES))
|
||||||
|
return filename;
|
||||||
|
|
||||||
|
/* User isn't a member of the default role, so check if it's allowable */
|
||||||
|
if (is_absolute_path(filename))
|
||||||
|
{
|
||||||
|
/* Disallow '/a/b/data/..' */
|
||||||
|
if (path_contains_parent_reference(filename))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("reference to parent directory (\"..\") not allowed"))));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow absolute paths if within DataDir or Log_directory, even
|
||||||
|
* though Log_directory might be outside DataDir.
|
||||||
|
*/
|
||||||
|
if (!path_is_prefix_of_path(DataDir, filename) &&
|
||||||
|
(!logAllowed || !is_absolute_path(Log_directory) ||
|
||||||
|
!path_is_prefix_of_path(Log_directory, filename)))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("absolute path not allowed"))));
|
||||||
|
}
|
||||||
|
else if (!path_is_relative_and_below_cwd(filename))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("path must be in or below the current directory"))));
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check for superuser, bark if not.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
requireSuperuser(void)
|
||||||
|
{
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("only superuser may access generic file functions"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_write - old version
|
||||||
|
*
|
||||||
|
* The superuser() check here must be kept as the library might be upgraded
|
||||||
|
* without the extension being upgraded, meaning that in pre-1.1 installations
|
||||||
|
* these functions could be called by any user.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_write(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *file = PG_GETARG_TEXT_PP(0);
|
||||||
|
text *data = PG_GETARG_TEXT_PP(1);
|
||||||
|
bool replace = PG_GETARG_BOOL(2);
|
||||||
|
int64 count = 0;
|
||||||
|
|
||||||
|
requireSuperuser();
|
||||||
|
|
||||||
|
count = pg_file_write_internal(file, data, replace);
|
||||||
|
|
||||||
|
PG_RETURN_INT64(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_write_v1_1 - Version 1.1
|
||||||
|
*
|
||||||
|
* As of adminpack version 1.1, we no longer need to check if the user
|
||||||
|
* is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
|
||||||
|
* Users can then grant access to it based on their policies.
|
||||||
|
*
|
||||||
|
* Otherwise identical to pg_file_write (above).
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_write_v1_1(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *file = PG_GETARG_TEXT_PP(0);
|
||||||
|
text *data = PG_GETARG_TEXT_PP(1);
|
||||||
|
bool replace = PG_GETARG_BOOL(2);
|
||||||
|
int64 count = 0;
|
||||||
|
|
||||||
|
count = pg_file_write_internal(file, data, replace);
|
||||||
|
|
||||||
|
PG_RETURN_INT64(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_write_internal - Workhorse for pg_file_write functions.
|
||||||
|
*
|
||||||
|
* This handles the actual work for pg_file_write.
|
||||||
|
*/
|
||||||
|
static int64
|
||||||
|
pg_file_write_internal(text *file, text *data, bool replace)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *filename;
|
||||||
|
int64 count = 0;
|
||||||
|
|
||||||
|
filename = convert_and_check_filename(file, false);
|
||||||
|
|
||||||
|
if (!replace)
|
||||||
|
{
|
||||||
|
struct stat fst;
|
||||||
|
|
||||||
|
if (stat(filename, &fst) >= 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(ERRCODE_DUPLICATE_FILE,
|
||||||
|
errmsg("file \"%s\" exists", filename)));
|
||||||
|
|
||||||
|
f = AllocateFile(filename, "wb");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
f = AllocateFile(filename, "ab");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not open file \"%s\" for writing: %m",
|
||||||
|
filename)));
|
||||||
|
|
||||||
|
count = fwrite(VARDATA_ANY(data), 1, VARSIZE_ANY_EXHDR(data), f);
|
||||||
|
if (count != VARSIZE_ANY_EXHDR(data) || FreeFile(f))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not write file \"%s\": %m", filename)));
|
||||||
|
|
||||||
|
return (count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_rename - old version
|
||||||
|
*
|
||||||
|
* The superuser() check here must be kept as the library might be upgraded
|
||||||
|
* without the extension being upgraded, meaning that in pre-1.1 installations
|
||||||
|
* these functions could be called by any user.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_rename(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *file1;
|
||||||
|
text *file2;
|
||||||
|
text *file3;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
requireSuperuser();
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
|
file1 = PG_GETARG_TEXT_PP(0);
|
||||||
|
file2 = PG_GETARG_TEXT_PP(1);
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(2))
|
||||||
|
file3 = NULL;
|
||||||
|
else
|
||||||
|
file3 = PG_GETARG_TEXT_PP(2);
|
||||||
|
|
||||||
|
result = pg_file_rename_internal(file1, file2, file3);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_rename_v1_1 - Version 1.1
|
||||||
|
*
|
||||||
|
* As of adminpack version 1.1, we no longer need to check if the user
|
||||||
|
* is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
|
||||||
|
* Users can then grant access to it based on their policies.
|
||||||
|
*
|
||||||
|
* Otherwise identical to pg_file_write (above).
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_rename_v1_1(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *file1;
|
||||||
|
text *file2;
|
||||||
|
text *file3;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
|
file1 = PG_GETARG_TEXT_PP(0);
|
||||||
|
file2 = PG_GETARG_TEXT_PP(1);
|
||||||
|
|
||||||
|
if (PG_ARGISNULL(2))
|
||||||
|
file3 = NULL;
|
||||||
|
else
|
||||||
|
file3 = PG_GETARG_TEXT_PP(2);
|
||||||
|
|
||||||
|
result = pg_file_rename_internal(file1, file2, file3);
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_rename_internal - Workhorse for pg_file_rename functions.
|
||||||
|
*
|
||||||
|
* This handles the actual work for pg_file_rename.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
pg_file_rename_internal(text *file1, text *file2, text *file3)
|
||||||
|
{
|
||||||
|
char *fn1,
|
||||||
|
*fn2,
|
||||||
|
*fn3;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
fn1 = convert_and_check_filename(file1, false);
|
||||||
|
fn2 = convert_and_check_filename(file2, false);
|
||||||
|
|
||||||
|
if (file3 == NULL)
|
||||||
|
fn3 = NULL;
|
||||||
|
else
|
||||||
|
fn3 = convert_and_check_filename(file3, false);
|
||||||
|
|
||||||
|
if (access(fn1, W_OK) < 0)
|
||||||
|
{
|
||||||
|
ereport(WARNING,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("file \"%s\" is not accessible: %m", fn1)));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn3 && access(fn2, W_OK) < 0)
|
||||||
|
{
|
||||||
|
ereport(WARNING,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("file \"%s\" is not accessible: %m", fn2)));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = access(fn3 ? fn3 : fn2, W_OK);
|
||||||
|
if (rc >= 0 || errno != ENOENT)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(ERRCODE_DUPLICATE_FILE,
|
||||||
|
errmsg("cannot rename to target file \"%s\"",
|
||||||
|
fn3 ? fn3 : fn2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fn3)
|
||||||
|
{
|
||||||
|
if (rename(fn2, fn3) != 0)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not rename \"%s\" to \"%s\": %m",
|
||||||
|
fn2, fn3)));
|
||||||
|
}
|
||||||
|
if (rename(fn1, fn2) != 0)
|
||||||
|
{
|
||||||
|
ereport(WARNING,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not rename \"%s\" to \"%s\": %m",
|
||||||
|
fn1, fn2)));
|
||||||
|
|
||||||
|
if (rename(fn3, fn2) != 0)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not rename \"%s\" back to \"%s\": %m",
|
||||||
|
fn3, fn2)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(ERRCODE_UNDEFINED_FILE,
|
||||||
|
errmsg("renaming \"%s\" to \"%s\" was reverted",
|
||||||
|
fn2, fn3)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rename(fn1, fn2) != 0)
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_unlink - old version
|
||||||
|
*
|
||||||
|
* The superuser() check here must be kept as the library might be upgraded
|
||||||
|
* without the extension being upgraded, meaning that in pre-1.1 installations
|
||||||
|
* these functions could be called by any user.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_unlink(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
requireSuperuser();
|
||||||
|
|
||||||
|
filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false);
|
||||||
|
|
||||||
|
if (access(filename, W_OK) < 0)
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
PG_RETURN_BOOL(false);
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("file \"%s\" is not accessible: %m", filename)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink(filename) < 0)
|
||||||
|
{
|
||||||
|
ereport(WARNING,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not unlink file \"%s\": %m", filename)));
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(false);
|
||||||
|
}
|
||||||
|
PG_RETURN_BOOL(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_file_unlink_v1_1 - Version 1.1
|
||||||
|
*
|
||||||
|
* As of adminpack version 1.1, we no longer need to check if the user
|
||||||
|
* is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
|
||||||
|
* Users can then grant access to it based on their policies.
|
||||||
|
*
|
||||||
|
* Otherwise identical to pg_file_unlink (above).
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_file_unlink_v1_1(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
filename = convert_and_check_filename(PG_GETARG_TEXT_PP(0), false);
|
||||||
|
|
||||||
|
if (access(filename, W_OK) < 0)
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
PG_RETURN_BOOL(false);
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("file \"%s\" is not accessible: %m", filename)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlink(filename) < 0)
|
||||||
|
{
|
||||||
|
ereport(WARNING,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not unlink file \"%s\": %m", filename)));
|
||||||
|
|
||||||
|
PG_RETURN_BOOL(false);
|
||||||
|
}
|
||||||
|
PG_RETURN_BOOL(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_logdir_ls - Old version
|
||||||
|
*
|
||||||
|
* The superuser() check here must be kept as the library might be upgraded
|
||||||
|
* without the extension being upgraded, meaning that in pre-1.1 installations
|
||||||
|
* these functions could be called by any user.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_logdir_ls(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
if (!superuser())
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||||
|
(errmsg("only superuser can list the log directory"))));
|
||||||
|
|
||||||
|
return (pg_logdir_ls_internal(fcinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------
|
||||||
|
* pg_logdir_ls_v1_1 - Version 1.1
|
||||||
|
*
|
||||||
|
* As of adminpack version 1.1, we no longer need to check if the user
|
||||||
|
* is a superuser because we REVOKE EXECUTE on the function from PUBLIC.
|
||||||
|
* Users can then grant access to it based on their policies.
|
||||||
|
*
|
||||||
|
* Otherwise identical to pg_logdir_ls (above).
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
pg_logdir_ls_v1_1(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
return (pg_logdir_ls_internal(fcinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
pg_logdir_ls_internal(FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
FuncCallContext *funcctx;
|
||||||
|
struct dirent *de;
|
||||||
|
directory_fctx *fctx;
|
||||||
|
|
||||||
|
if (strcmp(Log_filename, "postgresql-%Y-%m-%d_%H%M%S.log") != 0)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
(errmsg("the log_filename parameter must equal 'postgresql-%%Y-%%m-%%d_%%H%%M%%S.log'"))));
|
||||||
|
|
||||||
|
if (SRF_IS_FIRSTCALL())
|
||||||
|
{
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
TupleDesc tupdesc;
|
||||||
|
|
||||||
|
funcctx = SRF_FIRSTCALL_INIT();
|
||||||
|
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||||
|
|
||||||
|
fctx = palloc(sizeof(directory_fctx));
|
||||||
|
|
||||||
|
tupdesc = CreateTemplateTupleDesc(2);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
|
||||||
|
TIMESTAMPOID, -1, 0);
|
||||||
|
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
|
||||||
|
TEXTOID, -1, 0);
|
||||||
|
|
||||||
|
funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||||
|
|
||||||
|
fctx->location = pstrdup(Log_directory);
|
||||||
|
fctx->dirdesc = AllocateDir(fctx->location);
|
||||||
|
|
||||||
|
if (!fctx->dirdesc)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode_for_file_access(),
|
||||||
|
errmsg("could not open directory \"%s\": %m",
|
||||||
|
fctx->location)));
|
||||||
|
|
||||||
|
funcctx->user_fctx = fctx;
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
}
|
||||||
|
|
||||||
|
funcctx = SRF_PERCALL_SETUP();
|
||||||
|
fctx = (directory_fctx *) funcctx->user_fctx;
|
||||||
|
|
||||||
|
while ((de = ReadDir(fctx->dirdesc, fctx->location)) != NULL)
|
||||||
|
{
|
||||||
|
char *values[2];
|
||||||
|
HeapTuple tuple;
|
||||||
|
char timestampbuf[32];
|
||||||
|
char *field[MAXDATEFIELDS];
|
||||||
|
char lowstr[MAXDATELEN + 1];
|
||||||
|
int dtype;
|
||||||
|
int nf,
|
||||||
|
ftype[MAXDATEFIELDS];
|
||||||
|
fsec_t fsec;
|
||||||
|
int tz = 0;
|
||||||
|
struct pg_tm date;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default format: postgresql-YYYY-MM-DD_HHMMSS.log
|
||||||
|
*/
|
||||||
|
if (strlen(de->d_name) != 32
|
||||||
|
|| strncmp(de->d_name, "postgresql-", 11) != 0
|
||||||
|
|| de->d_name[21] != '_'
|
||||||
|
|| strcmp(de->d_name + 28, ".log") != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* extract timestamp portion of filename */
|
||||||
|
strcpy(timestampbuf, de->d_name + 11);
|
||||||
|
timestampbuf[17] = '\0';
|
||||||
|
|
||||||
|
/* parse and decode expected timestamp to verify it's OK format */
|
||||||
|
if (ParseDateTime(timestampbuf, lowstr, MAXDATELEN, field, ftype, MAXDATEFIELDS, &nf))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Seems the timestamp is OK; prepare and return tuple */
|
||||||
|
|
||||||
|
values[0] = timestampbuf;
|
||||||
|
values[1] = psprintf("%s/%s", fctx->location, de->d_name);
|
||||||
|
|
||||||
|
tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
|
||||||
|
|
||||||
|
SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeDir(fctx->dirdesc);
|
||||||
|
SRF_RETURN_DONE(funcctx);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
# adminpack extension
|
||||||
|
comment = 'administrative functions for PostgreSQL'
|
||||||
|
default_version = '2.0'
|
||||||
|
module_pathname = '$libdir/adminpack'
|
||||||
|
relocatable = false
|
||||||
|
schema = pg_catalog
|
|
@ -0,0 +1,155 @@
|
||||||
|
CREATE EXTENSION adminpack;
|
||||||
|
-- create new file
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', false);
|
||||||
|
pg_file_write
|
||||||
|
---------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- append
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', true);
|
||||||
|
pg_file_write
|
||||||
|
---------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test1test1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- error, already exists
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', false);
|
||||||
|
ERROR: file "test_file1" exists
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test1test1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- disallowed file paths for non-superusers and users who are
|
||||||
|
-- not members of pg_write_server_files
|
||||||
|
CREATE ROLE regress_user1;
|
||||||
|
GRANT pg_read_all_settings TO regress_user1;
|
||||||
|
GRANT EXECUTE ON FUNCTION pg_file_write(text,text,bool) TO regress_user1;
|
||||||
|
SET ROLE regress_user1;
|
||||||
|
SELECT pg_file_write('../test_file0', 'test0', false);
|
||||||
|
ERROR: path must be in or below the current directory
|
||||||
|
SELECT pg_file_write('/tmp/test_file0', 'test0', false);
|
||||||
|
ERROR: absolute path not allowed
|
||||||
|
SELECT pg_file_write(current_setting('data_directory') || '/test_file4', 'test4', false);
|
||||||
|
pg_file_write
|
||||||
|
---------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_write(current_setting('data_directory') || '/../test_file4', 'test4', false);
|
||||||
|
ERROR: reference to parent directory ("..") not allowed
|
||||||
|
RESET ROLE;
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
|
||||||
|
REVOKE pg_read_all_settings FROM regress_user1;
|
||||||
|
DROP ROLE regress_user1;
|
||||||
|
-- rename file
|
||||||
|
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||||
|
pg_file_rename
|
||||||
|
----------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_read_file('test_file1'); -- not there
|
||||||
|
ERROR: could not stat file "test_file1": No such file or directory
|
||||||
|
SELECT pg_read_file('test_file2');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test1test1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- error
|
||||||
|
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||||
|
WARNING: file "test_file1" is not accessible: No such file or directory
|
||||||
|
pg_file_rename
|
||||||
|
----------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- rename file and archive
|
||||||
|
SELECT pg_file_write('test_file3', 'test3', false);
|
||||||
|
pg_file_write
|
||||||
|
---------------
|
||||||
|
5
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_rename('test_file2', 'test_file3', 'test_file3_archive');
|
||||||
|
pg_file_rename
|
||||||
|
----------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_read_file('test_file2'); -- not there
|
||||||
|
ERROR: could not stat file "test_file2": No such file or directory
|
||||||
|
SELECT pg_read_file('test_file3');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test1test1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_read_file('test_file3_archive');
|
||||||
|
pg_read_file
|
||||||
|
--------------
|
||||||
|
test3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- unlink
|
||||||
|
SELECT pg_file_unlink('test_file1'); -- does not exist
|
||||||
|
pg_file_unlink
|
||||||
|
----------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_unlink('test_file2'); -- does not exist
|
||||||
|
pg_file_unlink
|
||||||
|
----------------
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_unlink('test_file3');
|
||||||
|
pg_file_unlink
|
||||||
|
----------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_unlink('test_file3_archive');
|
||||||
|
pg_file_unlink
|
||||||
|
----------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT pg_file_unlink('test_file4');
|
||||||
|
pg_file_unlink
|
||||||
|
----------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- superuser checks
|
||||||
|
CREATE USER regress_user1;
|
||||||
|
SET ROLE regress_user1;
|
||||||
|
SELECT pg_file_write('test_file0', 'test0', false);
|
||||||
|
ERROR: permission denied for function pg_file_write
|
||||||
|
SELECT pg_file_rename('test_file0', 'test_file0');
|
||||||
|
ERROR: permission denied for function pg_file_rename
|
||||||
|
CONTEXT: SQL function "pg_file_rename" statement 1
|
||||||
|
SELECT pg_file_unlink('test_file0');
|
||||||
|
ERROR: permission denied for function pg_file_unlink
|
||||||
|
SELECT pg_logdir_ls();
|
||||||
|
ERROR: permission denied for function pg_logdir_ls
|
||||||
|
RESET ROLE;
|
||||||
|
DROP USER regress_user1;
|
||||||
|
-- no further tests for pg_logdir_ls() because it depends on the
|
||||||
|
-- server's logging setup
|
|
@ -0,0 +1,70 @@
|
||||||
|
CREATE EXTENSION adminpack;
|
||||||
|
|
||||||
|
-- create new file
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', false);
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
|
||||||
|
-- append
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', true);
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
|
||||||
|
-- error, already exists
|
||||||
|
SELECT pg_file_write('test_file1', 'test1', false);
|
||||||
|
SELECT pg_read_file('test_file1');
|
||||||
|
|
||||||
|
-- disallowed file paths for non-superusers and users who are
|
||||||
|
-- not members of pg_write_server_files
|
||||||
|
CREATE ROLE regress_user1;
|
||||||
|
|
||||||
|
GRANT pg_read_all_settings TO regress_user1;
|
||||||
|
GRANT EXECUTE ON FUNCTION pg_file_write(text,text,bool) TO regress_user1;
|
||||||
|
|
||||||
|
SET ROLE regress_user1;
|
||||||
|
SELECT pg_file_write('../test_file0', 'test0', false);
|
||||||
|
SELECT pg_file_write('/tmp/test_file0', 'test0', false);
|
||||||
|
SELECT pg_file_write(current_setting('data_directory') || '/test_file4', 'test4', false);
|
||||||
|
SELECT pg_file_write(current_setting('data_directory') || '/../test_file4', 'test4', false);
|
||||||
|
RESET ROLE;
|
||||||
|
REVOKE EXECUTE ON FUNCTION pg_file_write(text,text,bool) FROM regress_user1;
|
||||||
|
REVOKE pg_read_all_settings FROM regress_user1;
|
||||||
|
DROP ROLE regress_user1;
|
||||||
|
|
||||||
|
-- rename file
|
||||||
|
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||||
|
SELECT pg_read_file('test_file1'); -- not there
|
||||||
|
SELECT pg_read_file('test_file2');
|
||||||
|
|
||||||
|
-- error
|
||||||
|
SELECT pg_file_rename('test_file1', 'test_file2');
|
||||||
|
|
||||||
|
-- rename file and archive
|
||||||
|
SELECT pg_file_write('test_file3', 'test3', false);
|
||||||
|
SELECT pg_file_rename('test_file2', 'test_file3', 'test_file3_archive');
|
||||||
|
SELECT pg_read_file('test_file2'); -- not there
|
||||||
|
SELECT pg_read_file('test_file3');
|
||||||
|
SELECT pg_read_file('test_file3_archive');
|
||||||
|
|
||||||
|
|
||||||
|
-- unlink
|
||||||
|
SELECT pg_file_unlink('test_file1'); -- does not exist
|
||||||
|
SELECT pg_file_unlink('test_file2'); -- does not exist
|
||||||
|
SELECT pg_file_unlink('test_file3');
|
||||||
|
SELECT pg_file_unlink('test_file3_archive');
|
||||||
|
SELECT pg_file_unlink('test_file4');
|
||||||
|
|
||||||
|
|
||||||
|
-- superuser checks
|
||||||
|
CREATE USER regress_user1;
|
||||||
|
SET ROLE regress_user1;
|
||||||
|
|
||||||
|
SELECT pg_file_write('test_file0', 'test0', false);
|
||||||
|
SELECT pg_file_rename('test_file0', 'test_file0');
|
||||||
|
SELECT pg_file_unlink('test_file0');
|
||||||
|
SELECT pg_logdir_ls();
|
||||||
|
|
||||||
|
RESET ROLE;
|
||||||
|
DROP USER regress_user1;
|
||||||
|
|
||||||
|
|
||||||
|
-- no further tests for pg_logdir_ls() because it depends on the
|
||||||
|
-- server's logging setup
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Generated subdirectories
|
||||||
|
/log/
|
||||||
|
/results/
|
||||||
|
/tmp_check/
|
|
@ -0,0 +1,21 @@
|
||||||
|
# contrib/amcheck/Makefile
|
||||||
|
|
||||||
|
MODULE_big = amcheck
|
||||||
|
OBJS = verify_nbtree.o $(WIN32RES)
|
||||||
|
|
||||||
|
EXTENSION = amcheck
|
||||||
|
DATA = amcheck--1.1--1.2.sql amcheck--1.0--1.1.sql amcheck--1.0.sql
|
||||||
|
PGFILEDESC = "amcheck - function for verifying relation integrity"
|
||||||
|
|
||||||
|
REGRESS = check check_btree
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/amcheck
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* contrib/amcheck/amcheck--1.0--1.1.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION amcheck UPDATE TO '1.1'" to load this file. \quit
|
||||||
|
|
||||||
|
-- In order to avoid issues with dependencies when updating amcheck to 1.1,
|
||||||
|
-- create new, overloaded versions of the 1.0 functions
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_index_check()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_index_check(index regclass,
|
||||||
|
heapallindexed boolean)
|
||||||
|
RETURNS VOID
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_index_check'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_index_parent_check()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_index_parent_check(index regclass,
|
||||||
|
heapallindexed boolean)
|
||||||
|
RETURNS VOID
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_index_parent_check'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
||||||
|
|
||||||
|
-- Don't want these to be available to public
|
||||||
|
REVOKE ALL ON FUNCTION bt_index_check(regclass, boolean) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION bt_index_parent_check(regclass, boolean) FROM PUBLIC;
|
|
@ -0,0 +1,24 @@
|
||||||
|
/* contrib/amcheck/amcheck--1.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "CREATE EXTENSION amcheck" to load this file. \quit
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_index_check()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_index_check(index regclass)
|
||||||
|
RETURNS VOID
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_index_check'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_index_parent_check()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_index_parent_check(index regclass)
|
||||||
|
RETURNS VOID
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_index_parent_check'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
||||||
|
|
||||||
|
-- Don't want these to be available to public
|
||||||
|
REVOKE ALL ON FUNCTION bt_index_check(regclass) FROM PUBLIC;
|
||||||
|
REVOKE ALL ON FUNCTION bt_index_parent_check(regclass) FROM PUBLIC;
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* contrib/amcheck/amcheck--1.1--1.2.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION amcheck UPDATE TO '1.2'" to load this file. \quit
|
||||||
|
|
||||||
|
-- In order to avoid issues with dependencies when updating amcheck to 1.2,
|
||||||
|
-- create new, overloaded version of the 1.1 function signature
|
||||||
|
|
||||||
|
--
|
||||||
|
-- bt_index_parent_check()
|
||||||
|
--
|
||||||
|
CREATE FUNCTION bt_index_parent_check(index regclass,
|
||||||
|
heapallindexed boolean, rootdescend boolean)
|
||||||
|
RETURNS VOID
|
||||||
|
AS 'MODULE_PATHNAME', 'bt_index_parent_check'
|
||||||
|
LANGUAGE C STRICT PARALLEL RESTRICTED;
|
||||||
|
|
||||||
|
-- Don't want this to be available to public
|
||||||
|
REVOKE ALL ON FUNCTION bt_index_parent_check(regclass, boolean, boolean) FROM PUBLIC;
|
|
@ -0,0 +1,5 @@
|
||||||
|
# amcheck extension
|
||||||
|
comment = 'functions for verifying relation integrity'
|
||||||
|
default_version = '1.2'
|
||||||
|
module_pathname = '$libdir/amcheck'
|
||||||
|
relocatable = true
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE EXTENSION amcheck;
|
|
@ -0,0 +1,170 @@
|
||||||
|
CREATE TABLE bttest_a(id int8);
|
||||||
|
CREATE TABLE bttest_b(id int8);
|
||||||
|
CREATE TABLE bttest_multi(id int8, data int8);
|
||||||
|
CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint);
|
||||||
|
-- Stabalize tests
|
||||||
|
ALTER TABLE bttest_a SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE bttest_b SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE bttest_multi SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE delete_test_table SET (autovacuum_enabled = false);
|
||||||
|
INSERT INTO bttest_a SELECT * FROM generate_series(1, 100000);
|
||||||
|
INSERT INTO bttest_b SELECT * FROM generate_series(100000, 1, -1);
|
||||||
|
INSERT INTO bttest_multi SELECT i, i%2 FROM generate_series(1, 100000) as i;
|
||||||
|
CREATE INDEX bttest_a_idx ON bttest_a USING btree (id);
|
||||||
|
CREATE INDEX bttest_b_idx ON bttest_b USING btree (id);
|
||||||
|
CREATE UNIQUE INDEX bttest_multi_idx ON bttest_multi
|
||||||
|
USING btree (id) INCLUDE (data);
|
||||||
|
CREATE ROLE regress_bttest_role;
|
||||||
|
-- verify permissions are checked (error due to function not callable)
|
||||||
|
SET ROLE regress_bttest_role;
|
||||||
|
SELECT bt_index_check('bttest_a_idx'::regclass);
|
||||||
|
ERROR: permission denied for function bt_index_check
|
||||||
|
SELECT bt_index_parent_check('bttest_a_idx'::regclass);
|
||||||
|
ERROR: permission denied for function bt_index_parent_check
|
||||||
|
RESET ROLE;
|
||||||
|
-- we, intentionally, don't check relation permissions - it's useful
|
||||||
|
-- to run this cluster-wide with a restricted account, and as tested
|
||||||
|
-- above explicit permission has to be granted for that.
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass, boolean) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass, boolean) TO regress_bttest_role;
|
||||||
|
SET ROLE regress_bttest_role;
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT bt_index_parent_check('bttest_a_idx');
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
RESET ROLE;
|
||||||
|
-- verify plain tables are rejected (error)
|
||||||
|
SELECT bt_index_check('bttest_a');
|
||||||
|
ERROR: "bttest_a" is not an index
|
||||||
|
SELECT bt_index_parent_check('bttest_a');
|
||||||
|
ERROR: "bttest_a" is not an index
|
||||||
|
-- verify non-existing indexes are rejected (error)
|
||||||
|
SELECT bt_index_check(17);
|
||||||
|
ERROR: could not open relation with OID 17
|
||||||
|
SELECT bt_index_parent_check(17);
|
||||||
|
ERROR: could not open relation with OID 17
|
||||||
|
-- verify wrong index types are rejected (error)
|
||||||
|
BEGIN;
|
||||||
|
CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
|
||||||
|
SELECT bt_index_parent_check('bttest_a_brin_idx');
|
||||||
|
ERROR: only B-Tree indexes are supported as targets for verification
|
||||||
|
DETAIL: Relation "bttest_a_brin_idx" is not a B-Tree index.
|
||||||
|
ROLLBACK;
|
||||||
|
-- normal check outside of xact
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- more expansive tests
|
||||||
|
SELECT bt_index_check('bttest_a_idx', true);
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT bt_index_parent_check('bttest_b_idx', true);
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT bt_index_parent_check('bttest_b_idx');
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- make sure we don't have any leftover locks
|
||||||
|
SELECT * FROM pg_locks
|
||||||
|
WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[])
|
||||||
|
AND pid = pg_backend_pid();
|
||||||
|
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid | mode | granted | fastpath
|
||||||
|
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----+------+---------+----------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
-- normal check outside of xact for index with included columns
|
||||||
|
SELECT bt_index_check('bttest_multi_idx');
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- more expansive tests for index with included columns
|
||||||
|
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- repeat expansive tests for index built using insertions
|
||||||
|
TRUNCATE bttest_multi;
|
||||||
|
INSERT INTO bttest_multi SELECT i, i%2 FROM generate_series(1, 100000) as i;
|
||||||
|
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test for multilevel page deletion/downlink present checks, and rootdescend
|
||||||
|
-- checks
|
||||||
|
--
|
||||||
|
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i;
|
||||||
|
ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d);
|
||||||
|
-- Delete most entries, and vacuum, deleting internal pages and creating "fast
|
||||||
|
-- root"
|
||||||
|
DELETE FROM delete_test_table WHERE a < 79990;
|
||||||
|
VACUUM delete_test_table;
|
||||||
|
SELECT bt_index_parent_check('delete_test_table_pkey', true);
|
||||||
|
bt_index_parent_check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- BUG #15597: must not assume consistent input toasting state when forming
|
||||||
|
-- tuple. Bloom filter must fingerprint normalized index tuple representation.
|
||||||
|
--
|
||||||
|
CREATE TABLE toast_bug(buggy text);
|
||||||
|
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE plain;
|
||||||
|
-- pg_attribute entry for toasty.buggy will have plain storage:
|
||||||
|
CREATE INDEX toasty ON toast_bug(buggy);
|
||||||
|
-- Whereas pg_attribute entry for toast_bug.buggy now has extended storage:
|
||||||
|
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE extended;
|
||||||
|
-- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
|
||||||
|
INSERT INTO toast_bug SELECT repeat('a', 2200);
|
||||||
|
-- Should not get false positive report of corruption:
|
||||||
|
SELECT bt_index_check('toasty', true);
|
||||||
|
bt_index_check
|
||||||
|
----------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- cleanup
|
||||||
|
DROP TABLE bttest_a;
|
||||||
|
DROP TABLE bttest_b;
|
||||||
|
DROP TABLE bttest_multi;
|
||||||
|
DROP TABLE delete_test_table;
|
||||||
|
DROP TABLE toast_bug;
|
||||||
|
DROP OWNED BY regress_bttest_role; -- permissions
|
||||||
|
DROP ROLE regress_bttest_role;
|
|
@ -0,0 +1 @@
|
||||||
|
CREATE EXTENSION amcheck;
|
|
@ -0,0 +1,114 @@
|
||||||
|
CREATE TABLE bttest_a(id int8);
|
||||||
|
CREATE TABLE bttest_b(id int8);
|
||||||
|
CREATE TABLE bttest_multi(id int8, data int8);
|
||||||
|
CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint);
|
||||||
|
|
||||||
|
-- Stabalize tests
|
||||||
|
ALTER TABLE bttest_a SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE bttest_b SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE bttest_multi SET (autovacuum_enabled = false);
|
||||||
|
ALTER TABLE delete_test_table SET (autovacuum_enabled = false);
|
||||||
|
|
||||||
|
INSERT INTO bttest_a SELECT * FROM generate_series(1, 100000);
|
||||||
|
INSERT INTO bttest_b SELECT * FROM generate_series(100000, 1, -1);
|
||||||
|
INSERT INTO bttest_multi SELECT i, i%2 FROM generate_series(1, 100000) as i;
|
||||||
|
|
||||||
|
CREATE INDEX bttest_a_idx ON bttest_a USING btree (id);
|
||||||
|
CREATE INDEX bttest_b_idx ON bttest_b USING btree (id);
|
||||||
|
CREATE UNIQUE INDEX bttest_multi_idx ON bttest_multi
|
||||||
|
USING btree (id) INCLUDE (data);
|
||||||
|
|
||||||
|
CREATE ROLE regress_bttest_role;
|
||||||
|
|
||||||
|
-- verify permissions are checked (error due to function not callable)
|
||||||
|
SET ROLE regress_bttest_role;
|
||||||
|
SELECT bt_index_check('bttest_a_idx'::regclass);
|
||||||
|
SELECT bt_index_parent_check('bttest_a_idx'::regclass);
|
||||||
|
RESET ROLE;
|
||||||
|
|
||||||
|
-- we, intentionally, don't check relation permissions - it's useful
|
||||||
|
-- to run this cluster-wide with a restricted account, and as tested
|
||||||
|
-- above explicit permission has to be granted for that.
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_check(regclass, boolean) TO regress_bttest_role;
|
||||||
|
GRANT EXECUTE ON FUNCTION bt_index_parent_check(regclass, boolean) TO regress_bttest_role;
|
||||||
|
SET ROLE regress_bttest_role;
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
SELECT bt_index_parent_check('bttest_a_idx');
|
||||||
|
RESET ROLE;
|
||||||
|
|
||||||
|
-- verify plain tables are rejected (error)
|
||||||
|
SELECT bt_index_check('bttest_a');
|
||||||
|
SELECT bt_index_parent_check('bttest_a');
|
||||||
|
|
||||||
|
-- verify non-existing indexes are rejected (error)
|
||||||
|
SELECT bt_index_check(17);
|
||||||
|
SELECT bt_index_parent_check(17);
|
||||||
|
|
||||||
|
-- verify wrong index types are rejected (error)
|
||||||
|
BEGIN;
|
||||||
|
CREATE INDEX bttest_a_brin_idx ON bttest_a USING brin(id);
|
||||||
|
SELECT bt_index_parent_check('bttest_a_brin_idx');
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- normal check outside of xact
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
-- more expansive tests
|
||||||
|
SELECT bt_index_check('bttest_a_idx', true);
|
||||||
|
SELECT bt_index_parent_check('bttest_b_idx', true);
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SELECT bt_index_check('bttest_a_idx');
|
||||||
|
SELECT bt_index_parent_check('bttest_b_idx');
|
||||||
|
-- make sure we don't have any leftover locks
|
||||||
|
SELECT * FROM pg_locks
|
||||||
|
WHERE relation = ANY(ARRAY['bttest_a', 'bttest_a_idx', 'bttest_b', 'bttest_b_idx']::regclass[])
|
||||||
|
AND pid = pg_backend_pid();
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
-- normal check outside of xact for index with included columns
|
||||||
|
SELECT bt_index_check('bttest_multi_idx');
|
||||||
|
-- more expansive tests for index with included columns
|
||||||
|
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
|
||||||
|
|
||||||
|
-- repeat expansive tests for index built using insertions
|
||||||
|
TRUNCATE bttest_multi;
|
||||||
|
INSERT INTO bttest_multi SELECT i, i%2 FROM generate_series(1, 100000) as i;
|
||||||
|
SELECT bt_index_parent_check('bttest_multi_idx', true, true);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Test for multilevel page deletion/downlink present checks, and rootdescend
|
||||||
|
-- checks
|
||||||
|
--
|
||||||
|
INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i;
|
||||||
|
ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d);
|
||||||
|
-- Delete most entries, and vacuum, deleting internal pages and creating "fast
|
||||||
|
-- root"
|
||||||
|
DELETE FROM delete_test_table WHERE a < 79990;
|
||||||
|
VACUUM delete_test_table;
|
||||||
|
SELECT bt_index_parent_check('delete_test_table_pkey', true);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- BUG #15597: must not assume consistent input toasting state when forming
|
||||||
|
-- tuple. Bloom filter must fingerprint normalized index tuple representation.
|
||||||
|
--
|
||||||
|
CREATE TABLE toast_bug(buggy text);
|
||||||
|
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE plain;
|
||||||
|
-- pg_attribute entry for toasty.buggy will have plain storage:
|
||||||
|
CREATE INDEX toasty ON toast_bug(buggy);
|
||||||
|
-- Whereas pg_attribute entry for toast_bug.buggy now has extended storage:
|
||||||
|
ALTER TABLE toast_bug ALTER COLUMN buggy SET STORAGE extended;
|
||||||
|
-- Insert compressible heap tuple (comfortably exceeds TOAST_TUPLE_THRESHOLD):
|
||||||
|
INSERT INTO toast_bug SELECT repeat('a', 2200);
|
||||||
|
-- Should not get false positive report of corruption:
|
||||||
|
SELECT bt_index_check('toasty', true);
|
||||||
|
|
||||||
|
-- cleanup
|
||||||
|
DROP TABLE bttest_a;
|
||||||
|
DROP TABLE bttest_b;
|
||||||
|
DROP TABLE bttest_multi;
|
||||||
|
DROP TABLE delete_test_table;
|
||||||
|
DROP TABLE toast_bug;
|
||||||
|
DROP OWNED BY regress_bttest_role; -- permissions
|
||||||
|
DROP ROLE regress_bttest_role;
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
||||||
|
# contrib/auth_delay/Makefile
|
||||||
|
|
||||||
|
MODULES = auth_delay
|
||||||
|
PGFILEDESC = "auth_delay - delay authentication failure reports"
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/auth_delay
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* -------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* auth_delay.c
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/auth_delay/auth_delay.c
|
||||||
|
*
|
||||||
|
* -------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "libpq/auth.h"
|
||||||
|
#include "port.h"
|
||||||
|
#include "utils/guc.h"
|
||||||
|
#include "utils/timestamp.h"
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
void _PG_init(void);
|
||||||
|
|
||||||
|
/* GUC Variables */
|
||||||
|
static int auth_delay_milliseconds;
|
||||||
|
|
||||||
|
/* Original Hook */
|
||||||
|
static ClientAuthentication_hook_type original_client_auth_hook = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check authentication
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
auth_delay_checks(Port *port, int status)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Any other plugins which use ClientAuthentication_hook.
|
||||||
|
*/
|
||||||
|
if (original_client_auth_hook)
|
||||||
|
original_client_auth_hook(port, status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inject a short delay if authentication failed.
|
||||||
|
*/
|
||||||
|
if (status != STATUS_OK)
|
||||||
|
{
|
||||||
|
pg_usleep(1000L * auth_delay_milliseconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module Load Callback
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PG_init(void)
|
||||||
|
{
|
||||||
|
/* Define custom GUC variables */
|
||||||
|
DefineCustomIntVariable("auth_delay.milliseconds",
|
||||||
|
"Milliseconds to delay before reporting authentication failure",
|
||||||
|
NULL,
|
||||||
|
&auth_delay_milliseconds,
|
||||||
|
0,
|
||||||
|
0, INT_MAX / 1000,
|
||||||
|
PGC_SIGHUP,
|
||||||
|
GUC_UNIT_MS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
/* Install Hooks */
|
||||||
|
original_client_auth_hook = ClientAuthentication_hook;
|
||||||
|
ClientAuthentication_hook = auth_delay_checks;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
# contrib/auto_explain/Makefile
|
||||||
|
|
||||||
|
MODULE_big = auto_explain
|
||||||
|
OBJS = auto_explain.o $(WIN32RES)
|
||||||
|
PGFILEDESC = "auto_explain - logging facility for execution plans"
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/auto_explain
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,425 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* auto_explain.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/auto_explain/auto_explain.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "access/parallel.h"
|
||||||
|
#include "commands/explain.h"
|
||||||
|
#include "executor/instrument.h"
|
||||||
|
#include "jit/jit.h"
|
||||||
|
#include "utils/guc.h"
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
/* GUC variables */
|
||||||
|
static int auto_explain_log_min_duration = -1; /* msec or -1 */
|
||||||
|
static bool auto_explain_log_analyze = false;
|
||||||
|
static bool auto_explain_log_verbose = false;
|
||||||
|
static bool auto_explain_log_buffers = false;
|
||||||
|
static bool auto_explain_log_triggers = false;
|
||||||
|
static bool auto_explain_log_timing = true;
|
||||||
|
static bool auto_explain_log_settings = false;
|
||||||
|
static int auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
|
||||||
|
static int auto_explain_log_level = LOG;
|
||||||
|
static bool auto_explain_log_nested_statements = false;
|
||||||
|
static double auto_explain_sample_rate = 1;
|
||||||
|
|
||||||
|
static const struct config_enum_entry format_options[] = {
|
||||||
|
{"text", EXPLAIN_FORMAT_TEXT, false},
|
||||||
|
{"xml", EXPLAIN_FORMAT_XML, false},
|
||||||
|
{"json", EXPLAIN_FORMAT_JSON, false},
|
||||||
|
{"yaml", EXPLAIN_FORMAT_YAML, false},
|
||||||
|
{NULL, 0, false}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct config_enum_entry loglevel_options[] = {
|
||||||
|
{"debug5", DEBUG5, false},
|
||||||
|
{"debug4", DEBUG4, false},
|
||||||
|
{"debug3", DEBUG3, false},
|
||||||
|
{"debug2", DEBUG2, false},
|
||||||
|
{"debug1", DEBUG1, false},
|
||||||
|
{"debug", DEBUG2, true},
|
||||||
|
{"info", INFO, false},
|
||||||
|
{"notice", NOTICE, false},
|
||||||
|
{"warning", WARNING, false},
|
||||||
|
{"log", LOG, false},
|
||||||
|
{NULL, 0, false}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Current nesting depth of ExecutorRun calls */
|
||||||
|
static int nesting_level = 0;
|
||||||
|
|
||||||
|
/* Is the current top-level query to be sampled? */
|
||||||
|
static bool current_query_sampled = false;
|
||||||
|
|
||||||
|
#define auto_explain_enabled() \
|
||||||
|
(auto_explain_log_min_duration >= 0 && \
|
||||||
|
(nesting_level == 0 || auto_explain_log_nested_statements) && \
|
||||||
|
current_query_sampled)
|
||||||
|
|
||||||
|
/* Saved hook values in case of unload */
|
||||||
|
static ExecutorStart_hook_type prev_ExecutorStart = NULL;
|
||||||
|
static ExecutorRun_hook_type prev_ExecutorRun = NULL;
|
||||||
|
static ExecutorFinish_hook_type prev_ExecutorFinish = NULL;
|
||||||
|
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
|
||||||
|
|
||||||
|
void _PG_init(void);
|
||||||
|
void _PG_fini(void);
|
||||||
|
|
||||||
|
static void explain_ExecutorStart(QueryDesc *queryDesc, int eflags);
|
||||||
|
static void explain_ExecutorRun(QueryDesc *queryDesc,
|
||||||
|
ScanDirection direction,
|
||||||
|
uint64 count, bool execute_once);
|
||||||
|
static void explain_ExecutorFinish(QueryDesc *queryDesc);
|
||||||
|
static void explain_ExecutorEnd(QueryDesc *queryDesc);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module load callback
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PG_init(void)
|
||||||
|
{
|
||||||
|
/* Define custom GUC variables. */
|
||||||
|
DefineCustomIntVariable("auto_explain.log_min_duration",
|
||||||
|
"Sets the minimum execution time above which plans will be logged.",
|
||||||
|
"Zero prints all plans. -1 turns this feature off.",
|
||||||
|
&auto_explain_log_min_duration,
|
||||||
|
-1,
|
||||||
|
-1, INT_MAX,
|
||||||
|
PGC_SUSET,
|
||||||
|
GUC_UNIT_MS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_analyze",
|
||||||
|
"Use EXPLAIN ANALYZE for plan logging.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_analyze,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_settings",
|
||||||
|
"Log modified configuration parameters affecting query planning.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_settings,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_verbose",
|
||||||
|
"Use EXPLAIN VERBOSE for plan logging.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_verbose,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_buffers",
|
||||||
|
"Log buffers usage.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_buffers,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_triggers",
|
||||||
|
"Include trigger statistics in plans.",
|
||||||
|
"This has no effect unless log_analyze is also set.",
|
||||||
|
&auto_explain_log_triggers,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomEnumVariable("auto_explain.log_format",
|
||||||
|
"EXPLAIN format to be used for plan logging.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_format,
|
||||||
|
EXPLAIN_FORMAT_TEXT,
|
||||||
|
format_options,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomEnumVariable("auto_explain.log_level",
|
||||||
|
"Log level for the plan.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_level,
|
||||||
|
LOG,
|
||||||
|
loglevel_options,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_nested_statements",
|
||||||
|
"Log nested statements.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_nested_statements,
|
||||||
|
false,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomBoolVariable("auto_explain.log_timing",
|
||||||
|
"Collect timing data, not just row counts.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_log_timing,
|
||||||
|
true,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DefineCustomRealVariable("auto_explain.sample_rate",
|
||||||
|
"Fraction of queries to process.",
|
||||||
|
NULL,
|
||||||
|
&auto_explain_sample_rate,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
PGC_SUSET,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
EmitWarningsOnPlaceholders("auto_explain");
|
||||||
|
|
||||||
|
/* Install hooks. */
|
||||||
|
prev_ExecutorStart = ExecutorStart_hook;
|
||||||
|
ExecutorStart_hook = explain_ExecutorStart;
|
||||||
|
prev_ExecutorRun = ExecutorRun_hook;
|
||||||
|
ExecutorRun_hook = explain_ExecutorRun;
|
||||||
|
prev_ExecutorFinish = ExecutorFinish_hook;
|
||||||
|
ExecutorFinish_hook = explain_ExecutorFinish;
|
||||||
|
prev_ExecutorEnd = ExecutorEnd_hook;
|
||||||
|
ExecutorEnd_hook = explain_ExecutorEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module unload callback
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PG_fini(void)
|
||||||
|
{
|
||||||
|
/* Uninstall hooks. */
|
||||||
|
ExecutorStart_hook = prev_ExecutorStart;
|
||||||
|
ExecutorRun_hook = prev_ExecutorRun;
|
||||||
|
ExecutorFinish_hook = prev_ExecutorFinish;
|
||||||
|
ExecutorEnd_hook = prev_ExecutorEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecutorStart hook: start up logging if needed
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* At the beginning of each top-level statement, decide whether we'll
|
||||||
|
* sample this statement. If nested-statement explaining is enabled,
|
||||||
|
* either all nested statements will be explained or none will.
|
||||||
|
*
|
||||||
|
* When in a parallel worker, we should do nothing, which we can implement
|
||||||
|
* cheaply by pretending we decided not to sample the current statement.
|
||||||
|
* If EXPLAIN is active in the parent session, data will be collected and
|
||||||
|
* reported back to the parent, and it's no business of ours to interfere.
|
||||||
|
*/
|
||||||
|
if (nesting_level == 0)
|
||||||
|
{
|
||||||
|
if (auto_explain_log_min_duration >= 0 && !IsParallelWorker())
|
||||||
|
current_query_sampled = (random() < auto_explain_sample_rate *
|
||||||
|
((double) MAX_RANDOM_VALUE + 1));
|
||||||
|
else
|
||||||
|
current_query_sampled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto_explain_enabled())
|
||||||
|
{
|
||||||
|
/* Enable per-node instrumentation iff log_analyze is required. */
|
||||||
|
if (auto_explain_log_analyze && (eflags & EXEC_FLAG_EXPLAIN_ONLY) == 0)
|
||||||
|
{
|
||||||
|
if (auto_explain_log_timing)
|
||||||
|
queryDesc->instrument_options |= INSTRUMENT_TIMER;
|
||||||
|
else
|
||||||
|
queryDesc->instrument_options |= INSTRUMENT_ROWS;
|
||||||
|
if (auto_explain_log_buffers)
|
||||||
|
queryDesc->instrument_options |= INSTRUMENT_BUFFERS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_ExecutorStart)
|
||||||
|
prev_ExecutorStart(queryDesc, eflags);
|
||||||
|
else
|
||||||
|
standard_ExecutorStart(queryDesc, eflags);
|
||||||
|
|
||||||
|
if (auto_explain_enabled())
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Set up to track total elapsed time in ExecutorRun. Make sure the
|
||||||
|
* space is allocated in the per-query context so it will go away at
|
||||||
|
* ExecutorEnd.
|
||||||
|
*/
|
||||||
|
if (queryDesc->totaltime == NULL)
|
||||||
|
{
|
||||||
|
MemoryContext oldcxt;
|
||||||
|
|
||||||
|
oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
|
||||||
|
queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL);
|
||||||
|
MemoryContextSwitchTo(oldcxt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecutorRun hook: all we need do is track nesting depth
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
explain_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction,
|
||||||
|
uint64 count, bool execute_once)
|
||||||
|
{
|
||||||
|
nesting_level++;
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
if (prev_ExecutorRun)
|
||||||
|
prev_ExecutorRun(queryDesc, direction, count, execute_once);
|
||||||
|
else
|
||||||
|
standard_ExecutorRun(queryDesc, direction, count, execute_once);
|
||||||
|
nesting_level--;
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
nesting_level--;
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecutorFinish hook: all we need do is track nesting depth
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
explain_ExecutorFinish(QueryDesc *queryDesc)
|
||||||
|
{
|
||||||
|
nesting_level++;
|
||||||
|
PG_TRY();
|
||||||
|
{
|
||||||
|
if (prev_ExecutorFinish)
|
||||||
|
prev_ExecutorFinish(queryDesc);
|
||||||
|
else
|
||||||
|
standard_ExecutorFinish(queryDesc);
|
||||||
|
nesting_level--;
|
||||||
|
}
|
||||||
|
PG_CATCH();
|
||||||
|
{
|
||||||
|
nesting_level--;
|
||||||
|
PG_RE_THROW();
|
||||||
|
}
|
||||||
|
PG_END_TRY();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ExecutorEnd hook: log results if needed
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
explain_ExecutorEnd(QueryDesc *queryDesc)
|
||||||
|
{
|
||||||
|
if (queryDesc->totaltime && auto_explain_enabled())
|
||||||
|
{
|
||||||
|
double msec;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure stats accumulation is done. (Note: it's okay if several
|
||||||
|
* levels of hook all do this.)
|
||||||
|
*/
|
||||||
|
InstrEndLoop(queryDesc->totaltime);
|
||||||
|
|
||||||
|
/* Log plan if duration is exceeded. */
|
||||||
|
msec = queryDesc->totaltime->total * 1000.0;
|
||||||
|
if (msec >= auto_explain_log_min_duration)
|
||||||
|
{
|
||||||
|
ExplainState *es = NewExplainState();
|
||||||
|
|
||||||
|
es->analyze = (queryDesc->instrument_options && auto_explain_log_analyze);
|
||||||
|
es->verbose = auto_explain_log_verbose;
|
||||||
|
es->buffers = (es->analyze && auto_explain_log_buffers);
|
||||||
|
es->timing = (es->analyze && auto_explain_log_timing);
|
||||||
|
es->summary = es->analyze;
|
||||||
|
es->format = auto_explain_log_format;
|
||||||
|
es->settings = auto_explain_log_settings;
|
||||||
|
|
||||||
|
ExplainBeginOutput(es);
|
||||||
|
ExplainQueryText(es, queryDesc);
|
||||||
|
ExplainPrintPlan(es, queryDesc);
|
||||||
|
if (es->analyze && auto_explain_log_triggers)
|
||||||
|
ExplainPrintTriggers(es, queryDesc);
|
||||||
|
if (es->costs)
|
||||||
|
ExplainPrintJITSummary(es, queryDesc);
|
||||||
|
ExplainEndOutput(es);
|
||||||
|
|
||||||
|
/* Remove last line break */
|
||||||
|
if (es->str->len > 0 && es->str->data[es->str->len - 1] == '\n')
|
||||||
|
es->str->data[--es->str->len] = '\0';
|
||||||
|
|
||||||
|
/* Fix JSON to output an object */
|
||||||
|
if (auto_explain_log_format == EXPLAIN_FORMAT_JSON)
|
||||||
|
{
|
||||||
|
es->str->data[0] = '{';
|
||||||
|
es->str->data[es->str->len - 1] = '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note: we rely on the existing logging of context or
|
||||||
|
* debug_query_string to identify just which statement is being
|
||||||
|
* reported. This isn't ideal but trying to do it here would
|
||||||
|
* often result in duplication.
|
||||||
|
*/
|
||||||
|
ereport(auto_explain_log_level,
|
||||||
|
(errmsg("duration: %.3f ms plan:\n%s",
|
||||||
|
msec, es->str->data),
|
||||||
|
errhidestmt(true)));
|
||||||
|
|
||||||
|
pfree(es->str->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_ExecutorEnd)
|
||||||
|
prev_ExecutorEnd(queryDesc);
|
||||||
|
else
|
||||||
|
standard_ExecutorEnd(queryDesc);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Generated subdirectories
|
||||||
|
/log/
|
||||||
|
/results/
|
||||||
|
/tmp_check/
|
|
@ -0,0 +1,25 @@
|
||||||
|
# contrib/bloom/Makefile
|
||||||
|
|
||||||
|
MODULE_big = bloom
|
||||||
|
OBJS = blcost.o blinsert.o blscan.o blutils.o blvacuum.o blvalidate.o $(WIN32RES)
|
||||||
|
|
||||||
|
EXTENSION = bloom
|
||||||
|
DATA = bloom--1.0.sql
|
||||||
|
PGFILEDESC = "bloom access method - signature file based index"
|
||||||
|
|
||||||
|
REGRESS = bloom
|
||||||
|
|
||||||
|
# Disable TAP tests for this module for now, as these are unstable on several
|
||||||
|
# buildfarm environments.
|
||||||
|
# TAP_TESTS = 1
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/bloom
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blcost.c
|
||||||
|
* Cost estimate function for bloom indexes.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blcost.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "utils/selfuncs.h"
|
||||||
|
|
||||||
|
#include "bloom.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Estimate cost of bloom index scan.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
blcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
|
||||||
|
Cost *indexStartupCost, Cost *indexTotalCost,
|
||||||
|
Selectivity *indexSelectivity, double *indexCorrelation,
|
||||||
|
double *indexPages)
|
||||||
|
{
|
||||||
|
IndexOptInfo *index = path->indexinfo;
|
||||||
|
GenericCosts costs;
|
||||||
|
|
||||||
|
MemSet(&costs, 0, sizeof(costs));
|
||||||
|
|
||||||
|
/* We have to visit all index tuples anyway */
|
||||||
|
costs.numIndexTuples = index->tuples;
|
||||||
|
|
||||||
|
/* Use generic estimate */
|
||||||
|
genericcostestimate(root, path, loop_count, &costs);
|
||||||
|
|
||||||
|
*indexStartupCost = costs.indexStartupCost;
|
||||||
|
*indexTotalCost = costs.indexTotalCost;
|
||||||
|
*indexSelectivity = costs.indexSelectivity;
|
||||||
|
*indexCorrelation = costs.indexCorrelation;
|
||||||
|
*indexPages = costs.numIndexPages;
|
||||||
|
}
|
|
@ -0,0 +1,367 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blinsert.c
|
||||||
|
* Bloom index build and insert functions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blinsert.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "access/generic_xlog.h"
|
||||||
|
#include "access/tableam.h"
|
||||||
|
#include "catalog/index.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "storage/bufmgr.h"
|
||||||
|
#include "storage/indexfsm.h"
|
||||||
|
#include "storage/smgr.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
#include "bloom.h"
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* State of bloom index build. We accumulate one page data here before
|
||||||
|
* flushing it to buffer manager.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
BloomState blstate; /* bloom index state */
|
||||||
|
int64 indtuples; /* total number of tuples indexed */
|
||||||
|
MemoryContext tmpCtx; /* temporary memory context reset after each
|
||||||
|
* tuple */
|
||||||
|
PGAlignedBlock data; /* cached page */
|
||||||
|
int count; /* number of tuples in cached page */
|
||||||
|
} BloomBuildState;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush page cached in BloomBuildState.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
flushCachedPage(Relation index, BloomBuildState *buildstate)
|
||||||
|
{
|
||||||
|
Page page;
|
||||||
|
Buffer buffer = BloomNewBuffer(index);
|
||||||
|
GenericXLogState *state;
|
||||||
|
|
||||||
|
state = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
|
||||||
|
memcpy(page, buildstate->data.data, BLCKSZ);
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (Re)initialize cached page in BloomBuildState.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
initCachedPage(BloomBuildState *buildstate)
|
||||||
|
{
|
||||||
|
memset(buildstate->data.data, 0, BLCKSZ);
|
||||||
|
BloomInitPage(buildstate->data.data, 0);
|
||||||
|
buildstate->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-tuple callback for table_index_build_scan.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
|
||||||
|
bool *isnull, bool tupleIsAlive, void *state)
|
||||||
|
{
|
||||||
|
BloomBuildState *buildstate = (BloomBuildState *) state;
|
||||||
|
MemoryContext oldCtx;
|
||||||
|
BloomTuple *itup;
|
||||||
|
|
||||||
|
oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
|
||||||
|
|
||||||
|
itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
|
||||||
|
|
||||||
|
/* Try to add next item to cached page */
|
||||||
|
if (BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
|
||||||
|
{
|
||||||
|
/* Next item was added successfully */
|
||||||
|
buildstate->count++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Cached page is full, flush it out and make a new one */
|
||||||
|
flushCachedPage(index, buildstate);
|
||||||
|
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
|
initCachedPage(buildstate);
|
||||||
|
|
||||||
|
if (!BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
|
||||||
|
{
|
||||||
|
/* We shouldn't be here since we're inserting to the empty page */
|
||||||
|
elog(ERROR, "could not add new bloom tuple to empty page");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Next item was added successfully */
|
||||||
|
buildstate->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update total tuple count */
|
||||||
|
buildstate->indtuples += 1;
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextReset(buildstate->tmpCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build a new bloom index.
|
||||||
|
*/
|
||||||
|
IndexBuildResult *
|
||||||
|
blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
|
||||||
|
{
|
||||||
|
IndexBuildResult *result;
|
||||||
|
double reltuples;
|
||||||
|
BloomBuildState buildstate;
|
||||||
|
|
||||||
|
if (RelationGetNumberOfBlocks(index) != 0)
|
||||||
|
elog(ERROR, "index \"%s\" already contains data",
|
||||||
|
RelationGetRelationName(index));
|
||||||
|
|
||||||
|
/* Initialize the meta page */
|
||||||
|
BloomInitMetapage(index);
|
||||||
|
|
||||||
|
/* Initialize the bloom build state */
|
||||||
|
memset(&buildstate, 0, sizeof(buildstate));
|
||||||
|
initBloomState(&buildstate.blstate, index);
|
||||||
|
buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"Bloom build temporary context",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
initCachedPage(&buildstate);
|
||||||
|
|
||||||
|
/* Do the heap scan */
|
||||||
|
reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
|
||||||
|
bloomBuildCallback, (void *) &buildstate,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Flush last page if needed (it will be, unless heap was empty) */
|
||||||
|
if (buildstate.count > 0)
|
||||||
|
flushCachedPage(index, &buildstate);
|
||||||
|
|
||||||
|
MemoryContextDelete(buildstate.tmpCtx);
|
||||||
|
|
||||||
|
result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
|
||||||
|
result->heap_tuples = reltuples;
|
||||||
|
result->index_tuples = buildstate.indtuples;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build an empty bloom index in the initialization fork.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
blbuildempty(Relation index)
|
||||||
|
{
|
||||||
|
Page metapage;
|
||||||
|
|
||||||
|
/* Construct metapage. */
|
||||||
|
metapage = (Page) palloc(BLCKSZ);
|
||||||
|
BloomFillMetapage(index, metapage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write the page and log it. It might seem that an immediate sync would
|
||||||
|
* be sufficient to guarantee that the file exists on disk, but recovery
|
||||||
|
* itself might remove it while replaying, for example, an
|
||||||
|
* XLOG_DBASE_CREATE or XLOG_TBLSPC_CREATE record. Therefore, we need
|
||||||
|
* this even when wal_level=minimal.
|
||||||
|
*/
|
||||||
|
PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
|
||||||
|
smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
|
||||||
|
(char *) metapage, true);
|
||||||
|
log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
|
||||||
|
BLOOM_METAPAGE_BLKNO, metapage, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An immediate sync is required even if we xlog'd the page, because the
|
||||||
|
* write did not go through shared_buffers and therefore a concurrent
|
||||||
|
* checkpoint may have moved the redo pointer past our xlog record.
|
||||||
|
*/
|
||||||
|
smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert new tuple to the bloom index.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
blinsert(Relation index, Datum *values, bool *isnull,
|
||||||
|
ItemPointer ht_ctid, Relation heapRel,
|
||||||
|
IndexUniqueCheck checkUnique,
|
||||||
|
IndexInfo *indexInfo)
|
||||||
|
{
|
||||||
|
BloomState blstate;
|
||||||
|
BloomTuple *itup;
|
||||||
|
MemoryContext oldCtx;
|
||||||
|
MemoryContext insertCtx;
|
||||||
|
BloomMetaPageData *metaData;
|
||||||
|
Buffer buffer,
|
||||||
|
metaBuffer;
|
||||||
|
Page page,
|
||||||
|
metaPage;
|
||||||
|
BlockNumber blkno = InvalidBlockNumber;
|
||||||
|
OffsetNumber nStart;
|
||||||
|
GenericXLogState *state;
|
||||||
|
|
||||||
|
insertCtx = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
|
"Bloom insert temporary context",
|
||||||
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
|
oldCtx = MemoryContextSwitchTo(insertCtx);
|
||||||
|
|
||||||
|
initBloomState(&blstate, index);
|
||||||
|
itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At first, try to insert new tuple to the first page in notFullPage
|
||||||
|
* array. If successful, we don't need to modify the meta page.
|
||||||
|
*/
|
||||||
|
metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
|
||||||
|
LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
|
||||||
|
metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
|
||||||
|
|
||||||
|
if (metaData->nEnd > metaData->nStart)
|
||||||
|
{
|
||||||
|
Page page;
|
||||||
|
|
||||||
|
blkno = metaData->notFullPage[metaData->nStart];
|
||||||
|
Assert(blkno != InvalidBlockNumber);
|
||||||
|
|
||||||
|
/* Don't hold metabuffer lock while doing insert */
|
||||||
|
LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
|
||||||
|
|
||||||
|
buffer = ReadBuffer(index, blkno);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
|
state = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buffer, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We might have found a page that was recently deleted by VACUUM. If
|
||||||
|
* so, we can reuse it, but we must reinitialize it.
|
||||||
|
*/
|
||||||
|
if (PageIsNew(page) || BloomPageIsDeleted(page))
|
||||||
|
BloomInitPage(page, 0);
|
||||||
|
|
||||||
|
if (BloomPageAddItem(&blstate, page, itup))
|
||||||
|
{
|
||||||
|
/* Success! Apply the change, clean up, and exit */
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
ReleaseBuffer(metaBuffer);
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextDelete(insertCtx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Didn't fit, must try other pages */
|
||||||
|
GenericXLogAbort(state);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No entries in notFullPage */
|
||||||
|
LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try other pages in notFullPage array. We will have to change nStart in
|
||||||
|
* metapage. Thus, grab exclusive lock on metapage.
|
||||||
|
*/
|
||||||
|
LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
|
/* nStart might have changed while we didn't have lock */
|
||||||
|
nStart = metaData->nStart;
|
||||||
|
|
||||||
|
/* Skip first page if we already tried it above */
|
||||||
|
if (nStart < metaData->nEnd &&
|
||||||
|
blkno == metaData->notFullPage[nStart])
|
||||||
|
nStart++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This loop iterates for each page we try from the notFullPage array, and
|
||||||
|
* will also initialize a GenericXLogState for the fallback case of having
|
||||||
|
* to allocate a new page.
|
||||||
|
*/
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
state = GenericXLogStart(index);
|
||||||
|
|
||||||
|
/* get modifiable copy of metapage */
|
||||||
|
metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
|
||||||
|
metaData = BloomPageGetMeta(metaPage);
|
||||||
|
|
||||||
|
if (nStart >= metaData->nEnd)
|
||||||
|
break; /* no more entries in notFullPage array */
|
||||||
|
|
||||||
|
blkno = metaData->notFullPage[nStart];
|
||||||
|
Assert(blkno != InvalidBlockNumber);
|
||||||
|
|
||||||
|
buffer = ReadBuffer(index, blkno);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
page = GenericXLogRegisterBuffer(state, buffer, 0);
|
||||||
|
|
||||||
|
/* Basically same logic as above */
|
||||||
|
if (PageIsNew(page) || BloomPageIsDeleted(page))
|
||||||
|
BloomInitPage(page, 0);
|
||||||
|
|
||||||
|
if (BloomPageAddItem(&blstate, page, itup))
|
||||||
|
{
|
||||||
|
/* Success! Apply the changes, clean up, and exit */
|
||||||
|
metaData->nStart = nStart;
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
UnlockReleaseBuffer(metaBuffer);
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextDelete(insertCtx);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Didn't fit, must try other pages */
|
||||||
|
GenericXLogAbort(state);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
nStart++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Didn't find place to insert in notFullPage array. Allocate new page.
|
||||||
|
* (XXX is it good to do this while holding ex-lock on the metapage??)
|
||||||
|
*/
|
||||||
|
buffer = BloomNewBuffer(index);
|
||||||
|
|
||||||
|
page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
|
||||||
|
BloomInitPage(page, 0);
|
||||||
|
|
||||||
|
if (!BloomPageAddItem(&blstate, page, itup))
|
||||||
|
{
|
||||||
|
/* We shouldn't be here since we're inserting to an empty page */
|
||||||
|
elog(ERROR, "could not add new bloom tuple to empty page");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset notFullPage array to contain just this new page */
|
||||||
|
metaData->nStart = 0;
|
||||||
|
metaData->nEnd = 1;
|
||||||
|
metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
|
||||||
|
|
||||||
|
/* Apply the changes, clean up, and exit */
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
UnlockReleaseBuffer(metaBuffer);
|
||||||
|
|
||||||
|
MemoryContextSwitchTo(oldCtx);
|
||||||
|
MemoryContextDelete(insertCtx);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* contrib/bloom/bloom--1.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "CREATE EXTENSION bloom" to load this file. \quit
|
||||||
|
|
||||||
|
CREATE FUNCTION blhandler(internal)
|
||||||
|
RETURNS index_am_handler
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C;
|
||||||
|
|
||||||
|
-- Access method
|
||||||
|
CREATE ACCESS METHOD bloom TYPE INDEX HANDLER blhandler;
|
||||||
|
COMMENT ON ACCESS METHOD bloom IS 'bloom index access method';
|
||||||
|
|
||||||
|
-- Opclasses
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS int4_ops
|
||||||
|
DEFAULT FOR TYPE int4 USING bloom AS
|
||||||
|
OPERATOR 1 =(int4, int4),
|
||||||
|
FUNCTION 1 hashint4(int4);
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS text_ops
|
||||||
|
DEFAULT FOR TYPE text USING bloom AS
|
||||||
|
OPERATOR 1 =(text, text),
|
||||||
|
FUNCTION 1 hashtext(text);
|
|
@ -0,0 +1,5 @@
|
||||||
|
# bloom extension
|
||||||
|
comment = 'bloom access method - signature file based index'
|
||||||
|
default_version = '1.0'
|
||||||
|
module_pathname = '$libdir/bloom'
|
||||||
|
relocatable = true
|
|
@ -0,0 +1,214 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* bloom.h
|
||||||
|
* Header for bloom index.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/bloom.h
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef _BLOOM_H_
|
||||||
|
#define _BLOOM_H_
|
||||||
|
|
||||||
|
#include "access/amapi.h"
|
||||||
|
#include "access/generic_xlog.h"
|
||||||
|
#include "access/itup.h"
|
||||||
|
#include "access/xlog.h"
|
||||||
|
#include "nodes/pathnodes.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
|
||||||
|
/* Support procedures numbers */
|
||||||
|
#define BLOOM_HASH_PROC 1
|
||||||
|
#define BLOOM_NPROC 1
|
||||||
|
|
||||||
|
/* Scan strategies */
|
||||||
|
#define BLOOM_EQUAL_STRATEGY 1
|
||||||
|
#define BLOOM_NSTRATEGIES 1
|
||||||
|
|
||||||
|
/* Opaque for bloom pages */
|
||||||
|
typedef struct BloomPageOpaqueData
|
||||||
|
{
|
||||||
|
OffsetNumber maxoff; /* number of index tuples on page */
|
||||||
|
uint16 flags; /* see bit definitions below */
|
||||||
|
uint16 unused; /* placeholder to force maxaligning of size of
|
||||||
|
* BloomPageOpaqueData and to place
|
||||||
|
* bloom_page_id exactly at the end of page */
|
||||||
|
uint16 bloom_page_id; /* for identification of BLOOM indexes */
|
||||||
|
} BloomPageOpaqueData;
|
||||||
|
|
||||||
|
typedef BloomPageOpaqueData *BloomPageOpaque;
|
||||||
|
|
||||||
|
/* Bloom page flags */
|
||||||
|
#define BLOOM_META (1<<0)
|
||||||
|
#define BLOOM_DELETED (2<<0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The page ID is for the convenience of pg_filedump and similar utilities,
|
||||||
|
* which otherwise would have a hard time telling pages of different index
|
||||||
|
* types apart. It should be the last 2 bytes on the page. This is more or
|
||||||
|
* less "free" due to alignment considerations.
|
||||||
|
*
|
||||||
|
* See comments above GinPageOpaqueData.
|
||||||
|
*/
|
||||||
|
#define BLOOM_PAGE_ID 0xFF83
|
||||||
|
|
||||||
|
/* Macros for accessing bloom page structures */
|
||||||
|
#define BloomPageGetOpaque(page) ((BloomPageOpaque) PageGetSpecialPointer(page))
|
||||||
|
#define BloomPageGetMaxOffset(page) (BloomPageGetOpaque(page)->maxoff)
|
||||||
|
#define BloomPageIsMeta(page) \
|
||||||
|
((BloomPageGetOpaque(page)->flags & BLOOM_META) != 0)
|
||||||
|
#define BloomPageIsDeleted(page) \
|
||||||
|
((BloomPageGetOpaque(page)->flags & BLOOM_DELETED) != 0)
|
||||||
|
#define BloomPageSetDeleted(page) \
|
||||||
|
(BloomPageGetOpaque(page)->flags |= BLOOM_DELETED)
|
||||||
|
#define BloomPageSetNonDeleted(page) \
|
||||||
|
(BloomPageGetOpaque(page)->flags &= ~BLOOM_DELETED)
|
||||||
|
#define BloomPageGetData(page) ((BloomTuple *)PageGetContents(page))
|
||||||
|
#define BloomPageGetTuple(state, page, offset) \
|
||||||
|
((BloomTuple *)(PageGetContents(page) \
|
||||||
|
+ (state)->sizeOfBloomTuple * ((offset) - 1)))
|
||||||
|
#define BloomPageGetNextTuple(state, tuple) \
|
||||||
|
((BloomTuple *)((Pointer)(tuple) + (state)->sizeOfBloomTuple))
|
||||||
|
|
||||||
|
/* Preserved page numbers */
|
||||||
|
#define BLOOM_METAPAGE_BLKNO (0)
|
||||||
|
#define BLOOM_HEAD_BLKNO (1) /* first data page */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We store Bloom signatures as arrays of uint16 words.
|
||||||
|
*/
|
||||||
|
typedef uint16 BloomSignatureWord;
|
||||||
|
|
||||||
|
#define SIGNWORDBITS ((int) (BITS_PER_BYTE * sizeof(BloomSignatureWord)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default and maximum Bloom signature length in bits.
|
||||||
|
*/
|
||||||
|
#define DEFAULT_BLOOM_LENGTH (5 * SIGNWORDBITS)
|
||||||
|
#define MAX_BLOOM_LENGTH (256 * SIGNWORDBITS)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default and maximum signature bits generated per index key.
|
||||||
|
*/
|
||||||
|
#define DEFAULT_BLOOM_BITS 2
|
||||||
|
#define MAX_BLOOM_BITS (MAX_BLOOM_LENGTH - 1)
|
||||||
|
|
||||||
|
/* Bloom index options */
|
||||||
|
typedef struct BloomOptions
|
||||||
|
{
|
||||||
|
int32 vl_len_; /* varlena header (do not touch directly!) */
|
||||||
|
int bloomLength; /* length of signature in words (not bits!) */
|
||||||
|
int bitSize[INDEX_MAX_KEYS]; /* # of bits generated for each
|
||||||
|
* index key */
|
||||||
|
} BloomOptions;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FreeBlockNumberArray - array of block numbers sized so that metadata fill
|
||||||
|
* all space in metapage.
|
||||||
|
*/
|
||||||
|
typedef BlockNumber FreeBlockNumberArray[
|
||||||
|
MAXALIGN_DOWN(
|
||||||
|
BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(BloomPageOpaqueData))
|
||||||
|
- MAXALIGN(sizeof(uint16) * 2 + sizeof(uint32) + sizeof(BloomOptions))
|
||||||
|
) / sizeof(BlockNumber)
|
||||||
|
];
|
||||||
|
|
||||||
|
/* Metadata of bloom index */
|
||||||
|
typedef struct BloomMetaPageData
|
||||||
|
{
|
||||||
|
uint32 magickNumber;
|
||||||
|
uint16 nStart;
|
||||||
|
uint16 nEnd;
|
||||||
|
BloomOptions opts;
|
||||||
|
FreeBlockNumberArray notFullPage;
|
||||||
|
} BloomMetaPageData;
|
||||||
|
|
||||||
|
/* Magic number to distinguish bloom pages among anothers */
|
||||||
|
#define BLOOM_MAGICK_NUMBER (0xDBAC0DED)
|
||||||
|
|
||||||
|
/* Number of blocks numbers fit in BloomMetaPageData */
|
||||||
|
#define BloomMetaBlockN (sizeof(FreeBlockNumberArray) / sizeof(BlockNumber))
|
||||||
|
|
||||||
|
#define BloomPageGetMeta(page) ((BloomMetaPageData *) PageGetContents(page))
|
||||||
|
|
||||||
|
typedef struct BloomState
|
||||||
|
{
|
||||||
|
FmgrInfo hashFn[INDEX_MAX_KEYS];
|
||||||
|
Oid collations[INDEX_MAX_KEYS];
|
||||||
|
BloomOptions opts; /* copy of options on index's metapage */
|
||||||
|
int32 nColumns;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sizeOfBloomTuple is index-specific, and it depends on reloptions, so
|
||||||
|
* precompute it
|
||||||
|
*/
|
||||||
|
Size sizeOfBloomTuple;
|
||||||
|
} BloomState;
|
||||||
|
|
||||||
|
#define BloomPageGetFreeSpace(state, page) \
|
||||||
|
(BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
|
||||||
|
- BloomPageGetMaxOffset(page) * (state)->sizeOfBloomTuple \
|
||||||
|
- MAXALIGN(sizeof(BloomPageOpaqueData)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tuples are very different from all other relations
|
||||||
|
*/
|
||||||
|
typedef struct BloomTuple
|
||||||
|
{
|
||||||
|
ItemPointerData heapPtr;
|
||||||
|
BloomSignatureWord sign[FLEXIBLE_ARRAY_MEMBER];
|
||||||
|
} BloomTuple;
|
||||||
|
|
||||||
|
#define BLOOMTUPLEHDRSZ offsetof(BloomTuple, sign)
|
||||||
|
|
||||||
|
/* Opaque data structure for bloom index scan */
|
||||||
|
typedef struct BloomScanOpaqueData
|
||||||
|
{
|
||||||
|
BloomSignatureWord *sign; /* Scan signature */
|
||||||
|
BloomState state;
|
||||||
|
} BloomScanOpaqueData;
|
||||||
|
|
||||||
|
typedef BloomScanOpaqueData *BloomScanOpaque;
|
||||||
|
|
||||||
|
/* blutils.c */
|
||||||
|
extern void _PG_init(void);
|
||||||
|
extern void initBloomState(BloomState *state, Relation index);
|
||||||
|
extern void BloomFillMetapage(Relation index, Page metaPage);
|
||||||
|
extern void BloomInitMetapage(Relation index);
|
||||||
|
extern void BloomInitPage(Page page, uint16 flags);
|
||||||
|
extern Buffer BloomNewBuffer(Relation index);
|
||||||
|
extern void signValue(BloomState *state, BloomSignatureWord *sign, Datum value, int attno);
|
||||||
|
extern BloomTuple *BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull);
|
||||||
|
extern bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple);
|
||||||
|
|
||||||
|
/* blvalidate.c */
|
||||||
|
extern bool blvalidate(Oid opclassoid);
|
||||||
|
|
||||||
|
/* index access method interface functions */
|
||||||
|
extern bool blinsert(Relation index, Datum *values, bool *isnull,
|
||||||
|
ItemPointer ht_ctid, Relation heapRel,
|
||||||
|
IndexUniqueCheck checkUnique,
|
||||||
|
struct IndexInfo *indexInfo);
|
||||||
|
extern IndexScanDesc blbeginscan(Relation r, int nkeys, int norderbys);
|
||||||
|
extern int64 blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
|
||||||
|
extern void blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
|
||||||
|
ScanKey orderbys, int norderbys);
|
||||||
|
extern void blendscan(IndexScanDesc scan);
|
||||||
|
extern IndexBuildResult *blbuild(Relation heap, Relation index,
|
||||||
|
struct IndexInfo *indexInfo);
|
||||||
|
extern void blbuildempty(Relation index);
|
||||||
|
extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
|
||||||
|
IndexBulkDeleteResult *stats, IndexBulkDeleteCallback callback,
|
||||||
|
void *callback_state);
|
||||||
|
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
|
||||||
|
IndexBulkDeleteResult *stats);
|
||||||
|
extern bytea *bloptions(Datum reloptions, bool validate);
|
||||||
|
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
|
||||||
|
double loop_count, Cost *indexStartupCost,
|
||||||
|
Cost *indexTotalCost, Selectivity *indexSelectivity,
|
||||||
|
double *indexCorrelation, double *indexPages);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blscan.c
|
||||||
|
* Bloom index scan functions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blscan.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/relscan.h"
|
||||||
|
#include "pgstat.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "storage/bufmgr.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/rel.h"
|
||||||
|
|
||||||
|
#include "bloom.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Begin scan of bloom index.
|
||||||
|
*/
|
||||||
|
IndexScanDesc
|
||||||
|
blbeginscan(Relation r, int nkeys, int norderbys)
|
||||||
|
{
|
||||||
|
IndexScanDesc scan;
|
||||||
|
BloomScanOpaque so;
|
||||||
|
|
||||||
|
scan = RelationGetIndexScan(r, nkeys, norderbys);
|
||||||
|
|
||||||
|
so = (BloomScanOpaque) palloc(sizeof(BloomScanOpaqueData));
|
||||||
|
initBloomState(&so->state, scan->indexRelation);
|
||||||
|
so->sign = NULL;
|
||||||
|
|
||||||
|
scan->opaque = so;
|
||||||
|
|
||||||
|
return scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rescan a bloom index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
blrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
|
||||||
|
ScanKey orderbys, int norderbys)
|
||||||
|
{
|
||||||
|
BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
|
||||||
|
|
||||||
|
if (so->sign)
|
||||||
|
pfree(so->sign);
|
||||||
|
so->sign = NULL;
|
||||||
|
|
||||||
|
if (scankey && scan->numberOfKeys > 0)
|
||||||
|
{
|
||||||
|
memmove(scan->keyData, scankey,
|
||||||
|
scan->numberOfKeys * sizeof(ScanKeyData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End scan of bloom index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
blendscan(IndexScanDesc scan)
|
||||||
|
{
|
||||||
|
BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
|
||||||
|
|
||||||
|
if (so->sign)
|
||||||
|
pfree(so->sign);
|
||||||
|
so->sign = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert all matching tuples into a bitmap.
|
||||||
|
*/
|
||||||
|
int64
|
||||||
|
blgetbitmap(IndexScanDesc scan, TIDBitmap *tbm)
|
||||||
|
{
|
||||||
|
int64 ntids = 0;
|
||||||
|
BlockNumber blkno = BLOOM_HEAD_BLKNO,
|
||||||
|
npages;
|
||||||
|
int i;
|
||||||
|
BufferAccessStrategy bas;
|
||||||
|
BloomScanOpaque so = (BloomScanOpaque) scan->opaque;
|
||||||
|
|
||||||
|
if (so->sign == NULL)
|
||||||
|
{
|
||||||
|
/* New search: have to calculate search signature */
|
||||||
|
ScanKey skey = scan->keyData;
|
||||||
|
|
||||||
|
so->sign = palloc0(sizeof(BloomSignatureWord) * so->state.opts.bloomLength);
|
||||||
|
|
||||||
|
for (i = 0; i < scan->numberOfKeys; i++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Assume bloom-indexable operators to be strict, so nothing could
|
||||||
|
* be found for NULL key.
|
||||||
|
*/
|
||||||
|
if (skey->sk_flags & SK_ISNULL)
|
||||||
|
{
|
||||||
|
pfree(so->sign);
|
||||||
|
so->sign = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add next value to the signature */
|
||||||
|
signValue(&so->state, so->sign, skey->sk_argument,
|
||||||
|
skey->sk_attno - 1);
|
||||||
|
|
||||||
|
skey++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We're going to read the whole index. This is why we use appropriate
|
||||||
|
* buffer access strategy.
|
||||||
|
*/
|
||||||
|
bas = GetAccessStrategy(BAS_BULKREAD);
|
||||||
|
npages = RelationGetNumberOfBlocks(scan->indexRelation);
|
||||||
|
|
||||||
|
for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
|
||||||
|
{
|
||||||
|
Buffer buffer;
|
||||||
|
Page page;
|
||||||
|
|
||||||
|
buffer = ReadBufferExtended(scan->indexRelation, MAIN_FORKNUM,
|
||||||
|
blkno, RBM_NORMAL, bas);
|
||||||
|
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
|
page = BufferGetPage(buffer);
|
||||||
|
TestForOldSnapshot(scan->xs_snapshot, scan->indexRelation, page);
|
||||||
|
|
||||||
|
if (!PageIsNew(page) && !BloomPageIsDeleted(page))
|
||||||
|
{
|
||||||
|
OffsetNumber offset,
|
||||||
|
maxOffset = BloomPageGetMaxOffset(page);
|
||||||
|
|
||||||
|
for (offset = 1; offset <= maxOffset; offset++)
|
||||||
|
{
|
||||||
|
BloomTuple *itup = BloomPageGetTuple(&so->state, page, offset);
|
||||||
|
bool res = true;
|
||||||
|
|
||||||
|
/* Check index signature with scan signature */
|
||||||
|
for (i = 0; i < so->state.opts.bloomLength; i++)
|
||||||
|
{
|
||||||
|
if ((itup->sign[i] & so->sign[i]) != so->sign[i])
|
||||||
|
{
|
||||||
|
res = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add matching tuples to bitmap */
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
tbm_add_tuples(tbm, &itup->heapPtr, 1, true);
|
||||||
|
ntids++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
}
|
||||||
|
FreeAccessStrategy(bas);
|
||||||
|
|
||||||
|
return ntids;
|
||||||
|
}
|
|
@ -0,0 +1,492 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blutils.c
|
||||||
|
* Bloom index utilities.
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1990-1993, Regents of the University of California
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blutils.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/amapi.h"
|
||||||
|
#include "access/generic_xlog.h"
|
||||||
|
#include "catalog/index.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "storage/bufmgr.h"
|
||||||
|
#include "storage/indexfsm.h"
|
||||||
|
#include "utils/memutils.h"
|
||||||
|
#include "access/reloptions.h"
|
||||||
|
#include "storage/freespace.h"
|
||||||
|
#include "storage/indexfsm.h"
|
||||||
|
|
||||||
|
#include "bloom.h"
|
||||||
|
|
||||||
|
/* Signature dealing macros - note i is assumed to be of type int */
|
||||||
|
#define GETWORD(x,i) ( *( (BloomSignatureWord *)(x) + ( (i) / SIGNWORDBITS ) ) )
|
||||||
|
#define CLRBIT(x,i) GETWORD(x,i) &= ~( 0x01 << ( (i) % SIGNWORDBITS ) )
|
||||||
|
#define SETBIT(x,i) GETWORD(x,i) |= ( 0x01 << ( (i) % SIGNWORDBITS ) )
|
||||||
|
#define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % SIGNWORDBITS )) & 0x01 )
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(blhandler);
|
||||||
|
|
||||||
|
/* Kind of relation options for bloom index */
|
||||||
|
static relopt_kind bl_relopt_kind;
|
||||||
|
|
||||||
|
/* parse table for fillRelOptions */
|
||||||
|
static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
|
||||||
|
|
||||||
|
static int32 myRand(void);
|
||||||
|
static void mySrand(uint32 seed);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Module initialize function: initialize info about Bloom relation options.
|
||||||
|
*
|
||||||
|
* Note: keep this in sync with makeDefaultBloomOptions().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
_PG_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char buf[16];
|
||||||
|
|
||||||
|
bl_relopt_kind = add_reloption_kind();
|
||||||
|
|
||||||
|
/* Option for length of signature */
|
||||||
|
add_int_reloption(bl_relopt_kind, "length",
|
||||||
|
"Length of signature in bits",
|
||||||
|
DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH);
|
||||||
|
bl_relopt_tab[0].optname = "length";
|
||||||
|
bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
|
||||||
|
bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
|
||||||
|
|
||||||
|
/* Number of bits for each possible index column: col1, col2, ... */
|
||||||
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||||
|
{
|
||||||
|
snprintf(buf, sizeof(buf), "col%d", i + 1);
|
||||||
|
add_int_reloption(bl_relopt_kind, buf,
|
||||||
|
"Number of bits generated for each index column",
|
||||||
|
DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS);
|
||||||
|
bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
|
||||||
|
buf);
|
||||||
|
bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
|
||||||
|
bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct a default set of Bloom options.
|
||||||
|
*/
|
||||||
|
static BloomOptions *
|
||||||
|
makeDefaultBloomOptions(void)
|
||||||
|
{
|
||||||
|
BloomOptions *opts;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
|
||||||
|
/* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
|
||||||
|
opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
|
||||||
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||||
|
opts->bitSize[i] = DEFAULT_BLOOM_BITS;
|
||||||
|
SET_VARSIZE(opts, sizeof(BloomOptions));
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bloom handler function: return IndexAmRoutine with access method parameters
|
||||||
|
* and callbacks.
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
blhandler(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
|
||||||
|
|
||||||
|
amroutine->amstrategies = BLOOM_NSTRATEGIES;
|
||||||
|
amroutine->amsupport = BLOOM_NPROC;
|
||||||
|
amroutine->amcanorder = false;
|
||||||
|
amroutine->amcanorderbyop = false;
|
||||||
|
amroutine->amcanbackward = false;
|
||||||
|
amroutine->amcanunique = false;
|
||||||
|
amroutine->amcanmulticol = true;
|
||||||
|
amroutine->amoptionalkey = true;
|
||||||
|
amroutine->amsearcharray = false;
|
||||||
|
amroutine->amsearchnulls = false;
|
||||||
|
amroutine->amstorage = false;
|
||||||
|
amroutine->amclusterable = false;
|
||||||
|
amroutine->ampredlocks = false;
|
||||||
|
amroutine->amcanparallel = false;
|
||||||
|
amroutine->amcaninclude = false;
|
||||||
|
amroutine->amkeytype = InvalidOid;
|
||||||
|
|
||||||
|
amroutine->ambuild = blbuild;
|
||||||
|
amroutine->ambuildempty = blbuildempty;
|
||||||
|
amroutine->aminsert = blinsert;
|
||||||
|
amroutine->ambulkdelete = blbulkdelete;
|
||||||
|
amroutine->amvacuumcleanup = blvacuumcleanup;
|
||||||
|
amroutine->amcanreturn = NULL;
|
||||||
|
amroutine->amcostestimate = blcostestimate;
|
||||||
|
amroutine->amoptions = bloptions;
|
||||||
|
amroutine->amproperty = NULL;
|
||||||
|
amroutine->ambuildphasename = NULL;
|
||||||
|
amroutine->amvalidate = blvalidate;
|
||||||
|
amroutine->ambeginscan = blbeginscan;
|
||||||
|
amroutine->amrescan = blrescan;
|
||||||
|
amroutine->amgettuple = NULL;
|
||||||
|
amroutine->amgetbitmap = blgetbitmap;
|
||||||
|
amroutine->amendscan = blendscan;
|
||||||
|
amroutine->ammarkpos = NULL;
|
||||||
|
amroutine->amrestrpos = NULL;
|
||||||
|
amroutine->amestimateparallelscan = NULL;
|
||||||
|
amroutine->aminitparallelscan = NULL;
|
||||||
|
amroutine->amparallelrescan = NULL;
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(amroutine);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill BloomState structure for particular index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
initBloomState(BloomState *state, Relation index)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
state->nColumns = index->rd_att->natts;
|
||||||
|
|
||||||
|
/* Initialize hash function for each attribute */
|
||||||
|
for (i = 0; i < index->rd_att->natts; i++)
|
||||||
|
{
|
||||||
|
fmgr_info_copy(&(state->hashFn[i]),
|
||||||
|
index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
|
||||||
|
CurrentMemoryContext);
|
||||||
|
state->collations[i] = index->rd_indcollation[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize amcache if needed with options from metapage */
|
||||||
|
if (!index->rd_amcache)
|
||||||
|
{
|
||||||
|
Buffer buffer;
|
||||||
|
Page page;
|
||||||
|
BloomMetaPageData *meta;
|
||||||
|
BloomOptions *opts;
|
||||||
|
|
||||||
|
opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
|
||||||
|
|
||||||
|
buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
|
|
||||||
|
page = BufferGetPage(buffer);
|
||||||
|
|
||||||
|
if (!BloomPageIsMeta(page))
|
||||||
|
elog(ERROR, "Relation is not a bloom index");
|
||||||
|
meta = BloomPageGetMeta(BufferGetPage(buffer));
|
||||||
|
|
||||||
|
if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
|
||||||
|
elog(ERROR, "Relation is not a bloom index");
|
||||||
|
|
||||||
|
*opts = meta->opts;
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
|
||||||
|
index->rd_amcache = (void *) opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&state->opts, index->rd_amcache, sizeof(state->opts));
|
||||||
|
state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
|
||||||
|
sizeof(BloomSignatureWord) * state->opts.bloomLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Random generator copied from FreeBSD. Using own random generator here for
|
||||||
|
* two reasons:
|
||||||
|
*
|
||||||
|
* 1) In this case random numbers are used for on-disk storage. Usage of
|
||||||
|
* PostgreSQL number generator would obstruct it from all possible changes.
|
||||||
|
* 2) Changing seed of PostgreSQL random generator would be undesirable side
|
||||||
|
* effect.
|
||||||
|
*/
|
||||||
|
static int32 next;
|
||||||
|
|
||||||
|
static int32
|
||||||
|
myRand(void)
|
||||||
|
{
|
||||||
|
/*----------
|
||||||
|
* Compute x = (7^5 * x) mod (2^31 - 1)
|
||||||
|
* without overflowing 31 bits:
|
||||||
|
* (2^31 - 1) = 127773 * (7^5) + 2836
|
||||||
|
* From "Random number generators: good ones are hard to find",
|
||||||
|
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
|
||||||
|
* October 1988, p. 1195.
|
||||||
|
*----------
|
||||||
|
*/
|
||||||
|
int32 hi,
|
||||||
|
lo,
|
||||||
|
x;
|
||||||
|
|
||||||
|
/* Must be in [1, 0x7ffffffe] range at this point. */
|
||||||
|
hi = next / 127773;
|
||||||
|
lo = next % 127773;
|
||||||
|
x = 16807 * lo - 2836 * hi;
|
||||||
|
if (x < 0)
|
||||||
|
x += 0x7fffffff;
|
||||||
|
next = x;
|
||||||
|
/* Transform to [0, 0x7ffffffd] range. */
|
||||||
|
return (x - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mySrand(uint32 seed)
|
||||||
|
{
|
||||||
|
next = seed;
|
||||||
|
/* Transform to [1, 0x7ffffffe] range. */
|
||||||
|
next = (next % 0x7ffffffe) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add bits of given value to the signature.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
signValue(BloomState *state, BloomSignatureWord *sign, Datum value, int attno)
|
||||||
|
{
|
||||||
|
uint32 hashVal;
|
||||||
|
int nBit,
|
||||||
|
j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init generator with "column's" number to get "hashed" seed for new
|
||||||
|
* value. We don't want to map the same numbers from different columns
|
||||||
|
* into the same bits!
|
||||||
|
*/
|
||||||
|
mySrand(attno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init hash sequence to map our value into bits. the same values in
|
||||||
|
* different columns will be mapped into different bits because of step
|
||||||
|
* above
|
||||||
|
*/
|
||||||
|
hashVal = DatumGetInt32(FunctionCall1Coll(&state->hashFn[attno], state->collations[attno], value));
|
||||||
|
mySrand(hashVal ^ myRand());
|
||||||
|
|
||||||
|
for (j = 0; j < state->opts.bitSize[attno]; j++)
|
||||||
|
{
|
||||||
|
/* prevent multiple evaluation in SETBIT macro */
|
||||||
|
nBit = myRand() % (state->opts.bloomLength * SIGNWORDBITS);
|
||||||
|
SETBIT(sign, nBit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make bloom tuple from values.
|
||||||
|
*/
|
||||||
|
BloomTuple *
|
||||||
|
BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
BloomTuple *res = (BloomTuple *) palloc0(state->sizeOfBloomTuple);
|
||||||
|
|
||||||
|
res->heapPtr = *iptr;
|
||||||
|
|
||||||
|
/* Blooming each column */
|
||||||
|
for (i = 0; i < state->nColumns; i++)
|
||||||
|
{
|
||||||
|
/* skip nulls */
|
||||||
|
if (isnull[i])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
signValue(state, res->sign, values[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add new bloom tuple to the page. Returns true if new tuple was successfully
|
||||||
|
* added to the page. Returns false if it doesn't fit on the page.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
|
||||||
|
{
|
||||||
|
BloomTuple *itup;
|
||||||
|
BloomPageOpaque opaque;
|
||||||
|
Pointer ptr;
|
||||||
|
|
||||||
|
/* We shouldn't be pointed to an invalid page */
|
||||||
|
Assert(!PageIsNew(page) && !BloomPageIsDeleted(page));
|
||||||
|
|
||||||
|
/* Does new tuple fit on the page? */
|
||||||
|
if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Copy new tuple to the end of page */
|
||||||
|
opaque = BloomPageGetOpaque(page);
|
||||||
|
itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
|
||||||
|
memcpy((Pointer) itup, (Pointer) tuple, state->sizeOfBloomTuple);
|
||||||
|
|
||||||
|
/* Adjust maxoff and pd_lower */
|
||||||
|
opaque->maxoff++;
|
||||||
|
ptr = (Pointer) BloomPageGetTuple(state, page, opaque->maxoff + 1);
|
||||||
|
((PageHeader) page)->pd_lower = ptr - page;
|
||||||
|
|
||||||
|
/* Assert we didn't overrun available space */
|
||||||
|
Assert(((PageHeader) page)->pd_lower <= ((PageHeader) page)->pd_upper);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new page (either by recycling, or by extending the index file)
|
||||||
|
* The returned buffer is already pinned and exclusive-locked
|
||||||
|
* Caller is responsible for initializing the page by calling BloomInitPage
|
||||||
|
*/
|
||||||
|
Buffer
|
||||||
|
BloomNewBuffer(Relation index)
|
||||||
|
{
|
||||||
|
Buffer buffer;
|
||||||
|
bool needLock;
|
||||||
|
|
||||||
|
/* First, try to get a page from FSM */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
BlockNumber blkno = GetFreeIndexPage(index);
|
||||||
|
|
||||||
|
if (blkno == InvalidBlockNumber)
|
||||||
|
break;
|
||||||
|
|
||||||
|
buffer = ReadBuffer(index, blkno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to guard against the possibility that someone else already
|
||||||
|
* recycled this page; the buffer may be locked if so.
|
||||||
|
*/
|
||||||
|
if (ConditionalLockBuffer(buffer))
|
||||||
|
{
|
||||||
|
Page page = BufferGetPage(buffer);
|
||||||
|
|
||||||
|
if (PageIsNew(page))
|
||||||
|
return buffer; /* OK to use, if never initialized */
|
||||||
|
|
||||||
|
if (BloomPageIsDeleted(page))
|
||||||
|
return buffer; /* OK to use */
|
||||||
|
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't use it, so release buffer and try again */
|
||||||
|
ReleaseBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Must extend the file */
|
||||||
|
needLock = !RELATION_IS_LOCAL(index);
|
||||||
|
if (needLock)
|
||||||
|
LockRelationForExtension(index, ExclusiveLock);
|
||||||
|
|
||||||
|
buffer = ReadBuffer(index, P_NEW);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
|
if (needLock)
|
||||||
|
UnlockRelationForExtension(index, ExclusiveLock);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize any page of a bloom index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BloomInitPage(Page page, uint16 flags)
|
||||||
|
{
|
||||||
|
BloomPageOpaque opaque;
|
||||||
|
|
||||||
|
PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
|
||||||
|
|
||||||
|
opaque = BloomPageGetOpaque(page);
|
||||||
|
memset(opaque, 0, sizeof(BloomPageOpaqueData));
|
||||||
|
opaque->flags = flags;
|
||||||
|
opaque->bloom_page_id = BLOOM_PAGE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in metapage for bloom index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BloomFillMetapage(Relation index, Page metaPage)
|
||||||
|
{
|
||||||
|
BloomOptions *opts;
|
||||||
|
BloomMetaPageData *metadata;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Choose the index's options. If reloptions have been assigned, use
|
||||||
|
* those, otherwise create default options.
|
||||||
|
*/
|
||||||
|
opts = (BloomOptions *) index->rd_options;
|
||||||
|
if (!opts)
|
||||||
|
opts = makeDefaultBloomOptions();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize contents of meta page, including a copy of the options,
|
||||||
|
* which are now frozen for the life of the index.
|
||||||
|
*/
|
||||||
|
BloomInitPage(metaPage, BLOOM_META);
|
||||||
|
metadata = BloomPageGetMeta(metaPage);
|
||||||
|
memset(metadata, 0, sizeof(BloomMetaPageData));
|
||||||
|
metadata->magickNumber = BLOOM_MAGICK_NUMBER;
|
||||||
|
metadata->opts = *opts;
|
||||||
|
((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
|
||||||
|
|
||||||
|
/* If this fails, probably FreeBlockNumberArray size calc is wrong: */
|
||||||
|
Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize metapage for bloom index.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
BloomInitMetapage(Relation index)
|
||||||
|
{
|
||||||
|
Buffer metaBuffer;
|
||||||
|
Page metaPage;
|
||||||
|
GenericXLogState *state;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make a new page; since it is first page it should be associated with
|
||||||
|
* block number 0 (BLOOM_METAPAGE_BLKNO).
|
||||||
|
*/
|
||||||
|
metaBuffer = BloomNewBuffer(index);
|
||||||
|
Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
|
||||||
|
|
||||||
|
/* Initialize contents of meta page */
|
||||||
|
state = GenericXLogStart(index);
|
||||||
|
metaPage = GenericXLogRegisterBuffer(state, metaBuffer,
|
||||||
|
GENERIC_XLOG_FULL_IMAGE);
|
||||||
|
BloomFillMetapage(index, metaPage);
|
||||||
|
GenericXLogFinish(state);
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(metaBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse reloptions for bloom index, producing a BloomOptions struct.
|
||||||
|
*/
|
||||||
|
bytea *
|
||||||
|
bloptions(Datum reloptions, bool validate)
|
||||||
|
{
|
||||||
|
relopt_value *options;
|
||||||
|
int numoptions;
|
||||||
|
BloomOptions *rdopts;
|
||||||
|
|
||||||
|
/* Parse the user-given reloptions */
|
||||||
|
options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
|
||||||
|
rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
|
||||||
|
fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
|
||||||
|
validate, bl_relopt_tab, lengthof(bl_relopt_tab));
|
||||||
|
|
||||||
|
/* Convert signature length from # of bits to # to words, rounding up */
|
||||||
|
rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
|
||||||
|
|
||||||
|
return (bytea *) rdopts;
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blvacuum.c
|
||||||
|
* Bloom VACUUM functions.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blvacuum.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/genam.h"
|
||||||
|
#include "bloom.h"
|
||||||
|
#include "catalog/storage.h"
|
||||||
|
#include "commands/vacuum.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
#include "postmaster/autovacuum.h"
|
||||||
|
#include "storage/bufmgr.h"
|
||||||
|
#include "storage/indexfsm.h"
|
||||||
|
#include "storage/lmgr.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bulk deletion of all index entries pointing to a set of heap tuples.
|
||||||
|
* The set of target tuples is specified via a callback routine that tells
|
||||||
|
* whether any given heap tuple (identified by ItemPointer) is being deleted.
|
||||||
|
*
|
||||||
|
* Result: a palloc'd struct containing statistical info for VACUUM displays.
|
||||||
|
*/
|
||||||
|
IndexBulkDeleteResult *
|
||||||
|
blbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
|
||||||
|
IndexBulkDeleteCallback callback, void *callback_state)
|
||||||
|
{
|
||||||
|
Relation index = info->index;
|
||||||
|
BlockNumber blkno,
|
||||||
|
npages;
|
||||||
|
FreeBlockNumberArray notFullPage;
|
||||||
|
int countPage = 0;
|
||||||
|
BloomState state;
|
||||||
|
Buffer buffer;
|
||||||
|
Page page;
|
||||||
|
BloomMetaPageData *metaData;
|
||||||
|
GenericXLogState *gxlogState;
|
||||||
|
|
||||||
|
if (stats == NULL)
|
||||||
|
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
|
||||||
|
|
||||||
|
initBloomState(&state, index);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over the pages. We don't care about concurrently added pages,
|
||||||
|
* they can't contain tuples to delete.
|
||||||
|
*/
|
||||||
|
npages = RelationGetNumberOfBlocks(index);
|
||||||
|
for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
|
||||||
|
{
|
||||||
|
BloomTuple *itup,
|
||||||
|
*itupPtr,
|
||||||
|
*itupEnd;
|
||||||
|
|
||||||
|
vacuum_delay_point();
|
||||||
|
|
||||||
|
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
|
||||||
|
RBM_NORMAL, info->strategy);
|
||||||
|
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
gxlogState = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
|
||||||
|
|
||||||
|
/* Ignore empty/deleted pages until blvacuumcleanup() */
|
||||||
|
if (PageIsNew(page) || BloomPageIsDeleted(page))
|
||||||
|
{
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
GenericXLogAbort(gxlogState);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over the tuples. itup points to current tuple being
|
||||||
|
* scanned, itupPtr points to where to save next non-deleted tuple.
|
||||||
|
*/
|
||||||
|
itup = itupPtr = BloomPageGetTuple(&state, page, FirstOffsetNumber);
|
||||||
|
itupEnd = BloomPageGetTuple(&state, page,
|
||||||
|
OffsetNumberNext(BloomPageGetMaxOffset(page)));
|
||||||
|
while (itup < itupEnd)
|
||||||
|
{
|
||||||
|
/* Do we have to delete this tuple? */
|
||||||
|
if (callback(&itup->heapPtr, callback_state))
|
||||||
|
{
|
||||||
|
/* Yes; adjust count of tuples that will be left on page */
|
||||||
|
BloomPageGetOpaque(page)->maxoff--;
|
||||||
|
stats->tuples_removed += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* No; copy it to itupPtr++, but skip copy if not needed */
|
||||||
|
if (itupPtr != itup)
|
||||||
|
memmove((Pointer) itupPtr, (Pointer) itup,
|
||||||
|
state.sizeOfBloomTuple);
|
||||||
|
itupPtr = BloomPageGetNextTuple(&state, itupPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
itup = BloomPageGetNextTuple(&state, itup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assert that we counted correctly */
|
||||||
|
Assert(itupPtr == BloomPageGetTuple(&state, page,
|
||||||
|
OffsetNumberNext(BloomPageGetMaxOffset(page))));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add page to new notFullPage list if we will not mark page as
|
||||||
|
* deleted and there is free space on it
|
||||||
|
*/
|
||||||
|
if (BloomPageGetMaxOffset(page) != 0 &&
|
||||||
|
BloomPageGetFreeSpace(&state, page) >= state.sizeOfBloomTuple &&
|
||||||
|
countPage < BloomMetaBlockN)
|
||||||
|
notFullPage[countPage++] = blkno;
|
||||||
|
|
||||||
|
/* Did we delete something? */
|
||||||
|
if (itupPtr != itup)
|
||||||
|
{
|
||||||
|
/* Is it empty page now? */
|
||||||
|
if (BloomPageGetMaxOffset(page) == 0)
|
||||||
|
BloomPageSetDeleted(page);
|
||||||
|
/* Adjust pd_lower */
|
||||||
|
((PageHeader) page)->pd_lower = (Pointer) itupPtr - page;
|
||||||
|
/* Finish WAL-logging */
|
||||||
|
GenericXLogFinish(gxlogState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Didn't change anything: abort WAL-logging */
|
||||||
|
GenericXLogAbort(gxlogState);
|
||||||
|
}
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the metapage's notFullPage list with whatever we found. Our
|
||||||
|
* info could already be out of date at this point, but blinsert() will
|
||||||
|
* cope if so.
|
||||||
|
*/
|
||||||
|
buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
|
||||||
|
|
||||||
|
gxlogState = GenericXLogStart(index);
|
||||||
|
page = GenericXLogRegisterBuffer(gxlogState, buffer, 0);
|
||||||
|
|
||||||
|
metaData = BloomPageGetMeta(page);
|
||||||
|
memcpy(metaData->notFullPage, notFullPage, sizeof(BlockNumber) * countPage);
|
||||||
|
metaData->nStart = 0;
|
||||||
|
metaData->nEnd = countPage;
|
||||||
|
|
||||||
|
GenericXLogFinish(gxlogState);
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Post-VACUUM cleanup.
|
||||||
|
*
|
||||||
|
* Result: a palloc'd struct containing statistical info for VACUUM displays.
|
||||||
|
*/
|
||||||
|
IndexBulkDeleteResult *
|
||||||
|
blvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
|
||||||
|
{
|
||||||
|
Relation index = info->index;
|
||||||
|
BlockNumber npages,
|
||||||
|
blkno;
|
||||||
|
|
||||||
|
if (info->analyze_only)
|
||||||
|
return stats;
|
||||||
|
|
||||||
|
if (stats == NULL)
|
||||||
|
stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Iterate over the pages: insert deleted pages into FSM and collect
|
||||||
|
* statistics.
|
||||||
|
*/
|
||||||
|
npages = RelationGetNumberOfBlocks(index);
|
||||||
|
stats->num_pages = npages;
|
||||||
|
stats->pages_free = 0;
|
||||||
|
stats->num_index_tuples = 0;
|
||||||
|
for (blkno = BLOOM_HEAD_BLKNO; blkno < npages; blkno++)
|
||||||
|
{
|
||||||
|
Buffer buffer;
|
||||||
|
Page page;
|
||||||
|
|
||||||
|
vacuum_delay_point();
|
||||||
|
|
||||||
|
buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
|
||||||
|
RBM_NORMAL, info->strategy);
|
||||||
|
LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
||||||
|
page = (Page) BufferGetPage(buffer);
|
||||||
|
|
||||||
|
if (PageIsNew(page) || BloomPageIsDeleted(page))
|
||||||
|
{
|
||||||
|
RecordFreeIndexPage(index, blkno);
|
||||||
|
stats->pages_free++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stats->num_index_tuples += BloomPageGetMaxOffset(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnlockReleaseBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexFreeSpaceMapVacuum(info->index);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* blvalidate.c
|
||||||
|
* Opclass validator for bloom.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019, PostgreSQL Global Development Group
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* contrib/bloom/blvalidate.c
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/amvalidate.h"
|
||||||
|
#include "access/htup_details.h"
|
||||||
|
#include "catalog/pg_amop.h"
|
||||||
|
#include "catalog/pg_amproc.h"
|
||||||
|
#include "catalog/pg_opclass.h"
|
||||||
|
#include "catalog/pg_opfamily.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/regproc.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
#include "bloom.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validator for a bloom opclass.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
blvalidate(Oid opclassoid)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
HeapTuple classtup;
|
||||||
|
Form_pg_opclass classform;
|
||||||
|
Oid opfamilyoid;
|
||||||
|
Oid opcintype;
|
||||||
|
Oid opckeytype;
|
||||||
|
char *opclassname;
|
||||||
|
HeapTuple familytup;
|
||||||
|
Form_pg_opfamily familyform;
|
||||||
|
char *opfamilyname;
|
||||||
|
CatCList *proclist,
|
||||||
|
*oprlist;
|
||||||
|
List *grouplist;
|
||||||
|
OpFamilyOpFuncGroup *opclassgroup;
|
||||||
|
int i;
|
||||||
|
ListCell *lc;
|
||||||
|
|
||||||
|
/* Fetch opclass information */
|
||||||
|
classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
|
||||||
|
if (!HeapTupleIsValid(classtup))
|
||||||
|
elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
|
||||||
|
classform = (Form_pg_opclass) GETSTRUCT(classtup);
|
||||||
|
|
||||||
|
opfamilyoid = classform->opcfamily;
|
||||||
|
opcintype = classform->opcintype;
|
||||||
|
opckeytype = classform->opckeytype;
|
||||||
|
if (!OidIsValid(opckeytype))
|
||||||
|
opckeytype = opcintype;
|
||||||
|
opclassname = NameStr(classform->opcname);
|
||||||
|
|
||||||
|
/* Fetch opfamily information */
|
||||||
|
familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
|
||||||
|
if (!HeapTupleIsValid(familytup))
|
||||||
|
elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
|
||||||
|
familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
|
||||||
|
|
||||||
|
opfamilyname = NameStr(familyform->opfname);
|
||||||
|
|
||||||
|
/* Fetch all operators and support functions of the opfamily */
|
||||||
|
oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
|
||||||
|
proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
|
||||||
|
|
||||||
|
/* Check individual support functions */
|
||||||
|
for (i = 0; i < proclist->n_members; i++)
|
||||||
|
{
|
||||||
|
HeapTuple proctup = &proclist->members[i]->tuple;
|
||||||
|
Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All bloom support functions should be registered with matching
|
||||||
|
* left/right types
|
||||||
|
*/
|
||||||
|
if (procform->amproclefttype != procform->amprocrighttype)
|
||||||
|
{
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opfamily %s contains support procedure %s with cross-type registration",
|
||||||
|
opfamilyname,
|
||||||
|
format_procedure(procform->amproc))));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can't check signatures except within the specific opclass, since
|
||||||
|
* we need to know the associated opckeytype in many cases.
|
||||||
|
*/
|
||||||
|
if (procform->amproclefttype != opcintype)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Check procedure numbers and function signatures */
|
||||||
|
switch (procform->amprocnum)
|
||||||
|
{
|
||||||
|
case BLOOM_HASH_PROC:
|
||||||
|
ok = check_amproc_signature(procform->amproc, INT4OID, false,
|
||||||
|
1, 1, opckeytype);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opfamily %s contains function %s with invalid support number %d",
|
||||||
|
opfamilyname,
|
||||||
|
format_procedure(procform->amproc),
|
||||||
|
procform->amprocnum)));
|
||||||
|
result = false;
|
||||||
|
continue; /* don't want additional message */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("gist opfamily %s contains function %s with wrong signature for support number %d",
|
||||||
|
opfamilyname,
|
||||||
|
format_procedure(procform->amproc),
|
||||||
|
procform->amprocnum)));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check individual operators */
|
||||||
|
for (i = 0; i < oprlist->n_members; i++)
|
||||||
|
{
|
||||||
|
HeapTuple oprtup = &oprlist->members[i]->tuple;
|
||||||
|
Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
|
||||||
|
|
||||||
|
/* Check it's allowed strategy for bloom */
|
||||||
|
if (oprform->amopstrategy < 1 ||
|
||||||
|
oprform->amopstrategy > BLOOM_NSTRATEGIES)
|
||||||
|
{
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opfamily %s contains operator %s with invalid strategy number %d",
|
||||||
|
opfamilyname,
|
||||||
|
format_operator(oprform->amopopr),
|
||||||
|
oprform->amopstrategy)));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bloom doesn't support ORDER BY operators */
|
||||||
|
if (oprform->amoppurpose != AMOP_SEARCH ||
|
||||||
|
OidIsValid(oprform->amopsortfamily))
|
||||||
|
{
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opfamily %s contains invalid ORDER BY specification for operator %s",
|
||||||
|
opfamilyname,
|
||||||
|
format_operator(oprform->amopopr))));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check operator signature --- same for all bloom strategies */
|
||||||
|
if (!check_amop_signature(oprform->amopopr, BOOLOID,
|
||||||
|
oprform->amoplefttype,
|
||||||
|
oprform->amoprighttype))
|
||||||
|
{
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opfamily %s contains operator %s with wrong signature",
|
||||||
|
opfamilyname,
|
||||||
|
format_operator(oprform->amopopr))));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now check for inconsistent groups of operators/functions */
|
||||||
|
grouplist = identify_opfamily_groups(oprlist, proclist);
|
||||||
|
opclassgroup = NULL;
|
||||||
|
foreach(lc, grouplist)
|
||||||
|
{
|
||||||
|
OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
|
||||||
|
|
||||||
|
/* Remember the group exactly matching the test opclass */
|
||||||
|
if (thisgroup->lefttype == opcintype &&
|
||||||
|
thisgroup->righttype == opcintype)
|
||||||
|
opclassgroup = thisgroup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There is not a lot we can do to check the operator sets, since each
|
||||||
|
* bloom opclass is more or less a law unto itself, and some contain
|
||||||
|
* only operators that are binary-compatible with the opclass datatype
|
||||||
|
* (meaning that empty operator sets can be OK). That case also means
|
||||||
|
* that we shouldn't insist on nonempty function sets except for the
|
||||||
|
* opclass's own group.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the originally-named opclass is complete */
|
||||||
|
for (i = 1; i <= BLOOM_NPROC; i++)
|
||||||
|
{
|
||||||
|
if (opclassgroup &&
|
||||||
|
(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
|
||||||
|
continue; /* got it */
|
||||||
|
ereport(INFO,
|
||||||
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
||||||
|
errmsg("bloom opclass %s is missing support function %d",
|
||||||
|
opclassname, i)));
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseCatCacheList(proclist);
|
||||||
|
ReleaseCatCacheList(oprlist);
|
||||||
|
ReleaseSysCache(familytup);
|
||||||
|
ReleaseSysCache(classtup);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
CREATE EXTENSION bloom;
|
||||||
|
CREATE TABLE tst (
|
||||||
|
i int4,
|
||||||
|
t text
|
||||||
|
);
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
|
||||||
|
ALTER INDEX bloomidx SET (length=80);
|
||||||
|
SET enable_seqscan=on;
|
||||||
|
SET enable_bitmapscan=off;
|
||||||
|
SET enable_indexscan=off;
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SET enable_seqscan=off;
|
||||||
|
SET enable_bitmapscan=on;
|
||||||
|
SET enable_indexscan=on;
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tst
|
||||||
|
Recheck Cond: (i = 7)
|
||||||
|
-> Bitmap Index Scan on bloomidx
|
||||||
|
Index Cond: (i = 7)
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tst
|
||||||
|
Recheck Cond: (t = '5'::text)
|
||||||
|
-> Bitmap Index Scan on bloomidx
|
||||||
|
Index Cond: (t = '5'::text)
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tst
|
||||||
|
Recheck Cond: ((i = 7) AND (t = '5'::text))
|
||||||
|
-> Bitmap Index Scan on bloomidx
|
||||||
|
Index Cond: ((i = 7) AND (t = '5'::text))
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DELETE FROM tst;
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
VACUUM ANALYZE tst;
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DELETE FROM tst WHERE i > 1 OR t = '5';
|
||||||
|
VACUUM tst;
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
VACUUM FULL tst;
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Try an unlogged table too
|
||||||
|
CREATE UNLOGGED TABLE tstu (
|
||||||
|
i int4,
|
||||||
|
t text
|
||||||
|
);
|
||||||
|
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
|
||||||
|
SET enable_seqscan=off;
|
||||||
|
SET enable_bitmapscan=on;
|
||||||
|
SET enable_indexscan=on;
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE i = 7;
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tstu
|
||||||
|
Recheck Cond: (i = 7)
|
||||||
|
-> Bitmap Index Scan on bloomidxu
|
||||||
|
Index Cond: (i = 7)
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE t = '5';
|
||||||
|
QUERY PLAN
|
||||||
|
--------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tstu
|
||||||
|
Recheck Cond: (t = '5'::text)
|
||||||
|
-> Bitmap Index Scan on bloomidxu
|
||||||
|
Index Cond: (t = '5'::text)
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE i = 7 AND t = '5';
|
||||||
|
QUERY PLAN
|
||||||
|
---------------------------------------------------------
|
||||||
|
Aggregate
|
||||||
|
-> Bitmap Heap Scan on tstu
|
||||||
|
Recheck Cond: ((i = 7) AND (t = '5'::text))
|
||||||
|
-> Bitmap Index Scan on bloomidxu
|
||||||
|
Index Cond: ((i = 7) AND (t = '5'::text))
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tstu WHERE i = 7;
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
200
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tstu WHERE t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
112
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT count(*) FROM tstu WHERE i = 7 AND t = '5';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
13
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
RESET enable_seqscan;
|
||||||
|
RESET enable_bitmapscan;
|
||||||
|
RESET enable_indexscan;
|
||||||
|
-- Run amvalidator function on our opclasses
|
||||||
|
SELECT opcname, amvalidate(opc.oid)
|
||||||
|
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
|
||||||
|
WHERE amname = 'bloom'
|
||||||
|
ORDER BY 1;
|
||||||
|
opcname | amvalidate
|
||||||
|
----------+------------
|
||||||
|
int4_ops | t
|
||||||
|
text_ops | t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- relation options
|
||||||
|
--
|
||||||
|
DROP INDEX bloomidx;
|
||||||
|
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
|
||||||
|
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
|
||||||
|
reloptions
|
||||||
|
-------------------
|
||||||
|
{length=7,col1=4}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- check for min and max values
|
||||||
|
\set VERBOSITY terse
|
||||||
|
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
|
||||||
|
ERROR: value 0 out of bounds for option "length"
|
||||||
|
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
|
||||||
|
ERROR: value 0 out of bounds for option "col1"
|
|
@ -0,0 +1,95 @@
|
||||||
|
CREATE EXTENSION bloom;
|
||||||
|
|
||||||
|
CREATE TABLE tst (
|
||||||
|
i int4,
|
||||||
|
t text
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);
|
||||||
|
ALTER INDEX bloomidx SET (length=80);
|
||||||
|
|
||||||
|
SET enable_seqscan=on;
|
||||||
|
SET enable_bitmapscan=off;
|
||||||
|
SET enable_indexscan=off;
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
SET enable_seqscan=off;
|
||||||
|
SET enable_bitmapscan=on;
|
||||||
|
SET enable_indexscan=on;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
DELETE FROM tst;
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
VACUUM ANALYZE tst;
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
DELETE FROM tst WHERE i > 1 OR t = '5';
|
||||||
|
VACUUM tst;
|
||||||
|
INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
VACUUM FULL tst;
|
||||||
|
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tst WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tst WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
-- Try an unlogged table too
|
||||||
|
|
||||||
|
CREATE UNLOGGED TABLE tstu (
|
||||||
|
i int4,
|
||||||
|
t text
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO tstu SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,2000) i;
|
||||||
|
CREATE INDEX bloomidxu ON tstu USING bloom (i, t) WITH (col2 = 4);
|
||||||
|
|
||||||
|
SET enable_seqscan=off;
|
||||||
|
SET enable_bitmapscan=on;
|
||||||
|
SET enable_indexscan=on;
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE i = 7;
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE t = '5';
|
||||||
|
EXPLAIN (COSTS OFF) SELECT count(*) FROM tstu WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
SELECT count(*) FROM tstu WHERE i = 7;
|
||||||
|
SELECT count(*) FROM tstu WHERE t = '5';
|
||||||
|
SELECT count(*) FROM tstu WHERE i = 7 AND t = '5';
|
||||||
|
|
||||||
|
RESET enable_seqscan;
|
||||||
|
RESET enable_bitmapscan;
|
||||||
|
RESET enable_indexscan;
|
||||||
|
|
||||||
|
-- Run amvalidator function on our opclasses
|
||||||
|
SELECT opcname, amvalidate(opc.oid)
|
||||||
|
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
|
||||||
|
WHERE amname = 'bloom'
|
||||||
|
ORDER BY 1;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- relation options
|
||||||
|
--
|
||||||
|
DROP INDEX bloomidx;
|
||||||
|
CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1=4);
|
||||||
|
SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
|
||||||
|
-- check for min and max values
|
||||||
|
\set VERBOSITY terse
|
||||||
|
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
|
||||||
|
CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Test generic xlog record work for bloom index replication.
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use PostgresNode;
|
||||||
|
use TestLib;
|
||||||
|
use Test::More tests => 31;
|
||||||
|
|
||||||
|
my $node_master;
|
||||||
|
my $node_standby;
|
||||||
|
|
||||||
|
# Run few queries on both master and standby and check their results match.
|
||||||
|
sub test_index_replay
|
||||||
|
{
|
||||||
|
my ($test_name) = @_;
|
||||||
|
|
||||||
|
# Wait for standby to catch up
|
||||||
|
my $applname = $node_standby->name;
|
||||||
|
my $caughtup_query =
|
||||||
|
"SELECT pg_current_wal_lsn() <= write_lsn FROM pg_stat_replication WHERE application_name = '$applname';";
|
||||||
|
$node_master->poll_query_until('postgres', $caughtup_query)
|
||||||
|
or die "Timed out while waiting for standby 1 to catch up";
|
||||||
|
|
||||||
|
my $queries = qq(SET enable_seqscan=off;
|
||||||
|
SET enable_bitmapscan=on;
|
||||||
|
SET enable_indexscan=on;
|
||||||
|
SELECT * FROM tst WHERE i = 0;
|
||||||
|
SELECT * FROM tst WHERE i = 3;
|
||||||
|
SELECT * FROM tst WHERE t = 'b';
|
||||||
|
SELECT * FROM tst WHERE t = 'f';
|
||||||
|
SELECT * FROM tst WHERE i = 3 AND t = 'c';
|
||||||
|
SELECT * FROM tst WHERE i = 7 AND t = 'e';
|
||||||
|
);
|
||||||
|
|
||||||
|
# Run test queries and compare their result
|
||||||
|
my $master_result = $node_master->safe_psql("postgres", $queries);
|
||||||
|
my $standby_result = $node_standby->safe_psql("postgres", $queries);
|
||||||
|
|
||||||
|
is($master_result, $standby_result, "$test_name: query result matches");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Initialize master node
|
||||||
|
$node_master = get_new_node('master');
|
||||||
|
$node_master->init(allows_streaming => 1);
|
||||||
|
$node_master->start;
|
||||||
|
my $backup_name = 'my_backup';
|
||||||
|
|
||||||
|
# Take backup
|
||||||
|
$node_master->backup($backup_name);
|
||||||
|
|
||||||
|
# Create streaming standby linking to master
|
||||||
|
$node_standby = get_new_node('standby');
|
||||||
|
$node_standby->init_from_backup($node_master, $backup_name,
|
||||||
|
has_streaming => 1);
|
||||||
|
$node_standby->start;
|
||||||
|
|
||||||
|
# Create some bloom index on master
|
||||||
|
$node_master->safe_psql("postgres", "CREATE EXTENSION bloom;");
|
||||||
|
$node_master->safe_psql("postgres", "CREATE TABLE tst (i int4, t text);");
|
||||||
|
$node_master->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series(1,100000) i;"
|
||||||
|
);
|
||||||
|
$node_master->safe_psql("postgres",
|
||||||
|
"CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (col1 = 3);");
|
||||||
|
|
||||||
|
# Test that queries give same result
|
||||||
|
test_index_replay('initial');
|
||||||
|
|
||||||
|
# Run 10 cycles of table modification. Run test queries after each modification.
|
||||||
|
for my $i (1 .. 10)
|
||||||
|
{
|
||||||
|
$node_master->safe_psql("postgres", "DELETE FROM tst WHERE i = $i;");
|
||||||
|
test_index_replay("delete $i");
|
||||||
|
$node_master->safe_psql("postgres", "VACUUM tst;");
|
||||||
|
test_index_replay("vacuum $i");
|
||||||
|
my ($start, $end) = (100001 + ($i - 1) * 10000, 100000 + $i * 10000);
|
||||||
|
$node_master->safe_psql("postgres",
|
||||||
|
"INSERT INTO tst SELECT i%10, substr(md5(i::text), 1, 1) FROM generate_series($start,$end) i;"
|
||||||
|
);
|
||||||
|
test_index_replay("insert $i");
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Generated subdirectories
|
||||||
|
/log/
|
||||||
|
/results/
|
||||||
|
/tmp_check/
|
|
@ -0,0 +1,25 @@
|
||||||
|
# contrib/btree_gin/Makefile
|
||||||
|
|
||||||
|
MODULE_big = btree_gin
|
||||||
|
OBJS = btree_gin.o $(WIN32RES)
|
||||||
|
|
||||||
|
EXTENSION = btree_gin
|
||||||
|
DATA = btree_gin--1.0.sql btree_gin--1.0--1.1.sql btree_gin--1.1--1.2.sql \
|
||||||
|
btree_gin--1.2--1.3.sql btree_gin--unpackaged--1.0.sql
|
||||||
|
PGFILEDESC = "btree_gin - B-tree equivalent GIN operator classes"
|
||||||
|
|
||||||
|
REGRESS = install_btree_gin int2 int4 int8 float4 float8 money oid \
|
||||||
|
timestamp timestamptz time timetz date interval \
|
||||||
|
macaddr macaddr8 inet cidr text varchar char bytea bit varbit \
|
||||||
|
numeric enum uuid name bool bpchar
|
||||||
|
|
||||||
|
ifdef USE_PGXS
|
||||||
|
PG_CONFIG = pg_config
|
||||||
|
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||||
|
include $(PGXS)
|
||||||
|
else
|
||||||
|
subdir = contrib/btree_gin
|
||||||
|
top_builddir = ../..
|
||||||
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
include $(top_srcdir)/contrib/contrib-global.mk
|
||||||
|
endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* contrib/btree_gin/btree_gin--1.0--1.1.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.1'" to load this file. \quit
|
||||||
|
|
||||||
|
-- macaddr8 datatype support new in 10.0.
|
||||||
|
CREATE FUNCTION gin_extract_value_macaddr8(macaddr8, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_macaddr8(macaddr8, macaddr8, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_macaddr8(macaddr8, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS macaddr8_ops
|
||||||
|
DEFAULT FOR TYPE macaddr8 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 macaddr8_cmp(macaddr8, macaddr8),
|
||||||
|
FUNCTION 2 gin_extract_value_macaddr8(macaddr8, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_macaddr8(macaddr8, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_macaddr8(macaddr8, macaddr8, int2, internal),
|
||||||
|
STORAGE macaddr8;
|
|
@ -0,0 +1,689 @@
|
||||||
|
/* contrib/btree_gin/btree_gin--1.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "CREATE EXTENSION btree_gin" to load this file. \quit
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_btree_consistent(internal, int2, anyelement, int4, internal, internal)
|
||||||
|
RETURNS bool
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_int2(int2, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_int2(int2, int2, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_int2(int2, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS int2_ops
|
||||||
|
DEFAULT FOR TYPE int2 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btint2cmp(int2,int2),
|
||||||
|
FUNCTION 2 gin_extract_value_int2(int2, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_int2(int2, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_int2(int2,int2,int2, internal),
|
||||||
|
STORAGE int2;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_int4(int4, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_int4(int4, int4, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_int4(int4, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS int4_ops
|
||||||
|
DEFAULT FOR TYPE int4 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btint4cmp(int4,int4),
|
||||||
|
FUNCTION 2 gin_extract_value_int4(int4, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_int4(int4, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_int4(int4,int4,int2, internal),
|
||||||
|
STORAGE int4;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_int8(int8, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_int8(int8, int8, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_int8(int8, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS int8_ops
|
||||||
|
DEFAULT FOR TYPE int8 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btint8cmp(int8,int8),
|
||||||
|
FUNCTION 2 gin_extract_value_int8(int8, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_int8(int8, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_int8(int8,int8,int2, internal),
|
||||||
|
STORAGE int8;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_float4(float4, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_float4(float4, float4, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_float4(float4, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS float4_ops
|
||||||
|
DEFAULT FOR TYPE float4 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btfloat4cmp(float4,float4),
|
||||||
|
FUNCTION 2 gin_extract_value_float4(float4, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_float4(float4, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_float4(float4,float4,int2, internal),
|
||||||
|
STORAGE float4;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_float8(float8, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_float8(float8, float8, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_float8(float8, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS float8_ops
|
||||||
|
DEFAULT FOR TYPE float8 USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btfloat8cmp(float8,float8),
|
||||||
|
FUNCTION 2 gin_extract_value_float8(float8, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_float8(float8, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_float8(float8,float8,int2, internal),
|
||||||
|
STORAGE float8;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_money(money, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_money(money, money, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_money(money, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS money_ops
|
||||||
|
DEFAULT FOR TYPE money USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 cash_cmp(money,money),
|
||||||
|
FUNCTION 2 gin_extract_value_money(money, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_money(money, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_money(money,money,int2, internal),
|
||||||
|
STORAGE money;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_oid(oid, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_oid(oid, oid, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_oid(oid, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS oid_ops
|
||||||
|
DEFAULT FOR TYPE oid USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btoidcmp(oid,oid),
|
||||||
|
FUNCTION 2 gin_extract_value_oid(oid, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_oid(oid, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_oid(oid,oid,int2, internal),
|
||||||
|
STORAGE oid;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_timestamp(timestamp, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_timestamp(timestamp, timestamp, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_timestamp(timestamp, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS timestamp_ops
|
||||||
|
DEFAULT FOR TYPE timestamp USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 timestamp_cmp(timestamp,timestamp),
|
||||||
|
FUNCTION 2 gin_extract_value_timestamp(timestamp, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_timestamp(timestamp, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_timestamp(timestamp,timestamp,int2, internal),
|
||||||
|
STORAGE timestamp;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_timestamptz(timestamptz, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_timestamptz(timestamptz, timestamptz, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_timestamptz(timestamptz, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS timestamptz_ops
|
||||||
|
DEFAULT FOR TYPE timestamptz USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 timestamptz_cmp(timestamptz,timestamptz),
|
||||||
|
FUNCTION 2 gin_extract_value_timestamptz(timestamptz, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_timestamptz(timestamptz, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_timestamptz(timestamptz,timestamptz,int2, internal),
|
||||||
|
STORAGE timestamptz;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_time(time, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_time(time, time, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_time(time, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS time_ops
|
||||||
|
DEFAULT FOR TYPE time USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 time_cmp(time,time),
|
||||||
|
FUNCTION 2 gin_extract_value_time(time, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_time(time, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_time(time,time,int2, internal),
|
||||||
|
STORAGE time;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_timetz(timetz, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_timetz(timetz, timetz, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_timetz(timetz, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS timetz_ops
|
||||||
|
DEFAULT FOR TYPE timetz USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 timetz_cmp(timetz,timetz),
|
||||||
|
FUNCTION 2 gin_extract_value_timetz(timetz, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_timetz(timetz, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_timetz(timetz,timetz,int2, internal),
|
||||||
|
STORAGE timetz;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_date(date, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_date(date, date, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_date(date, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS date_ops
|
||||||
|
DEFAULT FOR TYPE date USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 date_cmp(date,date),
|
||||||
|
FUNCTION 2 gin_extract_value_date(date, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_date(date, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_date(date,date,int2, internal),
|
||||||
|
STORAGE date;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_interval(interval, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_interval(interval, interval, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_interval(interval, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS interval_ops
|
||||||
|
DEFAULT FOR TYPE interval USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 interval_cmp(interval,interval),
|
||||||
|
FUNCTION 2 gin_extract_value_interval(interval, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_interval(interval, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_interval(interval,interval,int2, internal),
|
||||||
|
STORAGE interval;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_macaddr(macaddr, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_macaddr(macaddr, macaddr, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_macaddr(macaddr, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS macaddr_ops
|
||||||
|
DEFAULT FOR TYPE macaddr USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 macaddr_cmp(macaddr,macaddr),
|
||||||
|
FUNCTION 2 gin_extract_value_macaddr(macaddr, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_macaddr(macaddr, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_macaddr(macaddr,macaddr,int2, internal),
|
||||||
|
STORAGE macaddr;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_inet(inet, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_inet(inet, inet, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_inet(inet, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS inet_ops
|
||||||
|
DEFAULT FOR TYPE inet USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 network_cmp(inet,inet),
|
||||||
|
FUNCTION 2 gin_extract_value_inet(inet, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_inet(inet, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_inet(inet,inet,int2, internal),
|
||||||
|
STORAGE inet;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_cidr(cidr, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_cidr(cidr, cidr, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_cidr(cidr, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS cidr_ops
|
||||||
|
DEFAULT FOR TYPE cidr USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <(inet,inet),
|
||||||
|
OPERATOR 2 <=(inet,inet),
|
||||||
|
OPERATOR 3 =(inet,inet),
|
||||||
|
OPERATOR 4 >=(inet,inet),
|
||||||
|
OPERATOR 5 >(inet,inet),
|
||||||
|
FUNCTION 1 network_cmp(inet,inet),
|
||||||
|
FUNCTION 2 gin_extract_value_cidr(cidr, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_cidr(cidr, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_cidr(cidr,cidr,int2, internal),
|
||||||
|
STORAGE cidr;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_text(text, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_text(text, text, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_text(text, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS text_ops
|
||||||
|
DEFAULT FOR TYPE text USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 bttextcmp(text,text),
|
||||||
|
FUNCTION 2 gin_extract_value_text(text, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_text(text, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_text(text,text,int2, internal),
|
||||||
|
STORAGE text;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS varchar_ops
|
||||||
|
DEFAULT FOR TYPE varchar USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <(text,text),
|
||||||
|
OPERATOR 2 <=(text,text),
|
||||||
|
OPERATOR 3 =(text,text),
|
||||||
|
OPERATOR 4 >=(text,text),
|
||||||
|
OPERATOR 5 >(text,text),
|
||||||
|
FUNCTION 1 bttextcmp(text,text),
|
||||||
|
FUNCTION 2 gin_extract_value_text(text, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_text(text, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_text(text,text,int2, internal),
|
||||||
|
STORAGE varchar;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_char("char", internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_char("char", "char", int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_char("char", internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS char_ops
|
||||||
|
DEFAULT FOR TYPE "char" USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btcharcmp("char","char"),
|
||||||
|
FUNCTION 2 gin_extract_value_char("char", internal),
|
||||||
|
FUNCTION 3 gin_extract_query_char("char", internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_char("char","char",int2, internal),
|
||||||
|
STORAGE "char";
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_bytea(bytea, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_bytea(bytea, bytea, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_bytea(bytea, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bytea_ops
|
||||||
|
DEFAULT FOR TYPE bytea USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 byteacmp(bytea,bytea),
|
||||||
|
FUNCTION 2 gin_extract_value_bytea(bytea, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_bytea(bytea, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_bytea(bytea,bytea,int2, internal),
|
||||||
|
STORAGE bytea;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_bit(bit, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_bit(bit, bit, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_bit(bit, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bit_ops
|
||||||
|
DEFAULT FOR TYPE bit USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 bitcmp(bit,bit),
|
||||||
|
FUNCTION 2 gin_extract_value_bit(bit, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_bit(bit, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_bit(bit,bit,int2, internal),
|
||||||
|
STORAGE bit;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_varbit(varbit, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_varbit(varbit, varbit, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_varbit(varbit, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS varbit_ops
|
||||||
|
DEFAULT FOR TYPE varbit USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 varbitcmp(varbit,varbit),
|
||||||
|
FUNCTION 2 gin_extract_value_varbit(varbit, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_varbit(varbit, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_varbit(varbit,varbit,int2, internal),
|
||||||
|
STORAGE varbit;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_numeric(numeric, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_numeric(numeric, numeric, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_numeric(numeric, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_numeric_cmp(numeric, numeric)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS numeric_ops
|
||||||
|
DEFAULT FOR TYPE numeric USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 gin_numeric_cmp(numeric,numeric),
|
||||||
|
FUNCTION 2 gin_extract_value_numeric(numeric, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_numeric(numeric, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_numeric(numeric,numeric,int2, internal),
|
||||||
|
STORAGE numeric;
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* contrib/btree_gin/btree_gin--1.1--1.2.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.1'" to load this file. \quit
|
||||||
|
|
||||||
|
--
|
||||||
|
--
|
||||||
|
--
|
||||||
|
-- enum ops
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_value_anyenum(anyenum, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_anyenum(anyenum, anyenum, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_anyenum(anyenum, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_enum_cmp(anyenum, anyenum)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS enum_ops
|
||||||
|
DEFAULT FOR TYPE anyenum USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 gin_enum_cmp(anyenum,anyenum),
|
||||||
|
FUNCTION 2 gin_extract_value_anyenum(anyenum, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_anyenum(anyenum, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_anyenum(anyenum,anyenum,int2, internal),
|
||||||
|
STORAGE anyenum;
|
|
@ -0,0 +1,128 @@
|
||||||
|
/* contrib/btree_gin/btree_gin--1.2--1.3.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "ALTER EXTENSION btree_gin UPDATE TO '1.3'" to load this file. \quit
|
||||||
|
|
||||||
|
-- uuid datatype support new in 1.3.
|
||||||
|
CREATE FUNCTION gin_extract_value_uuid(uuid, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_uuid(uuid, uuid, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_uuid(uuid, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS uuid_ops
|
||||||
|
DEFAULT FOR TYPE uuid USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 uuid_cmp(uuid,uuid),
|
||||||
|
FUNCTION 2 gin_extract_value_uuid(uuid, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_uuid(uuid, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_uuid(uuid,uuid,int2, internal),
|
||||||
|
STORAGE uuid;
|
||||||
|
|
||||||
|
-- name datatype support new in 1.3.
|
||||||
|
CREATE FUNCTION gin_extract_value_name(name, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_name(name, name, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_name(name, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS name_ops
|
||||||
|
DEFAULT FOR TYPE name USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btnamecmp(name,name),
|
||||||
|
FUNCTION 2 gin_extract_value_name(name, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_name(name, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_name(name,name,int2, internal),
|
||||||
|
STORAGE name;
|
||||||
|
|
||||||
|
-- bool datatype support new in 1.3.
|
||||||
|
CREATE FUNCTION gin_extract_value_bool(bool, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_bool(bool, bool, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_bool(bool, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bool_ops
|
||||||
|
DEFAULT FOR TYPE bool USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 btboolcmp(bool,bool),
|
||||||
|
FUNCTION 2 gin_extract_value_bool(bool, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_bool(bool, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_bool(bool,bool,int2, internal),
|
||||||
|
STORAGE bool;
|
||||||
|
|
||||||
|
-- bpchar datatype support new in 1.3.
|
||||||
|
CREATE FUNCTION gin_extract_value_bpchar(bpchar, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_compare_prefix_bpchar(bpchar, bpchar, int2, internal)
|
||||||
|
RETURNS int4
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE FUNCTION gin_extract_query_bpchar(bpchar, internal, int2, internal, internal)
|
||||||
|
RETURNS internal
|
||||||
|
AS 'MODULE_PATHNAME'
|
||||||
|
LANGUAGE C STRICT IMMUTABLE;
|
||||||
|
|
||||||
|
CREATE OPERATOR CLASS bpchar_ops
|
||||||
|
DEFAULT FOR TYPE bpchar USING gin
|
||||||
|
AS
|
||||||
|
OPERATOR 1 <,
|
||||||
|
OPERATOR 2 <=,
|
||||||
|
OPERATOR 3 =,
|
||||||
|
OPERATOR 4 >=,
|
||||||
|
OPERATOR 5 >,
|
||||||
|
FUNCTION 1 bpcharcmp(bpchar, bpchar),
|
||||||
|
FUNCTION 2 gin_extract_value_bpchar(bpchar, internal),
|
||||||
|
FUNCTION 3 gin_extract_query_bpchar(bpchar, internal, int2, internal, internal),
|
||||||
|
FUNCTION 4 gin_btree_consistent(internal, int2, anyelement, int4, internal, internal),
|
||||||
|
FUNCTION 5 gin_compare_prefix_bpchar(bpchar,bpchar,int2, internal),
|
||||||
|
STORAGE bpchar;
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* contrib/btree_gin/btree_gin--unpackaged--1.0.sql */
|
||||||
|
|
||||||
|
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
|
||||||
|
\echo Use "CREATE EXTENSION btree_gin FROM unpackaged" to load this file. \quit
|
||||||
|
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_btree_consistent(internal,smallint,anyelement,integer,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_int2(smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int2(smallint,smallint,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_int2(smallint,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family int2_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class int2_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_int4(integer,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int4(integer,integer,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_int4(integer,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family int4_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class int4_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_int8(bigint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int8(bigint,bigint,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_int8(bigint,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family int8_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class int8_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_float4(real,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_float4(real,real,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_float4(real,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family float4_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class float4_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_float8(double precision,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_float8(double precision,double precision,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_float8(double precision,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family float8_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class float8_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_money(money,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_money(money,money,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_money(money,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family money_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class money_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_oid(oid,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_oid(oid,oid,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_oid(oid,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family oid_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class oid_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_timestamp(timestamp without time zone,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timestamp(timestamp without time zone,timestamp without time zone,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_timestamp(timestamp without time zone,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family timestamp_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class timestamp_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_timestamptz(timestamp with time zone,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timestamptz(timestamp with time zone,timestamp with time zone,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_timestamptz(timestamp with time zone,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family timestamptz_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class timestamptz_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_time(time without time zone,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_time(time without time zone,time without time zone,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_time(time without time zone,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family time_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class time_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_timetz(time with time zone,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timetz(time with time zone,time with time zone,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_timetz(time with time zone,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family timetz_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class timetz_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_date(date,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_date(date,date,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_date(date,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family date_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class date_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_interval(interval,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_interval(interval,interval,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_interval(interval,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family interval_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class interval_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_macaddr(macaddr,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_macaddr(macaddr,macaddr,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_macaddr(macaddr,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family macaddr_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class macaddr_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_inet(inet,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_inet(inet,inet,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_inet(inet,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family inet_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class inet_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_cidr(cidr,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_cidr(cidr,cidr,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_cidr(cidr,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family cidr_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class cidr_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_text(text,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_text(text,text,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_text(text,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family text_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class text_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family varchar_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class varchar_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_char("char",internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_char("char","char",smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_char("char",internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family char_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class char_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_bytea(bytea,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_bytea(bytea,bytea,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_bytea(bytea,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family bytea_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class bytea_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_bit(bit,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_bit(bit,bit,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_bit(bit,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family bit_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class bit_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_varbit(bit varying,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_varbit(bit varying,bit varying,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_varbit(bit varying,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family varbit_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class varbit_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_value_numeric(numeric,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_compare_prefix_numeric(numeric,numeric,smallint,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_extract_query_numeric(numeric,internal,smallint,internal,internal);
|
||||||
|
ALTER EXTENSION btree_gin ADD function gin_numeric_cmp(numeric,numeric);
|
||||||
|
ALTER EXTENSION btree_gin ADD operator family numeric_ops using gin;
|
||||||
|
ALTER EXTENSION btree_gin ADD operator class numeric_ops using gin;
|
|
@ -0,0 +1,515 @@
|
||||||
|
/*
|
||||||
|
* contrib/btree_gin/btree_gin.c
|
||||||
|
*/
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "access/stratnum.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/bytea.h"
|
||||||
|
#include "utils/cash.h"
|
||||||
|
#include "utils/date.h"
|
||||||
|
#include "utils/float.h"
|
||||||
|
#include "utils/inet.h"
|
||||||
|
#include "utils/numeric.h"
|
||||||
|
#include "utils/timestamp.h"
|
||||||
|
#include "utils/varbit.h"
|
||||||
|
#include "utils/uuid.h"
|
||||||
|
|
||||||
|
PG_MODULE_MAGIC;
|
||||||
|
|
||||||
|
typedef struct QueryInfo
|
||||||
|
{
|
||||||
|
StrategyNumber strategy;
|
||||||
|
Datum datum;
|
||||||
|
bool is_varlena;
|
||||||
|
Datum (*typecmp) (FunctionCallInfo);
|
||||||
|
} QueryInfo;
|
||||||
|
|
||||||
|
/*** GIN support functions shared by all datatypes ***/
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
|
||||||
|
{
|
||||||
|
Datum datum = PG_GETARG_DATUM(0);
|
||||||
|
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
|
||||||
|
Datum *entries = (Datum *) palloc(sizeof(Datum));
|
||||||
|
|
||||||
|
if (is_varlena)
|
||||||
|
datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
|
||||||
|
entries[0] = datum;
|
||||||
|
*nentries = 1;
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
|
||||||
|
* BTEqualStrategyNumber we want to start the index scan at the
|
||||||
|
* supplied query datum, and work forward. For BTLessStrategyNumber
|
||||||
|
* and BTLessEqualStrategyNumber, we need to start at the leftmost
|
||||||
|
* key, and work forward until the supplied query datum (which must be
|
||||||
|
* sent along inside the QueryInfo structure).
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
gin_btree_extract_query(FunctionCallInfo fcinfo,
|
||||||
|
bool is_varlena,
|
||||||
|
Datum (*leftmostvalue) (void),
|
||||||
|
Datum (*typecmp) (FunctionCallInfo))
|
||||||
|
{
|
||||||
|
Datum datum = PG_GETARG_DATUM(0);
|
||||||
|
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
|
||||||
|
StrategyNumber strategy = PG_GETARG_UINT16(2);
|
||||||
|
bool **partialmatch = (bool **) PG_GETARG_POINTER(3);
|
||||||
|
Pointer **extra_data = (Pointer **) PG_GETARG_POINTER(4);
|
||||||
|
Datum *entries = (Datum *) palloc(sizeof(Datum));
|
||||||
|
QueryInfo *data = (QueryInfo *) palloc(sizeof(QueryInfo));
|
||||||
|
bool *ptr_partialmatch;
|
||||||
|
|
||||||
|
*nentries = 1;
|
||||||
|
ptr_partialmatch = *partialmatch = (bool *) palloc(sizeof(bool));
|
||||||
|
*ptr_partialmatch = false;
|
||||||
|
if (is_varlena)
|
||||||
|
datum = PointerGetDatum(PG_DETOAST_DATUM(datum));
|
||||||
|
data->strategy = strategy;
|
||||||
|
data->datum = datum;
|
||||||
|
data->is_varlena = is_varlena;
|
||||||
|
data->typecmp = typecmp;
|
||||||
|
*extra_data = (Pointer *) palloc(sizeof(Pointer));
|
||||||
|
**extra_data = (Pointer) data;
|
||||||
|
|
||||||
|
switch (strategy)
|
||||||
|
{
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
entries[0] = leftmostvalue();
|
||||||
|
*ptr_partialmatch = true;
|
||||||
|
break;
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
*ptr_partialmatch = true;
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
case BTEqualStrategyNumber:
|
||||||
|
entries[0] = datum;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d", strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_POINTER(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Datum a is a value from extract_query method and for BTLess*
|
||||||
|
* strategy it is a left-most value. So, use original datum from QueryInfo
|
||||||
|
* to decide to stop scanning or not. Datum b is always from index.
|
||||||
|
*/
|
||||||
|
static Datum
|
||||||
|
gin_btree_compare_prefix(FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
Datum a = PG_GETARG_DATUM(0);
|
||||||
|
Datum b = PG_GETARG_DATUM(1);
|
||||||
|
QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3);
|
||||||
|
int32 res,
|
||||||
|
cmp;
|
||||||
|
|
||||||
|
cmp = DatumGetInt32(CallerFInfoFunctionCall2(
|
||||||
|
data->typecmp,
|
||||||
|
fcinfo->flinfo,
|
||||||
|
PG_GET_COLLATION(),
|
||||||
|
(data->strategy == BTLessStrategyNumber ||
|
||||||
|
data->strategy == BTLessEqualStrategyNumber)
|
||||||
|
? data->datum : a,
|
||||||
|
b));
|
||||||
|
|
||||||
|
switch (data->strategy)
|
||||||
|
{
|
||||||
|
case BTLessStrategyNumber:
|
||||||
|
/* If original datum > indexed one then return match */
|
||||||
|
if (cmp > 0)
|
||||||
|
res = 0;
|
||||||
|
else
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
case BTLessEqualStrategyNumber:
|
||||||
|
/* The same except equality */
|
||||||
|
if (cmp >= 0)
|
||||||
|
res = 0;
|
||||||
|
else
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
case BTEqualStrategyNumber:
|
||||||
|
if (cmp != 0)
|
||||||
|
res = 1;
|
||||||
|
else
|
||||||
|
res = 0;
|
||||||
|
break;
|
||||||
|
case BTGreaterEqualStrategyNumber:
|
||||||
|
/* If original datum <= indexed one then return match */
|
||||||
|
if (cmp <= 0)
|
||||||
|
res = 0;
|
||||||
|
else
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
case BTGreaterStrategyNumber:
|
||||||
|
/* If original datum <= indexed one then return match */
|
||||||
|
/* If original datum == indexed one then continue scan */
|
||||||
|
if (cmp < 0)
|
||||||
|
res = 0;
|
||||||
|
else if (cmp == 0)
|
||||||
|
res = -1;
|
||||||
|
else
|
||||||
|
res = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unrecognized strategy number: %d",
|
||||||
|
data->strategy);
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_INT32(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(gin_btree_consistent);
|
||||||
|
Datum
|
||||||
|
gin_btree_consistent(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
bool *recheck = (bool *) PG_GETARG_POINTER(5);
|
||||||
|
|
||||||
|
*recheck = false;
|
||||||
|
PG_RETURN_BOOL(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** GIN_SUPPORT macro defines the datatype specific functions ***/
|
||||||
|
|
||||||
|
#define GIN_SUPPORT(type, is_varlena, leftmostvalue, typecmp) \
|
||||||
|
PG_FUNCTION_INFO_V1(gin_extract_value_##type); \
|
||||||
|
Datum \
|
||||||
|
gin_extract_value_##type(PG_FUNCTION_ARGS) \
|
||||||
|
{ \
|
||||||
|
return gin_btree_extract_value(fcinfo, is_varlena); \
|
||||||
|
} \
|
||||||
|
PG_FUNCTION_INFO_V1(gin_extract_query_##type); \
|
||||||
|
Datum \
|
||||||
|
gin_extract_query_##type(PG_FUNCTION_ARGS) \
|
||||||
|
{ \
|
||||||
|
return gin_btree_extract_query(fcinfo, \
|
||||||
|
is_varlena, leftmostvalue, typecmp); \
|
||||||
|
} \
|
||||||
|
PG_FUNCTION_INFO_V1(gin_compare_prefix_##type); \
|
||||||
|
Datum \
|
||||||
|
gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
|
||||||
|
{ \
|
||||||
|
return gin_btree_compare_prefix(fcinfo); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*** Datatype specifications ***/
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_int2(void)
|
||||||
|
{
|
||||||
|
return Int16GetDatum(SHRT_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_int4(void)
|
||||||
|
{
|
||||||
|
return Int32GetDatum(INT_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_int8(void)
|
||||||
|
{
|
||||||
|
return Int64GetDatum(PG_INT64_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_float4(void)
|
||||||
|
{
|
||||||
|
return Float4GetDatum(-get_float4_infinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_float8(void)
|
||||||
|
{
|
||||||
|
return Float8GetDatum(-get_float8_infinity());
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_money(void)
|
||||||
|
{
|
||||||
|
return Int64GetDatum(PG_INT64_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_oid(void)
|
||||||
|
{
|
||||||
|
return ObjectIdGetDatum(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_timestamp(void)
|
||||||
|
{
|
||||||
|
return TimestampGetDatum(DT_NOBEGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp)
|
||||||
|
|
||||||
|
GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_time(void)
|
||||||
|
{
|
||||||
|
return TimeADTGetDatum(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_timetz(void)
|
||||||
|
{
|
||||||
|
TimeTzADT *v = palloc(sizeof(TimeTzADT));
|
||||||
|
|
||||||
|
v->time = 0;
|
||||||
|
v->zone = -24 * 3600; /* XXX is that true? */
|
||||||
|
|
||||||
|
return TimeTzADTPGetDatum(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_date(void)
|
||||||
|
{
|
||||||
|
return DateADTGetDatum(DATEVAL_NOBEGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_interval(void)
|
||||||
|
{
|
||||||
|
Interval *v = palloc(sizeof(Interval));
|
||||||
|
|
||||||
|
v->time = DT_NOBEGIN;
|
||||||
|
v->day = 0;
|
||||||
|
v->month = 0;
|
||||||
|
return IntervalPGetDatum(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_macaddr(void)
|
||||||
|
{
|
||||||
|
macaddr *v = palloc0(sizeof(macaddr));
|
||||||
|
|
||||||
|
return MacaddrPGetDatum(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_macaddr8(void)
|
||||||
|
{
|
||||||
|
macaddr8 *v = palloc0(sizeof(macaddr8));
|
||||||
|
|
||||||
|
return Macaddr8PGetDatum(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(macaddr8, false, leftmostvalue_macaddr8, macaddr8_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_inet(void)
|
||||||
|
{
|
||||||
|
return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp)
|
||||||
|
|
||||||
|
GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_text(void)
|
||||||
|
{
|
||||||
|
return PointerGetDatum(cstring_to_text_with_len("", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp)
|
||||||
|
|
||||||
|
GIN_SUPPORT(bpchar, true, leftmostvalue_text, bpcharcmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_char(void)
|
||||||
|
{
|
||||||
|
return CharGetDatum(SCHAR_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp)
|
||||||
|
|
||||||
|
GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_bit(void)
|
||||||
|
{
|
||||||
|
return DirectFunctionCall3(bit_in,
|
||||||
|
CStringGetDatum(""),
|
||||||
|
ObjectIdGetDatum(0),
|
||||||
|
Int32GetDatum(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_varbit(void)
|
||||||
|
{
|
||||||
|
return DirectFunctionCall3(varbit_in,
|
||||||
|
CStringGetDatum(""),
|
||||||
|
ObjectIdGetDatum(0),
|
||||||
|
Int32GetDatum(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Numeric type hasn't a real left-most value, so we use PointerGetDatum(NULL)
|
||||||
|
* (*not* a SQL NULL) to represent that. We can get away with that because
|
||||||
|
* the value returned by our leftmostvalue function will never be stored in
|
||||||
|
* the index nor passed to anything except our compare and prefix-comparison
|
||||||
|
* functions. The same trick could be used for other pass-by-reference types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NUMERIC_IS_LEFTMOST(x) ((x) == NULL)
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(gin_numeric_cmp);
|
||||||
|
|
||||||
|
Datum
|
||||||
|
gin_numeric_cmp(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Numeric a = (Numeric) PG_GETARG_POINTER(0);
|
||||||
|
Numeric b = (Numeric) PG_GETARG_POINTER(1);
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (NUMERIC_IS_LEFTMOST(a))
|
||||||
|
{
|
||||||
|
res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1;
|
||||||
|
}
|
||||||
|
else if (NUMERIC_IS_LEFTMOST(b))
|
||||||
|
{
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = DatumGetInt32(DirectFunctionCall2(numeric_cmp,
|
||||||
|
NumericGetDatum(a),
|
||||||
|
NumericGetDatum(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_INT32(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_numeric(void)
|
||||||
|
{
|
||||||
|
return PointerGetDatum(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a similar trick to that used for numeric for enums, since we don't
|
||||||
|
* actually know the leftmost value of any enum without knowing the concrete
|
||||||
|
* type, so we use a dummy leftmost value of InvalidOid.
|
||||||
|
*
|
||||||
|
* Note that we use CallerFInfoFunctionCall2 here so that enum_cmp
|
||||||
|
* gets a valid fn_extra to work with. Unlike most other type comparison
|
||||||
|
* routines it needs it, so we can't use DirectFunctionCall2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ENUM_IS_LEFTMOST(x) ((x) == InvalidOid)
|
||||||
|
|
||||||
|
PG_FUNCTION_INFO_V1(gin_enum_cmp);
|
||||||
|
|
||||||
|
Datum
|
||||||
|
gin_enum_cmp(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Oid a = PG_GETARG_OID(0);
|
||||||
|
Oid b = PG_GETARG_OID(1);
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if (ENUM_IS_LEFTMOST(a))
|
||||||
|
{
|
||||||
|
res = (ENUM_IS_LEFTMOST(b)) ? 0 : -1;
|
||||||
|
}
|
||||||
|
else if (ENUM_IS_LEFTMOST(b))
|
||||||
|
{
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res = DatumGetInt32(CallerFInfoFunctionCall2(
|
||||||
|
enum_cmp,
|
||||||
|
fcinfo->flinfo,
|
||||||
|
PG_GET_COLLATION(),
|
||||||
|
ObjectIdGetDatum(a),
|
||||||
|
ObjectIdGetDatum(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PG_RETURN_INT32(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_enum(void)
|
||||||
|
{
|
||||||
|
return ObjectIdGetDatum(InvalidOid);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(anyenum, false, leftmostvalue_enum, gin_enum_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_uuid(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* palloc0 will create the UUID with all zeroes:
|
||||||
|
* "00000000-0000-0000-0000-000000000000"
|
||||||
|
*/
|
||||||
|
pg_uuid_t *retval = (pg_uuid_t *) palloc0(sizeof(pg_uuid_t));
|
||||||
|
|
||||||
|
return UUIDPGetDatum(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(uuid, false, leftmostvalue_uuid, uuid_cmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_name(void)
|
||||||
|
{
|
||||||
|
NameData *result = (NameData *) palloc0(NAMEDATALEN);
|
||||||
|
|
||||||
|
return NameGetDatum(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(name, false, leftmostvalue_name, btnamecmp)
|
||||||
|
|
||||||
|
static Datum
|
||||||
|
leftmostvalue_bool(void)
|
||||||
|
{
|
||||||
|
return BoolGetDatum(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIN_SUPPORT(bool, false, leftmostvalue_bool, btboolcmp)
|
|
@ -0,0 +1,5 @@
|
||||||
|
# btree_gin extension
|
||||||
|
comment = 'support for indexing common datatypes in GIN'
|
||||||
|
default_version = '1.3'
|
||||||
|
module_pathname = '$libdir/btree_gin'
|
||||||
|
relocatable = true
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_bit (
|
||||||
|
i bit(3)
|
||||||
|
);
|
||||||
|
INSERT INTO test_bit VALUES ('001'),('010'),('011'),('100'),('101'),('110');
|
||||||
|
CREATE INDEX idx_bit ON test_bit USING gin (i);
|
||||||
|
SELECT * FROM test_bit WHERE i<'100'::bit(3) ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
001
|
||||||
|
010
|
||||||
|
011
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bit WHERE i<='100'::bit(3) ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
001
|
||||||
|
010
|
||||||
|
011
|
||||||
|
100
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bit WHERE i='100'::bit(3) ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
100
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bit WHERE i>='100'::bit(3) ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
100
|
||||||
|
101
|
||||||
|
110
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bit WHERE i>'100'::bit(3) ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
101
|
||||||
|
110
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_bool (
|
||||||
|
i boolean
|
||||||
|
);
|
||||||
|
INSERT INTO test_bool VALUES (false),(true),(null);
|
||||||
|
CREATE INDEX idx_bool ON test_bool USING gin (i);
|
||||||
|
SELECT * FROM test_bool WHERE i<true ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i<=true ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
f
|
||||||
|
t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i=true ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i>=true ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i>true ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i<false ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i<=false ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i=false ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
f
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i>=false ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
f
|
||||||
|
t
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bool WHERE i>false ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i<true ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bool
|
||||||
|
Recheck Cond: (i < true)
|
||||||
|
-> Bitmap Index Scan on idx_bool
|
||||||
|
Index Cond: (i < true)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i<=true ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bool
|
||||||
|
Recheck Cond: (i <= true)
|
||||||
|
-> Bitmap Index Scan on idx_bool
|
||||||
|
Index Cond: (i <= true)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i=true ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Seq Scan on test_bool
|
||||||
|
Filter: i
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i>=true ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bool
|
||||||
|
Recheck Cond: (i >= true)
|
||||||
|
-> Bitmap Index Scan on idx_bool
|
||||||
|
Index Cond: (i >= true)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bool WHERE i>true ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bool
|
||||||
|
Recheck Cond: (i > true)
|
||||||
|
-> Bitmap Index Scan on idx_bool
|
||||||
|
Index Cond: (i > true)
|
||||||
|
(6 rows)
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_bpchar (
|
||||||
|
i char(10)
|
||||||
|
);
|
||||||
|
INSERT INTO test_bpchar VALUES ('a'),('ab'),('abc'),('abc '),('abb'),('axy'),('xyz'),('xyz ');
|
||||||
|
CREATE INDEX idx_bpchar ON test_bpchar USING gin (i);
|
||||||
|
SELECT * FROM test_bpchar WHERE i<'abc' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
a
|
||||||
|
ab
|
||||||
|
abb
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bpchar WHERE i<='abc' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
a
|
||||||
|
ab
|
||||||
|
abb
|
||||||
|
abc
|
||||||
|
abc
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bpchar WHERE i='abc' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
abc
|
||||||
|
abc
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bpchar WHERE i='abc ' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
abc
|
||||||
|
abc
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bpchar WHERE i>='abc' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
abc
|
||||||
|
abc
|
||||||
|
axy
|
||||||
|
xyz
|
||||||
|
xyz
|
||||||
|
(5 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bpchar WHERE i>'abc' ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
axy
|
||||||
|
xyz
|
||||||
|
xyz
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bpchar WHERE i<'abc' ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bpchar
|
||||||
|
Recheck Cond: (i < 'abc'::bpchar)
|
||||||
|
-> Bitmap Index Scan on idx_bpchar
|
||||||
|
Index Cond: (i < 'abc'::bpchar)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bpchar WHERE i<='abc' ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bpchar
|
||||||
|
Recheck Cond: (i <= 'abc'::bpchar)
|
||||||
|
-> Bitmap Index Scan on idx_bpchar
|
||||||
|
Index Cond: (i <= 'abc'::bpchar)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bpchar WHERE i='abc' ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------
|
||||||
|
Bitmap Heap Scan on test_bpchar
|
||||||
|
Recheck Cond: (i = 'abc'::bpchar)
|
||||||
|
-> Bitmap Index Scan on idx_bpchar
|
||||||
|
Index Cond: (i = 'abc'::bpchar)
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bpchar WHERE i>='abc' ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
------------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bpchar
|
||||||
|
Recheck Cond: (i >= 'abc'::bpchar)
|
||||||
|
-> Bitmap Index Scan on idx_bpchar
|
||||||
|
Index Cond: (i >= 'abc'::bpchar)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
EXPLAIN (COSTS OFF) SELECT * FROM test_bpchar WHERE i>'abc' ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_bpchar
|
||||||
|
Recheck Cond: (i > 'abc'::bpchar)
|
||||||
|
-> Bitmap Index Scan on idx_bpchar
|
||||||
|
Index Cond: (i > 'abc'::bpchar)
|
||||||
|
(6 rows)
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
-- ensure consistent test output regardless of the default bytea format
|
||||||
|
SET bytea_output TO escape;
|
||||||
|
CREATE TABLE test_bytea (
|
||||||
|
i bytea
|
||||||
|
);
|
||||||
|
INSERT INTO test_bytea VALUES ('a'),('ab'),('abc'),('abb'),('axy'),('xyz');
|
||||||
|
CREATE INDEX idx_bytea ON test_bytea USING gin (i);
|
||||||
|
SELECT * FROM test_bytea WHERE i<'abc'::bytea ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
a
|
||||||
|
ab
|
||||||
|
abb
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bytea WHERE i<='abc'::bytea ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
a
|
||||||
|
ab
|
||||||
|
abb
|
||||||
|
abc
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bytea WHERE i='abc'::bytea ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
abc
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_bytea WHERE i>='abc'::bytea ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
abc
|
||||||
|
axy
|
||||||
|
xyz
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_bytea WHERE i>'abc'::bytea ORDER BY i;
|
||||||
|
i
|
||||||
|
-----
|
||||||
|
axy
|
||||||
|
xyz
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_char (
|
||||||
|
i "char"
|
||||||
|
);
|
||||||
|
INSERT INTO test_char VALUES ('a'),('b'),('c'),('d'),('e'),('f');
|
||||||
|
CREATE INDEX idx_char ON test_char USING gin (i);
|
||||||
|
SELECT * FROM test_char WHERE i<'d'::"char" ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_char WHERE i<='d'::"char" ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_char WHERE i='d'::"char" ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
d
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_char WHERE i>='d'::"char" ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
d
|
||||||
|
e
|
||||||
|
f
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_char WHERE i>'d'::"char" ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
e
|
||||||
|
f
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_cidr (
|
||||||
|
i cidr
|
||||||
|
);
|
||||||
|
INSERT INTO test_cidr VALUES
|
||||||
|
( '1.2.3.4' ),
|
||||||
|
( '1.2.4.4' ),
|
||||||
|
( '1.2.5.4' ),
|
||||||
|
( '1.2.6.4' ),
|
||||||
|
( '1.2.7.4' ),
|
||||||
|
( '1.2.8.4' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_cidr ON test_cidr USING gin (i);
|
||||||
|
SELECT * FROM test_cidr WHERE i<'1.2.6.4'::cidr ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.3.4/32
|
||||||
|
1.2.4.4/32
|
||||||
|
1.2.5.4/32
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_cidr WHERE i<='1.2.6.4'::cidr ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.3.4/32
|
||||||
|
1.2.4.4/32
|
||||||
|
1.2.5.4/32
|
||||||
|
1.2.6.4/32
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_cidr WHERE i='1.2.6.4'::cidr ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.6.4/32
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_cidr WHERE i>='1.2.6.4'::cidr ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.6.4/32
|
||||||
|
1.2.7.4/32
|
||||||
|
1.2.8.4/32
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_cidr WHERE i>'1.2.6.4'::cidr ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.7.4/32
|
||||||
|
1.2.8.4/32
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_date (
|
||||||
|
i date
|
||||||
|
);
|
||||||
|
INSERT INTO test_date VALUES
|
||||||
|
( '2004-10-23' ),
|
||||||
|
( '2004-10-24' ),
|
||||||
|
( '2004-10-25' ),
|
||||||
|
( '2004-10-26' ),
|
||||||
|
( '2004-10-27' ),
|
||||||
|
( '2004-10-28' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_date ON test_date USING gin (i);
|
||||||
|
SELECT * FROM test_date WHERE i<'2004-10-26'::date ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
10-23-2004
|
||||||
|
10-24-2004
|
||||||
|
10-25-2004
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_date WHERE i<='2004-10-26'::date ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
10-23-2004
|
||||||
|
10-24-2004
|
||||||
|
10-25-2004
|
||||||
|
10-26-2004
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_date WHERE i='2004-10-26'::date ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
10-26-2004
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_date WHERE i>='2004-10-26'::date ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
10-26-2004
|
||||||
|
10-27-2004
|
||||||
|
10-28-2004
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_date WHERE i>'2004-10-26'::date ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
10-27-2004
|
||||||
|
10-28-2004
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TYPE rainbow AS ENUM ('r','o','y','g','b','i','v');
|
||||||
|
CREATE TABLE test_enum (
|
||||||
|
i rainbow
|
||||||
|
);
|
||||||
|
INSERT INTO test_enum VALUES ('v'),('y'),('r'),('g'),('o'),('i'),('b');
|
||||||
|
CREATE INDEX idx_enum ON test_enum USING gin (i);
|
||||||
|
SELECT * FROM test_enum WHERE i<'g'::rainbow ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
r
|
||||||
|
o
|
||||||
|
y
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_enum WHERE i<='g'::rainbow ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
r
|
||||||
|
o
|
||||||
|
y
|
||||||
|
g
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_enum WHERE i='g'::rainbow ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
g
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
g
|
||||||
|
b
|
||||||
|
i
|
||||||
|
v
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_enum WHERE i>'g'::rainbow ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
b
|
||||||
|
i
|
||||||
|
v
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
explain (costs off) SELECT * FROM test_enum WHERE i>='g'::rainbow ORDER BY i;
|
||||||
|
QUERY PLAN
|
||||||
|
-----------------------------------------------
|
||||||
|
Sort
|
||||||
|
Sort Key: i
|
||||||
|
-> Bitmap Heap Scan on test_enum
|
||||||
|
Recheck Cond: (i >= 'g'::rainbow)
|
||||||
|
-> Bitmap Index Scan on idx_enum
|
||||||
|
Index Cond: (i >= 'g'::rainbow)
|
||||||
|
(6 rows)
|
||||||
|
|
||||||
|
-- make sure we handle the non-evenly-numbered oid case for enums
|
||||||
|
create type e as enum ('0', '2', '3');
|
||||||
|
alter type e add value '1' after '0';
|
||||||
|
create table t as select (i % 4)::text::e from generate_series(0, 100000) as i;
|
||||||
|
create index on t using gin (e);
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_float4 (
|
||||||
|
i float4
|
||||||
|
);
|
||||||
|
INSERT INTO test_float4 VALUES (-2),(-1),(0),(1),(2),(3);
|
||||||
|
CREATE INDEX idx_float4 ON test_float4 USING gin (i);
|
||||||
|
SELECT * FROM test_float4 WHERE i<1::float4 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float4 WHERE i<=1::float4 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float4 WHERE i=1::float4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_float4 WHERE i>=1::float4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float4 WHERE i>1::float4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_float8 (
|
||||||
|
i float8
|
||||||
|
);
|
||||||
|
INSERT INTO test_float8 VALUES (-2),(-1),(0),(1),(2),(3);
|
||||||
|
CREATE INDEX idx_float8 ON test_float8 USING gin (i);
|
||||||
|
SELECT * FROM test_float8 WHERE i<1::float8 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float8 WHERE i<=1::float8 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float8 WHERE i=1::float8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_float8 WHERE i>=1::float8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_float8 WHERE i>1::float8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_inet (
|
||||||
|
i inet
|
||||||
|
);
|
||||||
|
INSERT INTO test_inet VALUES
|
||||||
|
( '1.2.3.4/16' ),
|
||||||
|
( '1.2.4.4/16' ),
|
||||||
|
( '1.2.5.4/16' ),
|
||||||
|
( '1.2.6.4/16' ),
|
||||||
|
( '1.2.7.4/16' ),
|
||||||
|
( '1.2.8.4/16' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_inet ON test_inet USING gin (i);
|
||||||
|
SELECT * FROM test_inet WHERE i<'1.2.6.4/16'::inet ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.3.4/16
|
||||||
|
1.2.4.4/16
|
||||||
|
1.2.5.4/16
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_inet WHERE i<='1.2.6.4/16'::inet ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.3.4/16
|
||||||
|
1.2.4.4/16
|
||||||
|
1.2.5.4/16
|
||||||
|
1.2.6.4/16
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_inet WHERE i='1.2.6.4/16'::inet ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.6.4/16
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_inet WHERE i>='1.2.6.4/16'::inet ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.6.4/16
|
||||||
|
1.2.7.4/16
|
||||||
|
1.2.8.4/16
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_inet WHERE i>'1.2.6.4/16'::inet ORDER BY i;
|
||||||
|
i
|
||||||
|
------------
|
||||||
|
1.2.7.4/16
|
||||||
|
1.2.8.4/16
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
CREATE EXTENSION btree_gin;
|
||||||
|
-- Check whether any of our opclasses fail amvalidate
|
||||||
|
SELECT amname, opcname
|
||||||
|
FROM pg_opclass opc LEFT JOIN pg_am am ON am.oid = opcmethod
|
||||||
|
WHERE opc.oid >= 16384 AND NOT amvalidate(opc.oid);
|
||||||
|
amname | opcname
|
||||||
|
--------+---------
|
||||||
|
(0 rows)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_int2 (
|
||||||
|
i int2
|
||||||
|
);
|
||||||
|
INSERT INTO test_int2 VALUES (-2),(-1),(0),(1),(2),(3);
|
||||||
|
CREATE INDEX idx_int2 ON test_int2 USING gin (i);
|
||||||
|
SELECT * FROM test_int2 WHERE i<1::int2 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int2 WHERE i<=1::int2 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int2 WHERE i=1::int2 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_int2 WHERE i>=1::int2 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int2 WHERE i>1::int2 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_int4 (
|
||||||
|
i int4
|
||||||
|
);
|
||||||
|
INSERT INTO test_int4 VALUES (-2),(-1),(0),(1),(2),(3);
|
||||||
|
CREATE INDEX idx_int4 ON test_int4 USING gin (i);
|
||||||
|
SELECT * FROM test_int4 WHERE i<1::int4 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int4 WHERE i<=1::int4 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int4 WHERE i=1::int4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_int4 WHERE i>=1::int4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int4 WHERE i>1::int4 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_int8 (
|
||||||
|
i int8
|
||||||
|
);
|
||||||
|
INSERT INTO test_int8 VALUES (-2),(-1),(0),(1),(2),(3);
|
||||||
|
CREATE INDEX idx_int8 ON test_int8 USING gin (i);
|
||||||
|
SELECT * FROM test_int8 WHERE i<1::int8 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int8 WHERE i<=1::int8 ORDER BY i;
|
||||||
|
i
|
||||||
|
----
|
||||||
|
-2
|
||||||
|
-1
|
||||||
|
0
|
||||||
|
1
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int8 WHERE i=1::int8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_int8 WHERE i>=1::int8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_int8 WHERE i>1::int8 ORDER BY i;
|
||||||
|
i
|
||||||
|
---
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_interval (
|
||||||
|
i interval
|
||||||
|
);
|
||||||
|
INSERT INTO test_interval VALUES
|
||||||
|
( '03:55:08' ),
|
||||||
|
( '04:55:08' ),
|
||||||
|
( '05:55:08' ),
|
||||||
|
( '08:55:08' ),
|
||||||
|
( '09:55:08' ),
|
||||||
|
( '10:55:08' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_interval ON test_interval USING gin (i);
|
||||||
|
SELECT * FROM test_interval WHERE i<'08:55:08'::interval ORDER BY i;
|
||||||
|
i
|
||||||
|
--------------------------
|
||||||
|
@ 3 hours 55 mins 8 secs
|
||||||
|
@ 4 hours 55 mins 8 secs
|
||||||
|
@ 5 hours 55 mins 8 secs
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_interval WHERE i<='08:55:08'::interval ORDER BY i;
|
||||||
|
i
|
||||||
|
--------------------------
|
||||||
|
@ 3 hours 55 mins 8 secs
|
||||||
|
@ 4 hours 55 mins 8 secs
|
||||||
|
@ 5 hours 55 mins 8 secs
|
||||||
|
@ 8 hours 55 mins 8 secs
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_interval WHERE i='08:55:08'::interval ORDER BY i;
|
||||||
|
i
|
||||||
|
--------------------------
|
||||||
|
@ 8 hours 55 mins 8 secs
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_interval WHERE i>='08:55:08'::interval ORDER BY i;
|
||||||
|
i
|
||||||
|
---------------------------
|
||||||
|
@ 8 hours 55 mins 8 secs
|
||||||
|
@ 9 hours 55 mins 8 secs
|
||||||
|
@ 10 hours 55 mins 8 secs
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_interval WHERE i>'08:55:08'::interval ORDER BY i;
|
||||||
|
i
|
||||||
|
---------------------------
|
||||||
|
@ 9 hours 55 mins 8 secs
|
||||||
|
@ 10 hours 55 mins 8 secs
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_macaddr (
|
||||||
|
i macaddr
|
||||||
|
);
|
||||||
|
INSERT INTO test_macaddr VALUES
|
||||||
|
( '22:00:5c:03:55:08' ),
|
||||||
|
( '22:00:5c:04:55:08' ),
|
||||||
|
( '22:00:5c:05:55:08' ),
|
||||||
|
( '22:00:5c:08:55:08' ),
|
||||||
|
( '22:00:5c:09:55:08' ),
|
||||||
|
( '22:00:5c:10:55:08' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_macaddr ON test_macaddr USING gin (i);
|
||||||
|
SELECT * FROM test_macaddr WHERE i<'22:00:5c:08:55:08'::macaddr ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------
|
||||||
|
22:00:5c:03:55:08
|
||||||
|
22:00:5c:04:55:08
|
||||||
|
22:00:5c:05:55:08
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr WHERE i<='22:00:5c:08:55:08'::macaddr ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------
|
||||||
|
22:00:5c:03:55:08
|
||||||
|
22:00:5c:04:55:08
|
||||||
|
22:00:5c:05:55:08
|
||||||
|
22:00:5c:08:55:08
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr WHERE i='22:00:5c:08:55:08'::macaddr ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------
|
||||||
|
22:00:5c:08:55:08
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr WHERE i>='22:00:5c:08:55:08'::macaddr ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------
|
||||||
|
22:00:5c:08:55:08
|
||||||
|
22:00:5c:09:55:08
|
||||||
|
22:00:5c:10:55:08
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr WHERE i>'22:00:5c:08:55:08'::macaddr ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------
|
||||||
|
22:00:5c:09:55:08
|
||||||
|
22:00:5c:10:55:08
|
||||||
|
(2 rows)
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
set enable_seqscan=off;
|
||||||
|
CREATE TABLE test_macaddr8 (
|
||||||
|
i macaddr8
|
||||||
|
);
|
||||||
|
INSERT INTO test_macaddr8 VALUES
|
||||||
|
( '22:00:5c:03:55:08:01:02' ),
|
||||||
|
( '22:00:5c:04:55:08:01:02' ),
|
||||||
|
( '22:00:5c:05:55:08:01:02' ),
|
||||||
|
( '22:00:5c:08:55:08:01:02' ),
|
||||||
|
( '22:00:5c:09:55:08:01:02' ),
|
||||||
|
( '22:00:5c:10:55:08:01:02' )
|
||||||
|
;
|
||||||
|
CREATE INDEX idx_macaddr8 ON test_macaddr8 USING gin (i);
|
||||||
|
SELECT * FROM test_macaddr8 WHERE i<'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------------
|
||||||
|
22:00:5c:03:55:08:01:02
|
||||||
|
22:00:5c:04:55:08:01:02
|
||||||
|
22:00:5c:05:55:08:01:02
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr8 WHERE i<='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------------
|
||||||
|
22:00:5c:03:55:08:01:02
|
||||||
|
22:00:5c:04:55:08:01:02
|
||||||
|
22:00:5c:05:55:08:01:02
|
||||||
|
22:00:5c:08:55:08:01:02
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr8 WHERE i='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------------
|
||||||
|
22:00:5c:08:55:08:01:02
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr8 WHERE i>='22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------------
|
||||||
|
22:00:5c:08:55:08:01:02
|
||||||
|
22:00:5c:09:55:08:01:02
|
||||||
|
22:00:5c:10:55:08:01:02
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT * FROM test_macaddr8 WHERE i>'22:00:5c:08:55:08:01:02'::macaddr8 ORDER BY i;
|
||||||
|
i
|
||||||
|
-------------------------
|
||||||
|
22:00:5c:09:55:08:01:02
|
||||||
|
22:00:5c:10:55:08:01:02
|
||||||
|
(2 rows)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue