INIT
commit
10ecb96088
|
|
@ -0,0 +1,143 @@
|
|||
#
|
||||
# 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 external 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 $@
|
||||
# POLAR polar added extension
|
||||
$(MAKE) -C external distclean
|
||||
# POLAR end
|
||||
$(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-tests: | temp-install
|
||||
check check-tests installcheck installcheck-parallel installcheck-tests polar-check polar-installcheck polar-installcheck-px quick-installcheck quick-installcheck-px: CHECKPREP_TOP=src/test/regress
|
||||
check check-tests installcheck installcheck-parallel installcheck-tests polar-check polar-installcheck polar-installcheck-px quick-installcheck quick-installcheck-px: submake-generated-headers
|
||||
$(MAKE) -C src/test/regress $@
|
||||
|
||||
# Polar for polardb_build.sh
|
||||
$(call recurse,check-world,src/test src/pl contrib external,check)
|
||||
$(call recurse,checkprep, src/test src/pl src/interfaces/ecpg contrib src/bin)
|
||||
|
||||
# Polar for polardb_build.sh
|
||||
$(call recurse,installcheck-world,src/test src/pl contrib external,installcheck)
|
||||
px-regress-file=src/test/authentication src/test/encryption src/test/isolation src/test/kerberos src/test/ldap src/test/locale/
|
||||
px-regress-file+=src/test/mb src/test/modules/ src/test/polar_px src/test/polar_pl src/test/riority_replication
|
||||
px-regress-file+=src/test/python src/test/recovery src/test/rename_wal_ready_file src/test/ssl src/test/subscription src/test/thread
|
||||
$(call recurse,installcheck-world-px,$(px-regress-file) src/pl src/interfaces/ecpg contrib external,installcheck)
|
||||
$(call recurse,install-tests,src/test/regress,install-tests)
|
||||
|
||||
GNUmakefile: GNUmakefile.in $(top_builddir)/config.status
|
||||
./config.status $@
|
||||
|
||||
|
||||
##########################################################################
|
||||
|
||||
distdir = postgresql-$(VERSION)
|
||||
dummy = =install=
|
||||
garbage = =* "#"* ."#"* *~* *.orig *.rej core postgresql-*
|
||||
|
||||
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."
|
||||
|
||||
.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,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2020] [Alibaba Group Holding Limited]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -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,50 @@
|
|||
PolarDB for PostgreSQL
|
||||
|
||||
Copyright (c) 2021-2022, Alibaba Group Holding Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Other dependencies and licenses:
|
||||
|
||||
PostgreSQL Database Management System
|
||||
(formerly known as Postgres, then as Postgres95)
|
||||
|
||||
Portions Copyright (c) 1996-2019, 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.
|
||||
|
||||
Postgres-XL
|
||||
Portions Copyright (c) 2015-2016, 2ndQuadrant Ltd
|
||||
Portions Copyright (c) 2012-2015, TransLattice, Inc.
|
||||
Portions Copyright (c) 2010-2017, Postgres-XC Development Group
|
||||
|
||||
TBase Cluster Database Management System
|
||||
Copyright (C) 2019 THL A29 Limited, a Tencent company.
|
||||
TBase is licensed under the BSD 3-Clause License.
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
<div align="center">
|
||||
|
||||
[](https://developer.aliyun.com/topic/polardb-for-pg)
|
||||
|
||||
# PolarDB for PostgreSQL
|
||||
|
||||
**阿里云自主研发的云原生数据库产品**
|
||||
|
||||
#### [English](README.md) | 简体中文
|
||||
|
||||
[](https://developer.aliyun.com/topic/polardb-for-pg)
|
||||
|
||||
[](https://cirrus-ci.com/github/ApsaraDB/PolarDB-for-PostgreSQL/POLARDB_11_STABLE)
|
||||
[](https://cirrus-ci.com/github/ApsaraDB/PolarDB-for-PostgreSQL/POLARDB_11_DEV)
|
||||
[](LICENSE)
|
||||
[](https://GitHub.com/ApsaraDB/PolarDB-for-PostgreSQL/issues)
|
||||
[](https://GitHub.com/ApsaraDB/PolarDB-for-PostgreSQL/pulls)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/network/members)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/stargazers)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/graphs/contributors)
|
||||
[](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=polardb-pg)
|
||||
|
||||
</div>
|
||||
|
||||
## 什么是 PolarDB for PostgreSQL
|
||||
|
||||

|
||||
|
||||
PolarDB for PostgreSQL(下文简称为 PolarDB)是一款阿里云自主研发的云原生数据库产品,100% 兼容 PostgreSQL,采用基于 Shared-Storage 的存储计算分离架构,具有极致弹性、毫秒级延迟、HTAP 的能力。
|
||||
|
||||
1. 极致弹性:存储与计算能力均可独立地横向扩展。
|
||||
- 当计算能力不够时,可以单独扩展计算集群,数据无需复制。
|
||||
- 当存储容量或 I/O 不够时,可以单独扩展存储集群,而不中断业务。
|
||||
2. 毫秒级延迟:
|
||||
- WAL 日志存储在共享存储上,RW 到所有 RO 之间仅复制 WAL 的元数据。
|
||||
- 独创的 _LogIndex_ 技术,实现了 Lazy 回放和 Parallel 回放,理论上最大程度地缩小了 RW 和 RO 节点间的延迟。
|
||||
3. HTAP 能力:基于 Shared-Storage 的分布式并行执行框架,加速在 OLTP 场景下的 OLAP 查询。一套 OLTP 型的数据,可支持 2 套计算引擎:
|
||||
- 单机执行引擎:处理高并发的 TP 型负载。
|
||||
- 分布式执行引擎:处理大查询的 AP 型负载。
|
||||
|
||||
PolarDB 还支持时空、GIS、图像、向量、搜索、图谱等多模创新特性,应对企业对数据处理日新月异的需求。
|
||||
|
||||
## 分支说明
|
||||
|
||||
`POLARDB_11_STABLE` 为稳定分支,持存储计算分离的云原生形态。 `distribute` 分支支持分布式形态。
|
||||
|
||||
## 产品架构
|
||||
|
||||
PolarDB 采用了基于 Shared-Storage 的存储计算分离架构。数据库由传统的 Share-Nothing 架构,转变成了 Shared-Storage 架构。由原来的 N 份计算 + N 份存储,转变成了 N 份计算 + 1 份存储。虽然共享存储上数据是一份,但是数据在各节点内存中的状态是不同的,需要通过内存状态的同步来维护数据的一致性;同时主节点在刷脏时也需要做协调,避免只读节点读取到超前的 **“未来页面”**,也要避免只读节点读取到过时的没有在内存中被正确回放的 **“过去页面”**。为了解决该问题,PolarDB 创造性地设计了 _LogIndex_ 数据结构来维护页面的回放历史,该结构能够实现主节点与只读节点之间的同步。
|
||||
|
||||
在存储计算分离后,I/O 单路延迟变大的同时,I/O 的吞吐也变大了。在处理分析型查询时,仅使用单个只读节点无法发挥出存储侧的大 I/O 带宽优势,也无法利用其他只读节点的 CPU、内存和 I/O 资源。为了解决该问题,PolarDB 研发了基于 Shared-Storage 的并行执行引擎,能够在 SQL 级别上弹性利用任意数目的 CPU 来加速分析查询,支持 HTAP 的混合负载场景。
|
||||
|
||||
详情请查阅 [产品架构](https://apsaradb.github.io/PolarDB-for-PostgreSQL/zh/theory/arch-overview.html)。
|
||||
|
||||
## 快速入门
|
||||
|
||||
如果您已安装 Docker,那么可以从 DockerHub 上拉取 PolarDB for PostgreSQL 的 本地存储实例镜像,创建、运行并进入容器,然后直接使用 PolarDB 实例:
|
||||
|
||||
```bash
|
||||
# 拉取单节点 PolarDB 镜像
|
||||
docker pull polardb/polardb_pg_local_instance:single
|
||||
# 创建运行并进入容器
|
||||
docker run -it --cap-add=SYS_PTRACE --privileged=true --name polardb_pg_single polardb/polardb_pg_local_instance:single bash
|
||||
# 测试实例可用性
|
||||
psql -h 127.0.0.1 -c 'select version();'
|
||||
version
|
||||
--------------------------------
|
||||
PostgreSQL 11.9 (POLARDB 11.9)
|
||||
(1 row)
|
||||
```
|
||||
|
||||
对于更多进阶部署方式,请移步在线文档中的 [进阶部署](https://apsaradb.github.io/PolarDB-for-PostgreSQL/zh/deploying/deploy.html)。在部署前,我们建议您先了解一下 PolarDB for PostgreSQL 的 [架构简介](https://apsaradb.github.io/PolarDB-for-PostgreSQL/zh/deploying/introduction.html)。
|
||||
|
||||
## 文档
|
||||
|
||||
请移步本项目的 [在线文档网站](https://apsaradb.github.io/PolarDB-for-PostgreSQL/zh/) 查阅完整文档。
|
||||
|
||||
如果需要在本地预览或开发文档,请参考 [贡献文档](https://apsaradb.github.io/PolarDB-for-PostgreSQL/zh/contributing/contributing-polardb-docs.html)。
|
||||
|
||||
## 参与贡献
|
||||
|
||||
我们诚挚欢迎社区参与 PolarDB 的贡献,无论是代码还是文档。
|
||||
|
||||
以下是贡献者列表(由 [contrib.rocks](https://contrib.rocks) 支持):
|
||||
|
||||
<a href="https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ApsaraDB/PolarDB-for-PostgreSQL" />
|
||||
</a>
|
||||
|
||||
## 软件许可
|
||||
|
||||
PolarDB for PostgreSQL 的源代码遵循 Apache 2.0 许可证,基于遵循 PostgreSQL 许可证的 PostgreSQL 开发。其中还包含了部分遵循其它开源许可证的第三方组件。请移步 [LICENSE](./LICENSE) 和 [NOTICE](./NOTICE) 了解更多信息。
|
||||
|
||||
## 致谢
|
||||
|
||||
部分代码和设计思路参考了其他开源项目,例如:PG-XC/XL (pgxc_ctl)、TBase (部分基于时间戳的 vacuum 和 MVCC)、Greenplum 以及 Citus (pg_cron)。感谢以上开源项目的贡献。
|
||||
|
||||
## 加入社区
|
||||
|
||||
- PolarDB PostgreSQL Slack:[https://app.slack.com/client/T023NM10KGE/C023VEMKS02](https://app.slack.com/client/T023NM10KGE/C023VEMKS02)
|
||||
- 使用钉钉扫描如下二维码,加入 PolarDB 技术推广组钉钉群
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Copyright © Alibaba Group, Inc.
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<div align="center">
|
||||
|
||||
[](https://developer.aliyun.com/topic/polardb-for-pg)
|
||||
|
||||
# PolarDB for PostgreSQL
|
||||
|
||||
**A cloud-native database developed by Alibaba Cloud**
|
||||
|
||||
#### English | [简体中文](README-CN.md)
|
||||
|
||||
[](https://developer.aliyun.com/topic/polardb-for-pg)
|
||||
|
||||
[](https://cirrus-ci.com/github/ApsaraDB/PolarDB-for-PostgreSQL/POLARDB_11_STABLE)
|
||||
[](https://cirrus-ci.com/github/ApsaraDB/PolarDB-for-PostgreSQL/POLARDB_11_DEV)
|
||||
[](LICENSE)
|
||||
[](https://GitHub.com/ApsaraDB/PolarDB-for-PostgreSQL/issues)
|
||||
[](https://GitHub.com/ApsaraDB/PolarDB-for-PostgreSQL/pulls)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/network/members)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/stargazers)
|
||||
[](https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/graphs/contributors)
|
||||
[](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=polardb-pg)
|
||||
|
||||
</div>
|
||||
|
||||
## Overview
|
||||
|
||||

|
||||
|
||||
PolarDB for PostgreSQL (hereafter simplified as PolarDB) is a cloud native database service independently developed by Alibaba Cloud. This service is 100% compatible with PostgreSQL and uses a shared-storage-based architecture in which computing is decoupled from storage. This service features flexible scalability, millisecond-level latency and hybrid transactional/analytical processing (HTAP) capabilities.
|
||||
|
||||
1. Flexible scalability: You can use the service to scale out a compute cluster or a storage cluster based on your business requirements.
|
||||
- If the computing power is insufficient, you can scale out only the compute cluster.
|
||||
- If the storage capacity or the storage I/O is insufficient, you can scale out a storage cluster without interrupting your service.
|
||||
2. Millisecond-level latency:
|
||||
- Write-ahead logging (WAL) logs are stored in the shared storage. Only the metadata of WAL records is replicated from the read-write node to read-only nodes.
|
||||
- The _LogIndex_ technology provided by PolarDB features two record replay modes: lazy replay and parallel replay. The technology can be used to minimize the record replication latency from the read-write node to read-only nodes.
|
||||
3. HTAP: HTAP is implemented by using a shared-storage-based massively parallel processing (MPP) architecture. The architecture is used to accelerate online analytical processing (OLAP) queries in online transaction processing (OLTP) scenarios. PolarDB supports a complete suite of data types that are used in OLTP scenarios. PolarDB supports two computing engines that can process these types of data:
|
||||
- Standalone execution: processes OLTP queries that feature high concurrency.
|
||||
- Distributed execution: processes large OLAP queries.
|
||||
|
||||
PolarDB provides a wide range of innovative multi-model database capabilities to help you process, analyze, and search for different types of data, such as spatio-temporal, geographic information system (GIS), image, vector, and graph data.
|
||||
|
||||
## Branch Introduction
|
||||
|
||||
The `POLARDB_11_STABLE` is the stable branch based on PostgreSQL 11.9, which supports compute-storage separation architecture. The `distributed` branch supports distributed architecture.
|
||||
|
||||
## Architecture and Roadmap
|
||||
|
||||
PolarDB uses a shared-storage-based architecture in which computing is decoupled from storage. The conventional shared-nothing architecture is changed to the shared-storage architecture. N copies of data in the compute cluster and N copies of data in the storage cluster are changed to N copies of data in the compute cluster and one copy of data in the storage cluster. The shared storage stores one copy of data, but the data states in memory are different. The WAL logs must be synchronized from the primary node to read-only nodes to ensure data consistency. In addition, when the primary node flushes dirty pages, it must be controlled to prevent the read-only nodes from reading future pages. Meanwhile, the read-only nodes must be prevented from reading the outdated pages that are not correctly replayed in memory. To resolve this issue, PolarDB provides the index structure _LogIndex_ to maintain the page replay history. LogIndex can be used to synchronize data from the primary node to read-only nodes.
|
||||
|
||||
After computing is decoupled from storage, the I/O latency and throughput increase. When a single read-only node is used to process analytical queries, the CPUs, memory, and I/O of other read-only nodes and the large storage I/O bandwidth cannot be fully utilized. To resolve this issue, PolarDB provides the shared-storage-based MPP engine. The engine can use CPUs to accelerate analytical queries at SQL level and support a mix of OLAP workloads and OLTP workloads for HTAP.
|
||||
|
||||
For more information, see [Architecture](https://apsaradb.github.io/PolarDB-for-PostgreSQL/theory/arch-overview.html).
|
||||
|
||||
## Quick Start with PolarDB
|
||||
|
||||
If you have Docker installed already,then you can pull the instance image of PolarDB for PostgreSQL based on local storage. Create, run and enter the container, and use PolarDB instance directly:
|
||||
|
||||
```bash
|
||||
# pull the instance image from DockerHub
|
||||
docker pull polardb/polardb_pg_local_instance:single
|
||||
# create, run and enter the container
|
||||
docker run -it --cap-add=SYS_PTRACE --privileged=true --name polardb_pg_single polardb/polardb_pg_local_instance:single bash
|
||||
# check
|
||||
psql -h 127.0.0.1 -c 'select version();'
|
||||
version
|
||||
--------------------------------
|
||||
PostgreSQL 11.9 (POLARDB 11.9)
|
||||
(1 row)
|
||||
```
|
||||
|
||||
For more advanced deployment way, please refer to [Advanced Deployment](https://apsaradb.github.io/PolarDB-for-PostgreSQL/deploying/deploy.html). Before your deployment, we recommand to figure out the [architecture](https://apsaradb.github.io/PolarDB-for-PostgreSQL/deploying/introduction.html) of PolarDB for PostgreSQL.
|
||||
|
||||
## Documentation
|
||||
|
||||
Please refer to [Online Documentation Website](https://apsaradb.github.io/PolarDB-for-PostgreSQL/) to see the whole documentations.
|
||||
|
||||
If you want to explore or develop documentation locally, see [Document Contribution](https://apsaradb.github.io/PolarDB-for-PostgreSQL/contributing/contributing-polardb-docs.html).
|
||||
|
||||
## Contributing
|
||||
|
||||
You are welcome to make contributions to PolarDB, no matter code or documentation.
|
||||
|
||||
Here are the contributors:
|
||||
|
||||
<a href="https://github.com/ApsaraDB/PolarDB-for-PostgreSQL/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ApsaraDB/PolarDB-for-PostgreSQL" />
|
||||
</a>
|
||||
|
||||
Made with [contrib.rocks](https://contrib.rocks).
|
||||
|
||||
## Software License
|
||||
|
||||
PolarDB code is released under the Apache License (Version 2.0), developed based on the PostgreSQL which is released under the PostgreSQL License. This product contains various third-party components under other open source licenses.
|
||||
|
||||
See the [LICENSE](./LICENSE) and [NOTICE](./NOTICE) file for more information.
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
Some code and design ideas are based on other open source projects, such as PG-XC/XL (pgxc_ctl), TBase (Timestamp-based vacuum and MVCC), Greenplum and Citus (pg_cron). We thank the contributions of the preceding open source projects.
|
||||
|
||||
## Join the Community
|
||||
|
||||
- PolarDB PostgreSQL at Slack: [https://app.slack.com/client/T023NM10KGE/C023VEMKS02](https://app.slack.com/client/T023NM10KGE/C023VEMKS02)
|
||||
- Use the DingTalk application to scan the following QR code and join the DingTalk group for PolarDB technology promotion.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Copyright © Alibaba Group, Inc.
|
||||
|
|
@ -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' 'unsigned int PASCAL' 'SOCKET WSAAPI'; do
|
||||
for ac_cv_func_accept_arg1 in 'int' 'unsigned int' 'SOCKET'; 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,374 @@
|
|||
# ===========================================================================
|
||||
# http://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 link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads 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, 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 <http://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 21
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
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_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
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
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
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 -kthread lthread -pthread -pthreads -mt -mthreads pthread --thread-safe 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)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
|
||||
# -pthreads: Solaris/gcc
|
||||
# -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)
|
||||
# -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
|
||||
|
||||
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*)
|
||||
|
||||
# Newer versions of Solaris require the "-mt -lpthread" pair, and we
|
||||
# check that first. On some older versions, libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (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 look for -pthreads and -lpthread shortly thereafter.
|
||||
|
||||
ax_pthread_flags="-mt,pthread -pthreads -pthread pthread $ax_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Older versions of Clang only give a warning instead of an error for an
|
||||
# unrecognized option, unless we specify -Werror. (We throw in some extra
|
||||
# Clang warning flags for good measure.)
|
||||
|
||||
AC_CACHE_CHECK([if compiler needs certain flags to reject unknown flags],
|
||||
[ax_cv_PTHREAD_REJECT_UNKNOWN],
|
||||
[ax_cv_PTHREAD_REJECT_UNKNOWN=unknown
|
||||
save_CFLAGS="$CFLAGS"
|
||||
ax_pthread_extra_flags="-Wunknown-warning-option -Wunused-command-line-argument"
|
||||
CFLAGS="$CFLAGS $ax_pthread_extra_flags -Wfoobaz -foobaz"
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int foo(void);],[foo()])],
|
||||
[ax_cv_PTHREAD_REJECT_UNKNOWN="-Werror $ax_pthread_extra_flags"],
|
||||
[ax_cv_PTHREAD_REJECT_UNKNOWN=no])
|
||||
CFLAGS="$save_CFLAGS"
|
||||
])
|
||||
ax_pthread_extra_flags=
|
||||
AS_IF([test "x$ax_cv_PTHREAD_REJECT_UNKNOWN" != "xno"],
|
||||
[ax_pthread_extra_flags="$ax_cv_PTHREAD_REJECT_UNKNOWN"])
|
||||
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
for flag in $ax_pthread_flags; do
|
||||
|
||||
case $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 $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
|
||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ax_pthread_extra_flags"
|
||||
|
||||
# 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>
|
||||
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],
|
||||
[])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT([$ax_pthread_ok])
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# 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 attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
|
||||
[int attr = $attr; return attr /* ; */])],
|
||||
[ax_cv_PTHREAD_JOINABLE_ATTR=$attr; break],
|
||||
[])
|
||||
done
|
||||
])
|
||||
AS_IF([test "$ax_cv_PTHREAD_JOINABLE_ATTR" != unknown && \
|
||||
test "$ax_cv_PTHREAD_JOINABLE_ATTR" != PTHREAD_CREATE_JOINABLE],
|
||||
[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.])])
|
||||
|
||||
AC_CACHE_CHECK([if more special flags are required for pthreads],
|
||||
[ax_cv_PTHREAD_SPECIAL_FLAGS],
|
||||
[ax_cv_PTHREAD_SPECIAL_FLAGS=no
|
||||
ax_pthread_special_flags_added=no
|
||||
AC_EGREP_CPP([AX_PTHREAD_NEED_SPECIAL_FLAG],
|
||||
[
|
||||
# if !defined(_REENTRANT) && !defined(_THREAD_SAFE)
|
||||
AX_PTHREAD_NEED_SPECIAL_FLAG
|
||||
# endif
|
||||
],
|
||||
[case $host_os in
|
||||
aix* | freebsd*)
|
||||
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_THREAD_SAFE"
|
||||
;;
|
||||
darwin* | hpux* | osf* | solaris*)
|
||||
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_REENTRANT"
|
||||
;;
|
||||
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"],
|
||||
[AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# 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,725 @@
|
|||
# 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
|
||||
# -----------------------
|
||||
# Set the format archetype used by gcc to check printf type functions. We
|
||||
# prefer "gnu_printf", which includes what glibc uses, such as %m for error
|
||||
# strings and %lld for 64 bit long longs. GCC 4.4 introduced it. It makes a
|
||||
# dramatic difference on Windows.
|
||||
AC_DEFUN([PGAC_PRINTF_ARCHETYPE],
|
||||
[AC_CACHE_CHECK([for printf format archetype], pgac_cv_printf_archetype,
|
||||
[ac_save_c_werror_flag=$ac_c_werror_flag
|
||||
ac_c_werror_flag=yes
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
|
||||
[extern int
|
||||
pgac_write(int ignore, const char *fmt,...)
|
||||
__attribute__((format(gnu_printf, 2, 3)));], [])],
|
||||
[pgac_cv_printf_archetype=gnu_printf],
|
||||
[pgac_cv_printf_archetype=printf])
|
||||
ac_c_werror_flag=$ac_save_c_werror_flag])
|
||||
AC_DEFINE_UNQUOTED([PG_PRINTF_ATTRIBUTE], [$pgac_cv_printf_archetype],
|
||||
[Define to gnu_printf if compiler supports it, else printf.])
|
||||
])# PGAC_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_BSWAP16
|
||||
# -------------------------
|
||||
# Check if the C compiler understands __builtin_bswap16(),
|
||||
# and define HAVE__BUILTIN_BSWAP16 if so.
|
||||
AC_DEFUN([PGAC_C_BUILTIN_BSWAP16],
|
||||
[AC_CACHE_CHECK(for __builtin_bswap16, pgac_cv__builtin_bswap16,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||
[static unsigned long int x = __builtin_bswap16(0xaabb);]
|
||||
)],
|
||||
[pgac_cv__builtin_bswap16=yes],
|
||||
[pgac_cv__builtin_bswap16=no])])
|
||||
if test x"$pgac_cv__builtin_bswap16" = xyes ; then
|
||||
AC_DEFINE(HAVE__BUILTIN_BSWAP16, 1,
|
||||
[Define to 1 if your compiler understands __builtin_bswap16.])
|
||||
fi])# PGAC_C_BUILTIN_BSWAP16
|
||||
|
||||
|
||||
|
||||
# PGAC_C_BUILTIN_BSWAP32
|
||||
# -------------------------
|
||||
# Check if the C compiler understands __builtin_bswap32(),
|
||||
# and define HAVE__BUILTIN_BSWAP32 if so.
|
||||
AC_DEFUN([PGAC_C_BUILTIN_BSWAP32],
|
||||
[AC_CACHE_CHECK(for __builtin_bswap32, pgac_cv__builtin_bswap32,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||
[static unsigned long int x = __builtin_bswap32(0xaabbccdd);]
|
||||
)],
|
||||
[pgac_cv__builtin_bswap32=yes],
|
||||
[pgac_cv__builtin_bswap32=no])])
|
||||
if test x"$pgac_cv__builtin_bswap32" = xyes ; then
|
||||
AC_DEFINE(HAVE__BUILTIN_BSWAP32, 1,
|
||||
[Define to 1 if your compiler understands __builtin_bswap32.])
|
||||
fi])# PGAC_C_BUILTIN_BSWAP32
|
||||
|
||||
|
||||
|
||||
# PGAC_C_BUILTIN_BSWAP64
|
||||
# -------------------------
|
||||
# Check if the C compiler understands __builtin_bswap64(),
|
||||
# and define HAVE__BUILTIN_BSWAP64 if so.
|
||||
AC_DEFUN([PGAC_C_BUILTIN_BSWAP64],
|
||||
[AC_CACHE_CHECK(for __builtin_bswap64, pgac_cv__builtin_bswap64,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE(
|
||||
[static unsigned long int x = __builtin_bswap64(0xaabbccddeeff0011);]
|
||||
)],
|
||||
[pgac_cv__builtin_bswap64=yes],
|
||||
[pgac_cv__builtin_bswap64=no])])
|
||||
if test x"$pgac_cv__builtin_bswap64" = xyes ; then
|
||||
AC_DEFINE(HAVE__BUILTIN_BSWAP64, 1,
|
||||
[Define to 1 if your compiler understands __builtin_bswap64.])
|
||||
fi])# PGAC_C_BUILTIN_BSWAP64
|
||||
|
||||
|
||||
|
||||
# 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_C_VA_ARGS
|
||||
# --------------
|
||||
# Check if the C compiler understands C99-style variadic macros,
|
||||
# and define HAVE__VA_ARGS if so.
|
||||
AC_DEFUN([PGAC_C_VA_ARGS],
|
||||
[AC_CACHE_CHECK(for __VA_ARGS__, pgac_cv__va_args,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>],
|
||||
[#define debug(...) fprintf(stderr, __VA_ARGS__)
|
||||
debug("%s", "blarg");
|
||||
])],
|
||||
[pgac_cv__va_args=yes],
|
||||
[pgac_cv__va_args=no])])
|
||||
if test x"$pgac_cv__va_args" = xyes ; then
|
||||
AC_DEFINE(HAVE__VA_ARGS, 1,
|
||||
[Define to 1 if your compiler understands __VA_ARGS__ in macros.])
|
||||
fi])# PGAC_C_VA_ARGS
|
||||
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
# POLAR_AC_ARMV8_LSE
|
||||
# -------------------------
|
||||
# Check if the compiler supports the LSE instructions
|
||||
AC_DEFUN([POLAR_AC_ARMV8_LSE],
|
||||
[define([Ac_cachevar], [AS_TR_SH([polar_ac_cv_armv8_lse_$1])])dnl
|
||||
AC_CACHE_CHECK([for builtin __sync int32 locking functions with CFLAGS=$1], [Ac_cachevar],
|
||||
[polar_ac_save_CFLAGS=$CFLAGS
|
||||
CFLAGS="$polar_ac_save_CFLAGS $1"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([],
|
||||
[int lock = 0;
|
||||
__sync_lock_test_and_set(&lock, 1);
|
||||
__sync_lock_release(&lock);])],
|
||||
[Ac_cachevar="yes"],
|
||||
[Ac_cachevar="no"])
|
||||
CFLAGS="$polar_ac_save_CFLAGS"])
|
||||
if test x"$Ac_cachevar" = x"yes"; then
|
||||
POLAR_CFLAGS_ARMV8_LSE="$1"
|
||||
polar_ac_armv8_lse=yes
|
||||
fi
|
||||
undefine([Ac_cachevar])dnl
|
||||
])# POLAR_AC_ARMV8_LSE
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
# 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_FUNC_SNPRINTF_ARG_CONTROL
|
||||
# ---------------------------------------
|
||||
# Determine if snprintf supports %1$ argument selection, e.g. %5$ selects
|
||||
# the fifth argument after the printf format string.
|
||||
# This is not in the C99 standard, but in the Single Unix Specification (SUS).
|
||||
# It is used in our language translation strings.
|
||||
#
|
||||
AC_DEFUN([PGAC_FUNC_SNPRINTF_ARG_CONTROL],
|
||||
[AC_MSG_CHECKING([whether snprintf supports argument control])
|
||||
AC_CACHE_VAL(pgac_cv_snprintf_arg_control,
|
||||
[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char buf[100];
|
||||
|
||||
/* can it swap arguments? */
|
||||
snprintf(buf, 100, "%2\$d %1\$d", 3, 4);
|
||||
if (strcmp(buf, "4 3") != 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}]])],
|
||||
[pgac_cv_snprintf_arg_control=yes],
|
||||
[pgac_cv_snprintf_arg_control=no],
|
||||
[pgac_cv_snprintf_arg_control=cross])
|
||||
])dnl AC_CACHE_VAL
|
||||
AC_MSG_RESULT([$pgac_cv_snprintf_arg_control])
|
||||
])# PGAC_FUNC_SNPRINTF_ARG_CONTROL
|
||||
|
||||
# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
|
||||
# ---------------------------------------
|
||||
# Determine if snprintf supports the z length modifier for printing
|
||||
# size_t-sized variables. That's supported by C99 and POSIX but not
|
||||
# all platforms play ball, so we must test whether it's working.
|
||||
#
|
||||
AC_DEFUN([PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT],
|
||||
[AC_MSG_CHECKING([whether snprintf supports the %z modifier])
|
||||
AC_CACHE_VAL(pgac_cv_snprintf_size_t_support,
|
||||
[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char bufz[100];
|
||||
char buf64[100];
|
||||
|
||||
/*
|
||||
* Print the largest unsigned number fitting in a size_t using both %zu
|
||||
* and the previously-determined format for 64-bit integers. Note that
|
||||
* we don't run this code unless we know snprintf handles 64-bit ints.
|
||||
*/
|
||||
bufz[0] = '\0'; /* in case snprintf fails to emit anything */
|
||||
snprintf(bufz, sizeof(bufz), "%zu", ~((size_t) 0));
|
||||
snprintf(buf64, sizeof(buf64), "%" INT64_MODIFIER "u",
|
||||
(unsigned PG_INT64_TYPE) ~((size_t) 0));
|
||||
if (strcmp(bufz, buf64) != 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}]])],
|
||||
[pgac_cv_snprintf_size_t_support=yes],
|
||||
[pgac_cv_snprintf_size_t_support=no],
|
||||
[pgac_cv_snprintf_size_t_support=cross])
|
||||
])dnl AC_CACHE_VAL
|
||||
AC_MSG_RESULT([$pgac_cv_snprintf_size_t_support])
|
||||
])# PGAC_FUNC_SNPRINTF_SIZE_T_SUPPORT
|
||||
|
||||
|
||||
# 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`
|
||||
|
||||
# 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,113 @@
|
|||
# config/perl.m4
|
||||
|
||||
|
||||
# PGAC_PATH_PERL
|
||||
# --------------
|
||||
AC_DEFUN([PGAC_PATH_PERL],
|
||||
[PGAC_PATH_PROGS(PERL, perl)
|
||||
|
||||
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`; 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,293 @@
|
|||
# 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
|
||||
# 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)
|
||||
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,183 @@
|
|||
#
|
||||
# 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])
|
||||
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,104 @@
|
|||
# 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])
|
||||
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,100 @@
|
|||
# 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
|
||||
|
||||
ifeq ($(enable_inject_faults),yes)
|
||||
SUBDIRS += faultinjector
|
||||
else
|
||||
ALWAYS_SUBDIRS += faultinjector
|
||||
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,571 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* adminpack.c
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2002-2018, 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);
|
||||
|
||||
|
||||
/*-----------------------
|
||||
* 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(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(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(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)
|
||||
{
|
||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
|
||||
bool randomAccess;
|
||||
TupleDesc tupdesc;
|
||||
Tuplestorestate *tupstore;
|
||||
AttInMetadata *attinmeta;
|
||||
DIR *dirdesc;
|
||||
struct dirent *de;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
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'")));
|
||||
|
||||
/* check to see if caller supports us returning a tuplestore */
|
||||
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("set-valued function called in context that cannot accept a set")));
|
||||
if (!(rsinfo->allowedModes & SFRM_Materialize))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("materialize mode required, but it is not allowed in this context")));
|
||||
|
||||
/* The tupdesc and tuplestore must be created in ecxt_per_query_memory */
|
||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
||||
|
||||
tupdesc = CreateTemplateTupleDesc(2, false);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
|
||||
TIMESTAMPOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
|
||||
TEXTOID, -1, 0);
|
||||
|
||||
randomAccess = (rsinfo->allowedModes & SFRM_Materialize_Random) != 0;
|
||||
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
|
||||
rsinfo->returnMode = SFRM_Materialize;
|
||||
rsinfo->setResult = tupstore;
|
||||
rsinfo->setDesc = tupdesc;
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||
|
||||
dirdesc = polar_allocate_dir(Log_directory);
|
||||
while ((de = ReadDir(dirdesc, Log_directory)) != 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", Log_directory, de->d_name);
|
||||
|
||||
tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||
|
||||
tuplestore_puttuple(tupstore, tuple);
|
||||
}
|
||||
|
||||
FreeDir(dirdesc);
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
|
@ -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 open file "test_file1" for reading: 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 open file "test_file2" for reading: 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.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,5 @@
|
|||
# amcheck extension
|
||||
comment = 'functions for verifying relation integrity'
|
||||
default_version = '1.1'
|
||||
module_pathname = '$libdir/amcheck'
|
||||
relocatable = true
|
||||
|
|
@ -0,0 +1 @@
|
|||
CREATE EXTENSION amcheck;
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
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 test for index with included columns
|
||||
SELECT bt_index_parent_check('bttest_multi_idx', true);
|
||||
bt_index_parent_check
|
||||
-----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- repeat expansive test 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);
|
||||
bt_index_parent_check
|
||||
-----------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
--
|
||||
-- Test for multilevel page deletion/downlink present 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 FROM delete_test_table WHERE a > 40000;
|
||||
VACUUM delete_test_table;
|
||||
DELETE FROM delete_test_table WHERE a > 10;
|
||||
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 extended;
|
||||
CREATE INDEX toasty ON toast_bug(buggy);
|
||||
-- pg_attribute entry for toasty.buggy (the index) will have plain storage:
|
||||
UPDATE pg_attribute SET attstorage = 'p'
|
||||
WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
|
||||
-- Whereas pg_attribute entry for toast_bug.buggy (the table) still has extended storage:
|
||||
SELECT attstorage FROM pg_attribute
|
||||
WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
|
||||
attstorage
|
||||
------------
|
||||
x
|
||||
(1 row)
|
||||
|
||||
-- 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,119 @@
|
|||
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 test for index with included columns
|
||||
SELECT bt_index_parent_check('bttest_multi_idx', true);
|
||||
|
||||
-- repeat expansive test 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);
|
||||
|
||||
--
|
||||
-- Test for multilevel page deletion/downlink present 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 FROM delete_test_table WHERE a > 40000;
|
||||
VACUUM delete_test_table;
|
||||
DELETE FROM delete_test_table WHERE a > 10;
|
||||
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 extended;
|
||||
CREATE INDEX toasty ON toast_bug(buggy);
|
||||
|
||||
-- pg_attribute entry for toasty.buggy (the index) will have plain storage:
|
||||
UPDATE pg_attribute SET attstorage = 'p'
|
||||
WHERE attrelid = 'toasty'::regclass AND attname = 'buggy';
|
||||
|
||||
-- Whereas pg_attribute entry for toast_bug.buggy (the table) still has extended storage:
|
||||
SELECT attstorage FROM pg_attribute
|
||||
WHERE attrelid = 'toast_bug'::regclass AND attname = 'buggy';
|
||||
|
||||
-- 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-2018, 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,409 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* auto_explain.c
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2008-2018, 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"
|
||||
/* POLAR px */
|
||||
#include "portability/instr_time.h"
|
||||
#include "px/px_explain.h" /* pxexplain_recvExecStats */
|
||||
#include "executor/execdesc.h"
|
||||
#include "px/px_disp.h"
|
||||
/* POALR end */
|
||||
|
||||
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 int auto_explain_log_format = EXPLAIN_FORMAT_TEXT;
|
||||
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}
|
||||
};
|
||||
|
||||
/* 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_USERSET,
|
||||
GUC_UNIT_MS,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomBoolVariable("auto_explain.log_analyze",
|
||||
"Use EXPLAIN ANALYZE for plan logging.",
|
||||
NULL,
|
||||
&auto_explain_log_analyze,
|
||||
false,
|
||||
PGC_USERSET,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomBoolVariable("auto_explain.log_verbose",
|
||||
"Use EXPLAIN VERBOSE for plan logging.",
|
||||
NULL,
|
||||
&auto_explain_log_verbose,
|
||||
false,
|
||||
PGC_USERSET,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomBoolVariable("auto_explain.log_buffers",
|
||||
"Log buffers usage.",
|
||||
NULL,
|
||||
&auto_explain_log_buffers,
|
||||
false,
|
||||
PGC_USERSET,
|
||||
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_USERSET,
|
||||
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_USERSET,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomBoolVariable("auto_explain.log_nested_statements",
|
||||
"Log nested statements.",
|
||||
NULL,
|
||||
&auto_explain_log_nested_statements,
|
||||
false,
|
||||
PGC_USERSET,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
DefineCustomBoolVariable("auto_explain.log_timing",
|
||||
"Collect timing data, not just row counts.",
|
||||
NULL,
|
||||
&auto_explain_log_timing,
|
||||
true,
|
||||
PGC_USERSET,
|
||||
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_USERSET,
|
||||
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)
|
||||
{
|
||||
/* POLAR px */
|
||||
instr_time starttime;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* POLAR px */
|
||||
if (queryDesc->plannedstmt->planGen == PLANGEN_PX)
|
||||
{
|
||||
queryDesc->instrument_options |= INSTRUMENT_PX;
|
||||
INSTR_TIME_SET_CURRENT(starttime);
|
||||
queryDesc->showstatctx = pxexplain_showExecStatsBegin(queryDesc,
|
||||
starttime);
|
||||
}
|
||||
/* POLAR end */
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* POLAR px : Wait for completion of all qExec processes. */
|
||||
if (queryDesc->estate->dispatcherState && queryDesc->estate->dispatcherState->primaryResults)
|
||||
pxdisp_checkDispatchResult(queryDesc->estate->dispatcherState, DISPATCH_WAIT_NONE);
|
||||
/* POLAR end */
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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(LOG,
|
||||
(errmsg("duration: %.3f ms plan:\n%s",
|
||||
msec, es->str->data),
|
||||
polar_mark_slow_log(true), /* POLAR */
|
||||
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,24 @@
|
|||
# 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
|
||||
|
||||
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
|
||||
|
||||
wal-check: temp-install
|
||||
$(prove_check)
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* blcost.c
|
||||
* Cost estimate function for bloom indexes.
|
||||
*
|
||||
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* contrib/bloom/blcost.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "fmgr.h"
|
||||
#include "optimizer/cost.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;
|
||||
List *qinfos;
|
||||
GenericCosts costs;
|
||||
|
||||
/* Do preliminary analysis of indexquals */
|
||||
qinfos = deconstruct_indexquals(path);
|
||||
|
||||
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, qinfos, &costs);
|
||||
|
||||
*indexStartupCost = costs.indexStartupCost;
|
||||
*indexTotalCost = costs.indexTotalCost;
|
||||
*indexSelectivity = costs.indexSelectivity;
|
||||
*indexCorrelation = costs.indexCorrelation;
|
||||
*indexPages = costs.numIndexPages;
|
||||
}
|
||||
|
|
@ -0,0 +1,370 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* blinsert.c
|
||||
* Bloom index build and insert functions.
|
||||
*
|
||||
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* contrib/bloom/blinsert.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/generic_xlog.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 from IndexBuildHeapScan.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
polar_check_nblocks_consistent(index);
|
||||
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 = IndexBuildHeapScan(heap, index, indexInfo, 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.
|
||||
*/
|
||||
PageEncryptInplace(metapage, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO);
|
||||
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,213 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* bloom.h
|
||||
* Header for bloom index.
|
||||
*
|
||||
* Copyright (c) 2016-2018, 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/relation.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];
|
||||
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-2018, 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,490 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* blutils.c
|
||||
* Bloom index utilities.
|
||||
*
|
||||
* Portions Copyright (c) 2016-2018, 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->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);
|
||||
}
|
||||
|
||||
/* 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(FunctionCall1(&state->hashFn[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 BloomInitBuffer
|
||||
*/
|
||||
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-2018, 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-2018, 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,231 @@
|
|||
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
|
||||
);
|
||||
NOTICE: change unlogged table to logged table,because unlogged table not supports Master-Slave mode
|
||||
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,514 @@
|
|||
/*
|
||||
* 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/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)
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
set enable_seqscan=off;
|
||||
CREATE TABLE test_money (
|
||||
i money
|
||||
);
|
||||
INSERT INTO test_money VALUES ('-2'),('-1'),('0'),('1'),('2'),('3');
|
||||
CREATE INDEX idx_money ON test_money USING gin (i);
|
||||
SELECT * FROM test_money WHERE i<'1'::money ORDER BY i;
|
||||
i
|
||||
--------
|
||||
-$2.00
|
||||
-$1.00
|
||||
$0.00
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_money WHERE i<='1'::money ORDER BY i;
|
||||
i
|
||||
--------
|
||||
-$2.00
|
||||
-$1.00
|
||||
$0.00
|
||||
$1.00
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM test_money WHERE i='1'::money ORDER BY i;
|
||||
i
|
||||
-------
|
||||
$1.00
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_money WHERE i>='1'::money ORDER BY i;
|
||||
i
|
||||
-------
|
||||
$1.00
|
||||
$2.00
|
||||
$3.00
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_money WHERE i>'1'::money ORDER BY i;
|
||||
i
|
||||
-------
|
||||
$2.00
|
||||
$3.00
|
||||
(2 rows)
|
||||
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
set enable_seqscan=off;
|
||||
CREATE TABLE test_name (
|
||||
i name
|
||||
);
|
||||
INSERT INTO test_name VALUES ('a'),('ab'),('abc'),('abb'),('axy'),('xyz');
|
||||
CREATE INDEX idx_name ON test_name USING gin (i);
|
||||
SELECT * FROM test_name WHERE i<'abc' ORDER BY i;
|
||||
i
|
||||
-----
|
||||
a
|
||||
ab
|
||||
abb
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_name WHERE i<='abc' ORDER BY i;
|
||||
i
|
||||
-----
|
||||
a
|
||||
ab
|
||||
abb
|
||||
abc
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM test_name WHERE i='abc' ORDER BY i;
|
||||
i
|
||||
-----
|
||||
abc
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_name WHERE i>='abc' ORDER BY i;
|
||||
i
|
||||
-----
|
||||
abc
|
||||
axy
|
||||
xyz
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_name WHERE i>'abc' ORDER BY i;
|
||||
i
|
||||
-----
|
||||
axy
|
||||
xyz
|
||||
(2 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM test_name WHERE i<'abc' ORDER BY i;
|
||||
QUERY PLAN
|
||||
---------------------------------------------
|
||||
Sort
|
||||
Sort Key: i
|
||||
-> Bitmap Heap Scan on test_name
|
||||
Recheck Cond: (i < 'abc'::name)
|
||||
-> Bitmap Index Scan on idx_name
|
||||
Index Cond: (i < 'abc'::name)
|
||||
(6 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM test_name WHERE i<='abc' ORDER BY i;
|
||||
QUERY PLAN
|
||||
----------------------------------------------
|
||||
Sort
|
||||
Sort Key: i
|
||||
-> Bitmap Heap Scan on test_name
|
||||
Recheck Cond: (i <= 'abc'::name)
|
||||
-> Bitmap Index Scan on idx_name
|
||||
Index Cond: (i <= 'abc'::name)
|
||||
(6 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM test_name WHERE i='abc' ORDER BY i;
|
||||
QUERY PLAN
|
||||
---------------------------------------
|
||||
Bitmap Heap Scan on test_name
|
||||
Recheck Cond: (i = 'abc'::name)
|
||||
-> Bitmap Index Scan on idx_name
|
||||
Index Cond: (i = 'abc'::name)
|
||||
(4 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM test_name WHERE i>='abc' ORDER BY i;
|
||||
QUERY PLAN
|
||||
----------------------------------------------
|
||||
Sort
|
||||
Sort Key: i
|
||||
-> Bitmap Heap Scan on test_name
|
||||
Recheck Cond: (i >= 'abc'::name)
|
||||
-> Bitmap Index Scan on idx_name
|
||||
Index Cond: (i >= 'abc'::name)
|
||||
(6 rows)
|
||||
|
||||
EXPLAIN (COSTS OFF) SELECT * FROM test_name WHERE i>'abc' ORDER BY i;
|
||||
QUERY PLAN
|
||||
---------------------------------------------
|
||||
Sort
|
||||
Sort Key: i
|
||||
-> Bitmap Heap Scan on test_name
|
||||
Recheck Cond: (i > 'abc'::name)
|
||||
-> Bitmap Index Scan on idx_name
|
||||
Index Cond: (i > 'abc'::name)
|
||||
(6 rows)
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
set enable_seqscan=off;
|
||||
CREATE TABLE test_numeric (
|
||||
i numeric
|
||||
);
|
||||
INSERT INTO test_numeric VALUES (-2),(-1),(0),(1),(2),(3);
|
||||
CREATE INDEX idx_numeric ON test_numeric USING gin (i);
|
||||
SELECT * FROM test_numeric WHERE i<'1'::numeric ORDER BY i;
|
||||
i
|
||||
----
|
||||
-2
|
||||
-1
|
||||
0
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_numeric WHERE i<='1'::numeric ORDER BY i;
|
||||
i
|
||||
----
|
||||
-2
|
||||
-1
|
||||
0
|
||||
1
|
||||
(4 rows)
|
||||
|
||||
SELECT * FROM test_numeric WHERE i='1'::numeric ORDER BY i;
|
||||
i
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM test_numeric WHERE i>='1'::numeric ORDER BY i;
|
||||
i
|
||||
---
|
||||
1
|
||||
2
|
||||
3
|
||||
(3 rows)
|
||||
|
||||
SELECT * FROM test_numeric WHERE i>'1'::numeric ORDER BY i;
|
||||
i
|
||||
---
|
||||
2
|
||||
3
|
||||
(2 rows)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue