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