pax_global_header 0000666 0000000 0000000 00000000064 15066665201 0014521 g ustar 00root root 0000000 0000000 52 comment=27aa7b04af197129bf10069c4b512a326c94f3f8
mongo_fdw-REL-5_5_3/ 0000775 0000000 0000000 00000000000 15066665201 0014274 5 ustar 00root root 0000000 0000000 mongo_fdw-REL-5_5_3/.gitattributes 0000664 0000000 0000000 00000000522 15066665201 0017166 0 ustar 00root root 0000000 0000000 * whitespace=space-before-tab,trailing-space
*.[ch] whitespace=space-before-tab,trailing-space,indent-with-non-tab,tabwidth=4
# Avoid confusing ASCII underlines with leftover merge conflict markers
README conflict-marker-size=32
README.* conflict-marker-size=32
# Test output files that contain extra whitespace
*.out -whitespace
mongo_fdw-REL-5_5_3/.gitignore 0000664 0000000 0000000 00000000365 15066665201 0016270 0 ustar 00root root 0000000 0000000 # =====
# = C =
# =====
# Object files
*.o
*.ko
*.obj
*.elf
# Libraries
*.lib
*.a
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.app
*.i*86
*.x86_64
*.hex
# pgregress results directory
results/
.idea/
mongo_fdw-REL-5_5_3/.gitmodules 0000664 0000000 0000000 00000000254 15066665201 0016452 0 ustar 00root root 0000000 0000000 [submodule "mongo-c-driver"]
path = mongo-c-driver
url = ../../mongodb/mongo-c-driver.git
[submodule "json-c"]
path = json-c
url = https://github.com/json-c/json-c.git
mongo_fdw-REL-5_5_3/CONTRIBUTING.md 0000664 0000000 0000000 00000005344 15066665201 0016533 0 ustar 00root root 0000000 0000000 Contributing to `mongo_fdw`
===========================
Following these guidelines helps to facilitate relevant discussion in
pull requests and issues so the developers managing and developing this
open source project can address patches and bugs as efficiently as
possible.
Using Issues
------------
`mongo_fdw`'s maintainers prefer that bug reports, feature requests, and
pull requests are submitted as [GitHub Issues][1].
Bug Reports
-----------
Before opening a bug report:
1. Search for a duplicate issue using GitHub's issue search
2. Check whether the bug remains in the latest `master` or `develop`
commit
3. Create a reduced test case: remove code and data not relevant to
the bug
A contributor should be able to begin work on your bug without asking
too many followup questions. If you include the following information,
your bug will be serviced more quickly:
* Short, descriptive title
* Your OS
* Versions of dependencies
* Any custom modifications
Once the background information is out of the way, you are free to
present the bug itself. You should explain:
* Steps you took to exercise the bug
* The expected outcome
* What actually occurred
Feature Requests
----------------
We are open to adding features but ultimately control the scope and aims
of the project. If a proposed feature is likely to incur high testing,
maintenance, or performance costs it is also unlikely to be accepted.
If a _strong_ case exists for a given feature, we may be persuaded on
merit. Be specific.
Pull Requests
-------------
Well-constructed pull requests are very welcome. By _well-constructed_,
we mean they do not introduce unrelated changes or break backwards
compatibility. Just fork this repo and open a request against `develop`.
Some examples of things likely to increase the likelihood a pull request
is rejected:
* Large structural changes, including:
* Re-factoring for its own sake
* Adding languages to the project
* Unnecessary whitespace changes
* Deviation from obvious conventions
* Introduction of incompatible intellectual property
Please do not change version numbers in your pull request: they will be
updated by the project owners prior to the next release.
License
-------
By submitting a patch, you agree to allow the project owners to license
your work under the terms of the [`LICENSE`][2]. Additionally, you grant
the project owners a license under copyright covering your contribution
to the extent permitted by law. Finally, you confirm that you own said
copyright, have the legal authority to grant said license, and in doing
so are not violating any grant of rights you have made to third parties,
including your employer.
[1]: https://github.com/EnterpriseDB/mongo_fdw/issues
[2]: LICENSE
mongo_fdw-REL-5_5_3/INSTALL.md 0000664 0000000 0000000 00000011023 15066665201 0015721 0 ustar 00root root 0000000 0000000 Notes about installation Mongo Foreign Data Wrapper
===================================================
To compile the [MongoDB][1] foreign data wrapper for [PostgreSQL](https://www.postgresql.org/), `mongo-c` and `json-c`
libraries are needed. To build and install `mongo-c` and `json-c` libraries, there
are two ways. You can either use script `autogen.sh` or you can manually
perform all required steps listed.
### Notes about new MongoDB C Driver support
The current implementation is based on the driver version 1.30.2 of MongoDB.
## Installation using script
Number of manual steps needs to be performed to compile and install required
mongo-c and json-c libraries. If you want to avoid the manual steps, there is a
shell script available which will download and install the appropriate drivers
and libraries for you.
Here is how it works:
To install mongo-c and json-c libraries at custom locations, you need to
export environment variables `MONGOC_INSTALL_DIR` and `JSONC_INSTALL_DIR`
respectively. If these variables are not set then these libraries will be
installed in the default location. Please note that you need to have the
required permissions on the directory where you want to install the libraries.
* autogen.sh
The script autogen.sh will do all the necessary steps to build with mongo-c
driver accordingly.
## Steps for manual installation
### mongo-c
1. Download and extract source code of mongoc driver for version `1.30.2`
```sh
wget https://github.com/mongodb/mongo-c-driver/releases/download/1.30.2/mongo-c-driver-1.30.2.tar.gz
tar xzf mongo-c-driver-1.30.2.tar.gz
rm -rf mongo-c-driver
mv mongo-c-driver-1.30.2 mongo-c-driver
cd mongo-c-driver
```
2. Configure mongoc driver
```sh
cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF .
```
To install at custom location:
```sh
cmake -DCMAKE_INSTALL_PREFIX=YOUR_INSTALLATION_DIRECTORY -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF .
```
3. Compile and install
```sh
cmake --build .
cmake --build . --target install
```
For more details on installation of mongo-c driver, you can refer [here][3].
### json-c
1. Download and extract source code
```sh
wget https://github.com/json-c/json-c/archive/json-c-0.18-20240915.tar.gz
tar -xzf json-c-0.18-20240915.tar.gz
rm -rf json-c
mv json-c-json-c-0.18-20240915/ json-c
cd json-c
```
2. Configure
```sh
cmake .
```
To install at custom location:
```sh
cmake -DCMAKE_INSTALL_PREFIX=YOUR_INSTALLATION_DIRECTORY .
```
3. Compile and install
```sh
make
make install
```
For more details on installation of json-c library, you can refer [here][4].
## Mongo_fdw configuration, compilation and installation
The `PKG_CONFIG_PATH` environment variable must be set to mongo-c-driver source
directory for successful compilation as shown below,
```sh
export PKG_CONFIG_PATH=$YOUR_MONGO_FDW_SOURCE_DIR/mongo-c-driver/src/libmongoc/src:$YOUR_MONGO_FDW_SOURCE_DIR/mongo-c-driver/src/libbson/src:$MONGOC_INSTALL_DIR/lib/pkgconfig
```
The `LD_LIBRARY_PATH` environment variable must include the path to the mongo-c
installation directory containing the libmongoc-1.0.so and libbson-1.0.so
files. For example, assuming the installation directory is /home/mongo-c and
the libraries were created under it in lib64 sub-directory, then we can define
the `LD_LIBRARY_PATH` as:
```sh
export LD_LIBRARY_PATH=/home/mongo-c/lib64:$LD_LIBRARY_PATH
```
Note: This `LD_LIBRARY_PATH` environment variable setting must be in effect
when the `pg_ctl` utility is executed to start or restart PostgreSQL or
EDB Postgres Advanced Server.
1. To build on POSIX-compliant systems you need to ensure the
`pg_config` executable is in your path when you run `make`. This
executable is typically in your PostgreSQL installation's `bin`
directory. For example:
```sh
export PATH=/usr/local/pgsql/bin/:$PATH
```
2. Compile the code using make.
```sh
make USE_PGXS=1
```
3. Finally install the foreign data wrapper.
```sh
make USE_PGXS=1 install
```
4. Running regression test.
```sh
make USE_PGXS=1 installcheck
```
However, make sure to set the `MONGO_HOST`, `MONGO_PORT`, `MONGO_USER_NAME`,
and `MONGO_PWD` environment variables correctly. The default settings can be
found in the `mongodb_init.sh` script.
If you run into any issues, please [let us know][2].
[1]: http://www.mongodb.com
[2]: https://github.com/enterprisedb/mongo_fdw/issues/new
[3]: https://www.mongodb.com/docs/languages/c/c-driver/current/install-from-source
[4]: https://github.com/json-c/json-c/tree/json-c-0.18-20240915#build-instructions--
mongo_fdw-REL-5_5_3/LICENSE 0000664 0000000 0000000 00000016720 15066665201 0015307 0 ustar 00root root 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
mongo_fdw-REL-5_5_3/Makefile 0000664 0000000 0000000 00000003512 15066665201 0015735 0 ustar 00root root 0000000 0000000 # mongo_fdw/Makefile
#
# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
# Portions Copyright © 2012–2014 Citus Data, Inc.
#
MODULE_big = mongo_fdw
#
# We assume we are running on a POSIX compliant system (Linux, OSX). If you are
# on another platform, change env_posix.os in MONGO_OBJS with the appropriate
# environment object file.
#
LIBJSON = json-c
LIBJSON_OBJS = $(LIBJSON)/json_util.o $(LIBJSON)/json_object.o $(LIBJSON)/json_tokener.o \
$(LIBJSON)/json_object_iterator.o $(LIBJSON)/printbuf.o $(LIBJSON)/linkhash.o \
$(LIBJSON)/arraylist.o $(LIBJSON)/random_seed.o $(LIBJSON)/debug.o $(LIBJSON)/strerror_override.o
MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0)
PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON)
SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0)
OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o deparse.o $(LIBJSON_OBJS)
EXTENSION = mongo_fdw
DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql
REGRESS = server_options connection_validation dml select pushdown join_pushdown aggregate_pushdown limit_offset_pushdown
REGRESS_OPTS = --load-extension=$(EXTENSION)
ifdef USE_PGXS
#
# Users need to specify their Postgres installation path through pg_config. For
# example: /usr/local/pgsql/bin/pg_config or /usr/lib/postgresql/9.1/bin/pg_config
#
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
ifndef MAJORVERSION
MAJORVERSION := $(basename $(VERSION))
endif
ifeq (,$(findstring $(MAJORVERSION), 13 14 15 16 17 18))
$(error PostgreSQL 13, 14, 15, 16, 17, or 18 is required to compile this extension)
endif
else
subdir = contrib/mongo_fdw
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
mongo_fdw-REL-5_5_3/README.md 0000664 0000000 0000000 00000036707 15066665201 0015570 0 ustar 00root root 0000000 0000000 MongoDB Foreign Data Wrapper for PostgreSQL
============================================
This PostgreSQL extension implements a Foreign Data Wrapper (FDW) for
[MongoDB][1].
Please note that this version of mongo_fdw works with PostgreSQL and EDB
Postgres Advanced Server 13, 14, 15, 16, 17, and 18.
Contents
--------
1. [Features](#features)
2. [Supported platforms](#supported-platforms)
3. [Installation](#installation)
4. [Usage](#usage)
5. [Functions](#functions)
6. [Character set handling](#character-set-handling)
7. [Examples](#examples)
8. [Limitations](#limitations)
9. [Contributing](#contributing)
10. [Support](#support)
11. [Useful links](#useful-links)
12. [License](#license)
Features
--------
The following enhancements are added to the latest version of `mongo_fdw`:
#### Write-able FDW
The previous version was only read-only, the latest version provides the
write capability. The user can now issue an insert, update, and delete
statements for the foreign tables using the `mongo_fdw`.
#### Connection Pooling
The latest version comes with a connection pooler that utilizes the
same MongoDB database connection for all the queries in the same session.
The previous version would open a new [MongoDB][1] connection for every
query. This is a performance enhancement.
#### JOIN push-down
`mongo_fdw` now also supports join push-down. The joins between two
foreign tables from the same remote MongoDB server are pushed to a remote
server, instead of fetching all the rows for both the tables and
performing a join locally, thereby may enhance the performance. Currently,
joins involving only relational and arithmetic operators in join-clauses
are pushed down to avoid any potential join failure. Also, only the
INNER and LEFT/RIGHT OUTER joins are supported, and not the FULL OUTER,
SEMI, and ANTI join. Moreover, only joins between two tables are pushed
down and not when either inner or outer relation is the join itself.
#### AGGREGATE push-down
`mongo_fdw` now also supports aggregate push-down. Push aggregates to the
remote MongoDB server instead of fetching all of the rows and aggregating
them locally. This gives a very good performance boost for the cases
where aggregates can be pushed down. The push-down is currently limited
to aggregate functions min, max, sum, avg, and count, to avoid pushing
down the functions that are not present on the MongoDB server. The
aggregate filters, orders, variadic and distinct are not pushed down.
#### ORDER BY push-down
`mongo_fdw` now also supports order by push-down. If possible, push order
by clause to the remote server so that we get the ordered result set from
the foreign server itself. It might help us to have an efficient merge
join. NULLs behavior is opposite on the MongoDB server. Thus to get an
equivalent result, we can only push-down ORDER BY with either
ASC NULLS FIRST or DESC NULLS LAST. Moreover, as MongoDB sorts only on
fields, only column names in ORDER BY expressions are pushed down.
#### LIMIT OFFSET push-down
`mongo_fdw` now also supports limit offset push-down. Wherever possible,
perform LIMIT and OFFSET operations on the remote server. This reduces
network traffic between local PostgreSQL and remote MongoDB servers.
#### GUC variables:
* `mongo_fdw.enable_join_pushdown`: If `true`, pushes the join between two
foreign tables from the same foreign server, instead of fetching all the
rows for both the tables and performing a join locally. Default is `true`.
* `mongo_fdw.enable_aggregate_pushdown`: If `true`, pushes aggregate
operations to the foreign server, instead of fetching rows from the
foreign server and performing the operations locally. Default is `true`.
* `mongo_fdw.enable_order_by_pushdown`: If `true`, pushes the order by
operation to the foreign server, instead of fetching rows from the
foreign server and performing the sort locally. Default is `true`.
* `mongo_fdw.log_remote_query`: If `true`, logs the remote query that would
be executed on MongoDB. Intended for debugging purposes only. Default is
`false`.
Supported platforms
-------------------
`mongo_fdw` was developed on Linux, and should run on any
reasonably POSIX-compliant system.
Installation
------------
About script or manual installation, `mongo-c` driver please read the following [instructions in INSTALL.md](INSTALL.md).
If you run into any issues, please [let us know][2].
Usage
-----
## CREATE SERVER options
`mongo_fdw` accepts the following options via the `CREATE SERVER` command:
- **address** as *string*, optional, default `127.0.0.1`
Address or hostname of the MongoDB server.
- **port** as *integer*, optional, default `27017`.
Port number of the MongoDB server.
- **use_remote_estimate** as *boolean*, optional, default `false`
Controls whether `mongo_fdw` uses exact rows from
remote collection to obtain cost estimates.
- **authentication_database** as *string*, optional
Database against which user will be
authenticated against. Only valid with password based authentication.
- **replica_set** as *string*, optional
Replica set the server is member of. If set,
driver will auto-connect to correct primary in the replica set when
writing.
- **read_preference** as *string*, optional, default `primary`
`primary`, `secondary`, `primaryPreferred`,
`secondaryPreferred`, or `nearest`.
- **ssl** as *boolean*, optional, default `false`
Enable ssl. See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to
understand the options.
- **pem_file** as *string*, optional
The .pem file that contains both the TLS/SSL certificate and
key.
- **pem_pwd** as *string*, optional
The password to decrypt the certificate key file(i.e. pem_file)
- **ca_file** as *string*, optional
The .pem file that contains the root certificate chain from the
Certificate Authority.
- **ca_dir** as *string*, optional
The absolute path to the `ca_file`.
- **crl_file** as *string*, optional
The .pem file that contains the Certificate Revocation List.
- **weak_cert_validation** as *boolean*, optional, default `false`
Enable the validation checks for TLS/SSL certificates and allows the use of invalid
certificates to connect if set to `true`.
- **enable_join_pushdown** as *boolean*, optional, default `true`
If `true`, pushes the join between two foreign
tables from the same foreign server, instead of fetching all the rows
for both the tables and performing a join locally. This option can also
be set for an individual table, and if any of the tables involved in the
join has set it to false then the join will not be pushed down. The
table-level value of the option takes precedence over the server-level
option value.
- **enable_aggregate_pushdown** as *boolean*, optional, default `true`
If `true`, push aggregates to the remote
MongoDB server instead of fetching all of the rows and aggregating them
locally. This option can also be set for an individual table. The
table-level value of the option takes precedence over the server-level
option value.
- **enable_order_by_pushdown** as *boolean*, optional, default `true`
If `true`, pushes the ORDER BY clause to the foreign server instead of
performing a sort locally. This option can also be set for an individual
table, and if any of the tables involved in the query has set it to
false then the ORDER BY will not be pushed down. The table-level value
of the option takes precedence over the server-level option value.
## CREATE USER MAPPING options
`mongo_fdw` accepts the following options via the `CREATE USER MAPPING`
command:
- **username** as *string*, optional
Username to use when connecting to MongoDB.
- **password** as *string*, optional
Password to authenticate to the MongoDB server.
## CREATE FOREIGN TABLE options
`mongo_fdw` accepts the following table-level options via the
`CREATE FOREIGN TABLE` command:
- **database** as *string*, optional, default `test`
Name of the MongoDB database to query.
- **collection** as *string*, optional, default name of foreign table
Name of the MongoDB collection to query.
- **enable_join_pushdown** as *boolean*, optional, default `true`
Similar to the server-level option, but can be
configured at table level as well.
- **enable_aggregate_pushdown** as *boolean*, optional, default `true`
Similar to the server-level option, but can be configured at table level as well.
- **enable_order_by_pushdown** as *boolean*, optional, default `true`
Similar to the server-level option, but can be configured at table level as well.
No column-level options are available.
## IMPORT FOREIGN SCHEMA options
`mongo_fdw` don't supports [IMPORT FOREIGN SCHEMA](https://www.postgresql.org/docs/current/sql-importforeignschema.html)
because MongoDB is schemaless.
## TRUNCATE support
`mongo_fdw` don't implements the foreign data wrapper `TRUNCATE` API, available
from PostgreSQL 14, because MongoDB is schemaless.
Functions
---------
As well as the standard `mongo_fdw_handler()` and `mongo_fdw_validator()`
functions, `mongo_fdw` provides the following user-callable utility functions:
- **mongo_fdw_version()**
Returns the version number as an integer.
Character set handling
----------------------
`BSON` in MongoDB can only be encoded in `UTF-8`. Also `UTF-8` is recommended and
de-facto most popular PostgreSQL server encoding.
Examples
--------
As an example, the following commands demonstrate loading the
`mongo_fdw` wrapper, creating a server, and then creating a foreign
table associated with a MongoDB collection. The commands also show
specifying option values in the `OPTIONS` clause. If an option value
isn't provided, the wrapper uses the default value mentioned above.
`mongo_fdw` can collect data distribution statistics will incorporate
them when estimating costs for the query execution plan. To see selected
execution plans for a query, just run `EXPLAIN`.
### Install the extension:
Once for a database you need, as PostgreSQL superuser.
```sql
CREATE EXTENSION mongo_fdw;
```
### Create a foreign server with appropriate configuration:
Once for a foreign data source you need, as PostgreSQL superuser.
```sql
CREATE SERVER "MongoDB server" FOREIGN DATA WRAPPER mongo_fdw OPTIONS (
address '127.0.0.1',
port '27017'
);
```
### Grant usage on foreign server to normal user in PostgreSQL:
Once for a normal user (non-superuser) in PostgreSQL, as PostgreSQL superuser. It is a good idea to use a superuser only where really necessary, so let's allow a normal user to use the foreign server (this is not required for the example to work, but it's security recommendation).
```sql
GRANT USAGE ON FOREIGN SERVER "MongoDB server" TO pguser;
```
Where `pguser` is a sample user for works with foreign server (and foreign tables).
### User mapping
Create an appropriate user mapping:
```sql
CREATE USER MAPPING FOR pguser SERVER "MongoDB server" OPTIONS (
username 'mongo_user',
password 'mongo_pass'
);
```
Where `pguser` is a sample user for works with foreign server (and foreign tables).
### Create foreign table
All `CREATE FOREIGN TABLE` SQL commands can be executed as a normal PostgreSQL user if there were correct `GRANT USAGE ON FOREIGN SERVER`. No need of PostgreSQL supersuer for security reasons but also works with PostgreSQL supersuer.
Create a foreign table referencing the MongoDB collection:
```sql
-- Note: first column of the table must be "_id" of type "name".
CREATE FOREIGN TABLE warehouse (
_id name,
warehouse_id int,
warehouse_name text,
warehouse_created timestamptz
) SERVER "MongoDB server" OPTIONS (
database 'db',
collection 'warehouse'
);
```
### Typical examples with [MongoDB][1]'s equivalent statements.
#### `SELECT`
```sql
SELECT * FROM warehouse WHERE warehouse_id = 1;
```
```
_id | warehouse_id | warehouse_name | warehouse_created
--------------------------+--------------+----------------+---------------------------
53720b1904864dc1f5a571a0 | 1 | UPS | 2014-12-12 12:42:10+05:30
(1 row)
```
```
db.warehouse.find
(
{
"warehouse_id" : 1
}
).pretty()
{
"_id" : ObjectId("53720b1904864dc1f5a571a0"),
"warehouse_id" : 1,
"warehouse_name" : "UPS",
"warehouse_created" : ISODate("2014-12-12T07:12:10Z")
}
```
#### `INSERT`
```sql
INSERT INTO warehouse VALUES (0, 2, 'Laptop', '2015-11-11T08:13:10Z');
-- Note: The given value for "_id" column will be ignored and allows MongoDB to
-- insert the unique value for the "_id" column.
```
```
db.warehouse.insert
(
{
"warehouse_id" : NumberInt(2),
"warehouse_name" : "Laptop",
"warehouse_created" : ISODate("2015-11-11T08:13:10Z")
}
)
```
#### `DELETE`
```sql
DELETE FROM warehouse WHERE warehouse_id = 2;
```
```
db.warehouse.remove
(
{
"warehouse_id" : 2
}
)
```
#### `UPDATE`
```sql
UPDATE warehouse SET warehouse_name = 'UPS_NEW' WHERE warehouse_id = 1;
```
```
db.warehouse.update
(
{
"warehouse_id" : 1
},
{
"warehouse_id" : 1,
"warehouse_name" : "UPS_NEW",
"warehouse_created" : ISODate("2014-12-12T07:12:10Z")
}
)
```
#### `EXPLAIN`, `ANALYZE`
```sql
EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1;
```
```
QUERY PLAN
-----------------------------------------------------------------
Foreign Scan on warehouse (cost=0.00..0.00 rows=1000 width=84)
Filter: (warehouse_id = 1)
Foreign Namespace: db.warehouse
(3 rows)
```
```
ANALYZE warehouse;
```
Limitations
-----------
- If the BSON document key contains uppercase letters or occurs within
a nested document, ``mongo_fdw`` requires the corresponding column names
to be declared in double quotes.
- Note that PostgreSQL limits column names to 63 characters by
default. If you need column names that are longer, you can increase the
`NAMEDATALEN` constant in `src/include/pg_config_manual.h`, compile,
and re-install.
Contributing
------------
Have a fix for a bug or an idea for a great new feature? Great! Check
out the contribution guidelines [here][3].
Support
-------
This project will be modified to maintain compatibility with new
PostgreSQL and EDB Postgres Advanced Server releases.
If you need commercial support, please contact the EnterpriseDB sales
team, or check whether your existing PostgreSQL support provider can
also support `mongo_fdw`.
Useful links
------------
### Documentation
- For details, please refer to [mongo_fdw documentation][5].
### Source code
Reference FDW realization, `postgres_fdw`
- https://git.postgresql.org/gitweb/?p=postgresql.git;a=tree;f=contrib/postgres_fdw;hb=HEAD
### General FDW Documentation
- https://www.postgresql.org/docs/current/ddl-foreign-data.html
- https://www.postgresql.org/docs/current/sql-createforeigndatawrapper.html
- https://www.postgresql.org/docs/current/sql-createforeigntable.html
- https://www.postgresql.org/docs/current/sql-importforeignschema.html
- https://www.postgresql.org/docs/current/fdwhandler.html
- https://www.postgresql.org/docs/current/postgres-fdw.html
### Other FDWs
- https://wiki.postgresql.org/wiki/Fdw
- https://pgxn.org/tag/fdw/
License
-------
Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
Portions Copyright © 2012–2014 Citus Data, Inc.
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
See the [`LICENSE`][4] file for full details.
[1]: http://www.mongodb.com
[2]: https://github.com/enterprisedb/mongo_fdw/issues/new
[3]: CONTRIBUTING.md
[4]: LICENSE
[5]: https://www.enterprisedb.com/docs/mongo_data_adapter/latest/
mongo_fdw-REL-5_5_3/autogen.sh 0000775 0000000 0000000 00000004222 15066665201 0016275 0 ustar 00root root 0000000 0000000 #! /bin/bash
#-------------------------------------------------------------------------
#
# autogen.sh
# Foreign-data wrapper for remote MongoDB servers
#
# Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
#
# IDENTIFICATION
# autogen.sh
#
#-------------------------------------------------------------------------
MONGOC_VERSION=1.30.2
JSONC_VERSION=0.18-20240915
MONGOC_INSTALL="${MONGOC_INSTALL_DIR}"
JSONC_INSTALL="${JSONC_INSTALL_DIR}"
# Don't allow input to the script
if [ "$#" -ne 0 ]; then
echo "Usage: autogen.sh"
exit
fi
CMAKE_COMMAND='cmake3'
if ! [ -x "$(command -v cmake3)" ]; then
CMAKE_COMMAND='cmake'
fi
###
# Pull the latest version of Mongo C Driver's master branch
#
function checkout_mongo_driver
{
rm -rf mongo-c-driver &&
wget https://github.com/mongodb/mongo-c-driver/releases/download/$MONGOC_VERSION/mongo-c-driver-$MONGOC_VERSION.tar.gz &&
tar -zxf mongo-c-driver-$MONGOC_VERSION.tar.gz &&
mv mongo-c-driver-$MONGOC_VERSION mongo-c-driver &&
rm -rf mongo-c-driver-$MONGOC_VERSION.tar.gz
}
##
# Pull the json-c library
#
function checkout_json_lib
{
echo $PWD &&
rm -rf json-c &&
wget https://github.com/json-c/json-c/archive/json-c-$JSONC_VERSION.tar.gz &&
tar -zxf json-c-$JSONC_VERSION.tar.gz &&
mv json-c-json-c-$JSONC_VERSION json-c &&
rm -rf json-c-$JSONC_VERSION.tar.gz &&
echo $PWD
}
##
# Compile and install json-c library
#
function install_json_lib
{
cd json-c &&
$CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$JSONC_INSTALL $JSONC_CFLAGS -DDISABLE_EXTRA_LIBS=ON . &&
make install &&
cd ..
}
###
# Configure and install the Mongo C Driver and libbson
#
function install_mongoc_driver
{
cd mongo-c-driver &&
$CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$MONGOC_INSTALL -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . &&
make install &&
cd ..
}
checkout_mongo_driver &&
checkout_json_lib &&
install_mongoc_driver &&
install_json_lib &&
export PKG_CONFIG_PATH=mongo-c-driver/src/libmongoc/src:mongo-c-driver/src/libbson/src
ret=$?
if [ "$ret" -ne 0 ]; then
echo "Failed"
exit $ret
else
echo "Done"
exit 0
fi
mongo_fdw-REL-5_5_3/connection.c 0000664 0000000 0000000 00000014361 15066665201 0016604 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* connection.c
* Connection management functions for mongo_fdw
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* connection.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xact.h"
#include "common/hashfn.h"
#include "mongo_wrapper.h"
#include "utils/inval.h"
#include "utils/syscache.h"
/* Length of host */
#define HOST_LEN 256
/*
* Connection cache hash table entry
*
* The lookup key in this hash table is the foreign server OID plus the user
* mapping OID. (We use just one connection per user per foreign server,
* so that we can ensure all scans use the same snapshot during a query.)
*/
typedef struct ConnCacheKey
{
Oid serverid; /* OID of foreign server */
Oid userid; /* OID of local user whose mapping we use */
} ConnCacheKey;
typedef struct ConnCacheEntry
{
ConnCacheKey key; /* hash key (must be first) */
MONGO_CONN *conn; /* connection to foreign server, or NULL */
bool invalidated; /* true if reconnect is pending */
uint32 server_hashvalue; /* hash value of foreign server OID */
uint32 mapping_hashvalue; /* hash value of user mapping OID */
} ConnCacheEntry;
/*
* Connection cache (initialized on first use)
*/
static HTAB *ConnectionHash = NULL;
static void mongo_inval_callback(Datum arg, int cacheid, uint32 hashvalue);
/*
* mongo_get_connection
* Get a mongo connection which can be used to execute queries on the
* remote Mongo server with the user's authorization. A new connection is
* established if we don't already have a suitable one.
*/
MONGO_CONN *
mongo_get_connection(ForeignServer *server, UserMapping *user,
MongoFdwOptions *opt)
{
bool found;
ConnCacheEntry *entry;
ConnCacheKey key;
/* First time through, initialize connection cache hashtable */
if (ConnectionHash == NULL)
{
HASHCTL ctl;
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(ConnCacheKey);
ctl.entrysize = sizeof(ConnCacheEntry);
ctl.hash = tag_hash;
/* Allocate ConnectionHash in the cache context */
ctl.hcxt = CacheMemoryContext;
ConnectionHash = hash_create("mongo_fdw connections", 8,
&ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
/*
* Register some callback functions that manage connection cleanup.
* This should be done just once in each backend.
*/
CacheRegisterSyscacheCallback(FOREIGNSERVEROID,
mongo_inval_callback, (Datum) 0);
CacheRegisterSyscacheCallback(USERMAPPINGOID,
mongo_inval_callback, (Datum) 0);
}
/* Create hash key for the entry. Assume no pad bytes in key struct */
key.serverid = server->serverid;
key.userid = user->userid;
/*
* Find or create cached entry for requested connection.
*/
entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found);
if (!found)
{
/* Initialize new hashtable entry (key is already filled in) */
entry->conn = NULL;
}
/* If an existing entry has invalid connection then release it */
if (entry->conn != NULL && entry->invalidated)
{
elog(DEBUG3, "disconnecting mongo_fdw connection %p for option changes to take effect",
entry->conn);
mongoDisconnect(entry->conn);
entry->conn = NULL;
}
if (entry->conn == NULL)
{
entry->conn = mongoConnect(opt);
elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"",
entry->conn, opt->svr_address, opt->svr_port);
/*
* Once the connection is established, then set the connection
* invalidation flag to false, also set the server and user mapping
* hash values.
*/
entry->invalidated = false;
entry->server_hashvalue =
GetSysCacheHashValue1(FOREIGNSERVEROID,
ObjectIdGetDatum(server->serverid));
entry->mapping_hashvalue =
GetSysCacheHashValue1(USERMAPPINGOID,
ObjectIdGetDatum(user->umid));
}
/* Check if the existing or new connection is reachable/active or not? */
if (entry->conn != NULL)
{
bson_error_t error;
bool retval;
bson_t *command;
/* Ping the database using "ping" command */
command = BCON_NEW("ping", BCON_INT32(1));
retval = mongoc_client_command_simple(entry->conn, opt->svr_database,
command, NULL, NULL, &error);
if (!retval)
ereport(ERROR,
(errmsg("could not connect to server %s",
server->servername),
errhint("Mongo error: \"%s\"", error.message)));
}
return entry->conn;
}
/*
* mongo_cleanup_connection
* Delete all the cache entries on backend exits.
*/
void
mongo_cleanup_connection()
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
if (ConnectionHash == NULL)
return;
hash_seq_init(&scan, ConnectionHash);
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
{
if (entry->conn == NULL)
continue;
elog(DEBUG3, "disconnecting mongo_fdw connection %p", entry->conn);
mongoDisconnect(entry->conn);
entry->conn = NULL;
}
}
/*
* mongo_release_connection
* Release connection created by calling mongo_get_connection.
*/
void
mongo_release_connection(MONGO_CONN *conn)
{
/*
* We don't close the connection individually here, will do all connection
* cleanup on the backend exit.
*/
}
/*
* mongo_inval_callback
* Connection invalidation callback function for mongo.
*
* After a change to a pg_foreign_server or pg_user_mapping catalog entry,
* mark connections depending on that entry as needing to be remade. This
* implementation is similar as pgfdw_inval_callback.
*/
static void
mongo_inval_callback(Datum arg, int cacheid, uint32 hashvalue)
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
Assert(cacheid == FOREIGNSERVEROID || cacheid == USERMAPPINGOID);
/* ConnectionHash must exist already, if we're registered */
hash_seq_init(&scan, ConnectionHash);
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
{
/* Ignore invalid entries */
if (entry->conn == NULL)
continue;
/* hashvalue == 0 means a cache reset, must clear all state */
if (hashvalue == 0 ||
(cacheid == FOREIGNSERVEROID &&
entry->server_hashvalue == hashvalue) ||
(cacheid == USERMAPPINGOID &&
entry->mapping_hashvalue == hashvalue))
entry->invalidated = true;
}
}
mongo_fdw-REL-5_5_3/data/ 0000775 0000000 0000000 00000000000 15066665201 0015205 5 ustar 00root root 0000000 0000000 mongo_fdw-REL-5_5_3/data/mongo_fixture.json 0000664 0000000 0000000 00000002641 15066665201 0020770 0 ustar 00root root 0000000 0000000 [{
"_id": {"$oid": "5381ccf9d6d81c8e8bf0434f"},
"name": "Ukraine",
"population": 45590000,
"capital": "Kyiv",
"hdi": 0.74,
"lastElections": {"type": "presidential", "date": {"$date": 1400976000000}},
"mainExports": ["Semi-finished products of iron or non-alloy steel",
"Flat-rolled products of iron or non-alloy steel",
"Sunflower-seed, safflower or cotton-seed oil"]
}, {
"_id": {"$oid": "5381ccf9d6d81c8e8bf04350"},
"name": "Poland",
"population": 38540000,
"capital": "Warsaw",
"hdi": 0.821,
"lastElections": {"type": "presidential", "date": {"$date": 1400976000000}},
"lastElections": {"type": "parliamentary", "date": {"$date": 1318118400000}},
"mainExports": ["Parts and accessories of the motor vehicles of headings 87.01 to 87.0",
"Motor cars and other motor vehicles principally designed for the transport",
"Reception apparatus for television"]
}, {
"_id": {"$oid": "5381ccf9d6d81c8e8bf04351"},
"name": "Moldova",
"population": 3560000,
"capital": "Chișinău",
"hdi": 0.66,
"lastElections": {"type": "parliamentary", "date": {"$date": 1290902400000}},
"mainExports": ["Wine of fresh grapes, including fortified wines",
"Insulated (including enameled or anodized) wire, cable",
"Sunflower seeds, whether or not broken"]
}]
mongo_fdw-REL-5_5_3/data/mongo_test_data.js 0000664 0000000 0000000 00000020466 15066665201 0020722 0 ustar 00root root 0000000 0000000 // Cleanup of databases/collections created during regression run
// As 'test' is a default database, any foreign table created when
// database is not mentioned then corresponding collection gets
// created in test database. So dropping as part of cleanup.
use test
db.mongo_test3.drop();
use mongo_fdw_regress1
db.mongo_test1.drop();
use mongo_fdw_regress2
db.dropDatabase();
use mongo_fdw_regress
db.test_tbl1.drop();
db.test_tbl2.drop();
db.test_tbl3.drop();
db.test_tbl4.drop();
db.test_tbl5.drop();
db.test_tbl7.drop();
db.test_tbl8.drop();
db.test1.drop();
db.test2.drop();
db.test3.drop();
db.test4.drop();
db.mongo_test.drop();
db.test5.drop();
// Below queries will create and insert values in collections
db.mongo_test.insert({a : NumberInt(0), b : "mongo_test collection"});
db.test_tbl2.insertMany([
{c1 : NumberInt(10), c2 : "DEVELOPMENT", c3 :"PUNE" },
{c1: NumberInt(20), c2 : "ADMINISTRATION", c3 :"BANGLORE" },
{c1: NumberInt(30), c2 : "SALES", c3 :"MUMBAI" },
{c1: NumberInt(40), c2 : "HR", c3 :"NAGPUR" }
]);
db.test_tbl1.insertMany([
{c1: NumberInt(100), c2 : "EMP1", c3 :"ADMIN", c4 :NumberInt(1300) ,c5 :ISODate("1980-12-17"), c6 :800.300, c7 :NumberInt(0), c8 :NumberInt(20) },
{c1: NumberInt(200), c2 : "EMP2", c3 :"SALESMAN", c4 :NumberInt(600) ,c5 :ISODate("1981-02-20"), c6 :1600, c7 :NumberInt(300), c8 :NumberInt(30) },
{c1: NumberInt(300), c2 : "EMP3", c3 :"SALESMAN", c4 :NumberInt(600) ,c5 :ISODate("1981-02-22"), c6 :1250, c7 :NumberInt(500), c8 :NumberInt(30) },
{c1: NumberInt(400), c2 : "EMP4", c3 :"MANAGER", c4 :NumberInt(900) ,c5 :ISODate("1981-04-02"), c6 :2975, c7 :NumberInt(0), c8 :NumberInt(20) },
{c1: NumberInt(500), c2 : "EMP5", c3 :"SALESMAN", c4 :NumberInt(600) ,c5 :ISODate("1981-09-28"), c6 :1250.23, c7 :NumberInt(1400), c8 :NumberInt(30) },
{c1: NumberInt(600), c2 : "EMP6", c3 :"MANAGER", c4 :NumberInt(900) ,c5 :ISODate("1981-05-01"), c6 :2850, c7 :NumberInt(0), c8 :NumberInt(30) },
{c1: NumberInt(700), c2 : "EMP7", c3 :"MANAGER", c4 :NumberInt(900) ,c5 :ISODate("1981-06-09"), c6 :2450.34, c7 :NumberInt(0), c8 :NumberInt(10) },
{c1: NumberInt(800), c2 : "EMP8", c3 :"FINANCE", c4 :NumberInt(400) ,c5 :ISODate("1987-04-19"), c6 :3000, c7 :NumberInt(0), c8 :NumberInt(20) },
{c1: NumberInt(900), c2 : "EMP9", c3 :"HEAD", c4 :null ,c5 :ISODate("1981-11-17"), c6 :5000, c7 :NumberInt(0), c8 :NumberInt(10) },
{c1: NumberInt(1000), c2 : "EMP10", c3 :"SALESMAN", c4 :NumberInt(600) ,c5 :ISODate("1980-09-08"), c6 :1500, c7 :NumberInt(0), c8 :NumberInt(30) },
{c1: NumberInt(1100), c2 : "EMP11", c3 :"ADMIN", c4 :NumberInt(800) ,c5 :ISODate("1987-05-23"), c6 :1100, c7 :NumberInt(0), c8 :NumberInt(20) },
{c1: NumberInt(1200), c2 : "EMP12", c3 :"ADMIN", c4 :NumberInt(600) ,c5 :ISODate("1981-12-03"), c6 :950.00, c7 :NumberInt(0), c8 :NumberInt(30) },
{c1: NumberInt(1300), c2 : "EMP13", c3 :"FINANCE", c4 :NumberInt(400) ,c5 :ISODate("1981-12-03"), c6 :3000, c7 :NumberInt(0), c8 :NumberInt(20) },
{c1: NumberInt(1400), c2 : "EMP14", c3 :"ADMIN", c4 :NumberInt(700) ,c5 :ISODate("1982-01-23"), c6 :1300, c7 :NumberInt(0), c8 :NumberInt(10) },
]);
db.test_tbl3.insertMany([
{name: "dvd", marks: [23, 24], pass: false},
{name: "vdd", marks: [29, 31], pass: true}
]);
db.test1.insertMany([
{c1: NumberInt(1), c2: NumberInt(1), c3: "A"},
{c1: NumberInt(2), c2: NumberInt(2), c3: "B"},
{c1: NumberInt(3), c2: NumberInt(3), c3: "C"},
{c1: NumberInt(4), c2: NumberInt(4), c3: "D"},
]);
db.test2.insertMany([
{c1: NumberInt(5), c2: NumberInt(5), c3: "E"},
{c1: NumberInt(6), c2: NumberInt(6), c3: "F"},
{c1: NumberInt(7), c2: NumberInt(7), c3: "G"},
{c1: NumberInt(8), c2: NumberInt(8), c3: "H"},
]);
db.test3.insertMany([
{c1: NumberInt(1), c2: NumberInt(1), c3: "A"},
{c1: NumberInt(2), c2: NumberInt(2), c3: "B"},
{c1: NumberInt(3), c2: NumberInt(3), c3: "C"},
{c1: NumberInt(4), c2: NumberInt(4), c3: "D"},
]);
db.test4.insertMany([
{c1: NumberInt(5), c2: NumberInt(5), c3: "E"},
{c1: NumberInt(6), c2: NumberInt(6), c3: "F"},
{c1: NumberInt(7), c2: NumberInt(7), c3: "G"},
{c1: NumberInt(8), c2: NumberInt(8), c3: "H"},
]);
db.test5.insertMany([
{c1: 12.345678},
{c1: -1.23}
]);
db.test_tbl4.insertMany([
{a: NumberInt(25)},
{a: NumberLong(9999999999)},
{a: 25},
{a: 25.09},
{a: false}
]);
db.test_tbl5.insertMany([
{a: NumberInt(25)},
{a: 25},
{a: 25.09},
{a: true}
]);
db.test_tbl7.insertMany([
{a: NumberInt(10), b: "ROW1"},
{a: NumberInt(20), b: "ROW2"}
]);
db.test_tbl8.insertMany([
{_id: NumberInt(1), a: NumberInt(2), b: "ROW1"},
{a: NumberInt(3), b: "ROW2"},
]);
db.mongo_test_large.drop();
db.mongo_test_large.insertMany([
{_id: NumberInt(0), a01 : NumberInt(1), a02 : NumberInt(2), a03 : NumberInt(3), a04 : NumberInt(4), a05 : NumberInt(5), a06 : NumberInt(6), a07 : NumberInt(7), a08 : NumberInt(8), a09 : NumberInt(9), a10 : NumberInt(10), a11 : NumberInt(11), a12 : NumberInt(12), a13 : NumberInt(13), a14 : NumberInt(14), a15 : NumberInt(15), a16 : NumberInt(16), a17 : NumberInt(17), a18 : NumberInt(18), a19 : NumberInt(19), a20 : NumberInt(20), a21 : NumberInt(21), a22 : NumberInt(22), a23 : NumberInt(23), a24 : NumberInt(24), a25 : NumberInt(25), a26 : NumberInt(26), a27 : NumberInt(27), a28 : NumberInt(28), a29 : NumberInt(29), a30 : NumberInt(30), a31 : NumberInt(31), a32 : NumberInt(32), a33 : NumberInt(33), a34 : NumberInt(134), a35 : NumberInt(35)},
{_id: NumberInt(1), a01 : NumberInt(1), a02 : NumberInt(2), a03 : NumberInt(3), a04 : NumberInt(4), a05 : NumberInt(5), a06 : NumberInt(6), a07 : NumberInt(7), a08 : NumberInt(8), a09 : NumberInt(9), a10 : NumberInt(10), a11 : NumberInt(11), a12 : NumberInt(12), a13 : NumberInt(13), a14 : NumberInt(14), a15 : NumberInt(15), a16 : NumberInt(16), a17 : NumberInt(17), a18 : NumberInt(18), a19 : NumberInt(19), a20 : NumberInt(20), a21 : NumberInt(21), a22 : NumberInt(22), a23 : NumberInt(23), a24 : NumberInt(24), a25 : NumberInt(25), a26 : NumberInt(26), a27 : NumberInt(27), a28 : NumberInt(28), a29 : NumberInt(29), a30 : NumberInt(30), a31 : NumberInt(31), a32 : NumberInt(2), a33 : NumberInt(3), a34 : NumberInt(4), a35 : NumberInt(5)},
{_id: NumberInt(2), a01 : NumberInt(1), a02 : NumberInt(2), a03 : NumberInt(3), a04 : NumberInt(4), a05 : NumberInt(5), a06 : NumberInt(6), a07 : NumberInt(7), a08 : NumberInt(8), a09 : NumberInt(9), a10 : NumberInt(10), a11 : NumberInt(11), a12 : NumberInt(12), a13 : NumberInt(13), a14 : NumberInt(14), a15 : NumberInt(15), a16 : NumberInt(16), a17 : NumberInt(17), a18 : NumberInt(18), a19 : NumberInt(19), a20 : NumberInt(20), a21 : NumberInt(21), a22 : NumberInt(22), a23 : NumberInt(23), a24 : NumberInt(24), a25 : NumberInt(25), a26 : NumberInt(26), a27 : NumberInt(27), a28 : NumberInt(28), a29 : NumberInt(29), a30 : NumberInt(30), a31 : NumberInt(31), a32 : NumberInt(132), a33 : NumberInt(133), a34 : NumberInt(134), a35 : NumberInt(135)},
{_id: NumberInt(3), a01 : NumberInt(1), a02 : NumberInt(2), a03 : NumberInt(3), a04 : NumberInt(4), a05 : NumberInt(5), a06 : NumberInt(6), a07 : NumberInt(7), a08 : NumberInt(8), a09 : NumberInt(9), a10 : NumberInt(10), a11 : NumberInt(11), a12 : NumberInt(12), a13 : NumberInt(13), a14 : NumberInt(14), a15 : NumberInt(15), a16 : NumberInt(16), a17 : NumberInt(17), a18 : NumberInt(18), a19 : NumberInt(19), a20 : NumberInt(20), a21 : NumberInt(21), a22 : NumberInt(22), a23 : NumberInt(23), a24 : NumberInt(24), a25 : NumberInt(25), a26 : NumberInt(26), a27 : NumberInt(27), a28 : NumberInt(28), a29 : NumberInt(29), a30 : NumberInt(30), a31 : NumberInt(31), a32 : NumberInt(32), a33 : NumberInt(3), a34 : NumberInt(34), a35 : NumberInt(35)},
{_id: NumberInt(4), a01 : NumberInt(1), a02 : NumberInt(2), a03 : NumberInt(3), a04 : NumberInt(4), a05 : NumberInt(5), a06 : NumberInt(6), a07 : NumberInt(7), a08 : NumberInt(8), a09 : NumberInt(9), a10 : NumberInt(10), a11 : NumberInt(11), a12 : NumberInt(12), a13 : NumberInt(13), a14 : NumberInt(14), a15 : NumberInt(15), a16 : NumberInt(16), a17 : NumberInt(17), a18 : NumberInt(18), a19 : NumberInt(19), a20 : NumberInt(20), a21 : NumberInt(21), a22 : NumberInt(22), a23 : NumberInt(23), a24 : NumberInt(24), a25 : NumberInt(25), a26 : NumberInt(26), a27 : NumberInt(27), a28 : NumberInt(28), a29 : NumberInt(29), a30 : NumberInt(30), a31 : NumberInt(31), a32 : NumberInt(32), a33 : NumberInt(33), a34 : NumberInt(34), a35 : NumberInt(35)}
]);
mongo_fdw-REL-5_5_3/data/mongo_testdevice.json 0000664 0000000 0000000 00000000176 15066665201 0021442 0 ustar 00root root 0000000 0000000 [
{
"_id": {
"$oid": "6580400c4898199d6e0173cd"
},
"mac": "001122334455",
"name": "test device",
"level": 3
}
]
mongo_fdw-REL-5_5_3/data/mongo_testlog.json 0000664 0000000 0000000 00000000261 15066665201 0020757 0 ustar 00root root 0000000 0000000 [
{
"_id": {
"$oid": "658040214898199d6e0173d0"
},
"log": "hello log",
"logMeta": {
"logMac": "001122334455",
"nestMore": {
"level": 3
}
}
}
]
mongo_fdw-REL-5_5_3/data/mongo_warehouse.json 0000664 0000000 0000000 00000000472 15066665201 0021304 0 ustar 00root root 0000000 0000000 [
{
"_id" : {"$oid": "58a1ebbaf543ec0b90545859"},
"warehouse_id" : 1,
"warehouse_name" : "UPS",
"warehouse_created" : {"$date": 1418368330000}
},
{
"_id" : {"$oid": "58a1ebbaf543ec0b9054585a"},
"warehouse_id" : 2,
"warehouse_name" : "Laptop",
"warehouse_created" : {"$date": 1447229590000}
}
]
mongo_fdw-REL-5_5_3/deparse.c 0000664 0000000 0000000 00000043305 15066665201 0016070 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* deparse.c
* Query deparser for mongo_fdw
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* deparse.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "mongo_wrapper.h"
#include
#include
#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "common/hashfn.h"
#include "mongoc.h"
#include "mongo_query.h"
#include "optimizer/optimizer.h"
#include "parser/parsetree.h"
#include "utils/rel.h"
#include "utils/syscache.h"
/*
* Functions to gather information related to columns involved in the given
* query, which is useful at the time of execution to prepare MongoDB query.
*/
static void mongo_check_op_expr(OpExpr *node, MongoRelQualInfo *qual_info);
static void mongo_check_var(Var *column, MongoRelQualInfo *qual_info);
/* Helper functions to form MongoDB query document. */
static void mongo_append_bool_expr(BoolExpr *node, BSON *queryDoc,
pipeline_cxt *context);
static void mongo_append_op_expr(OpExpr *node, BSON *child,
pipeline_cxt *context);
static void mongo_append_column_name(Var *column, BSON *queryDoc,
pipeline_cxt *context);
static void mongo_add_null_check(Var *column, BSON *expr,
pipeline_cxt *context);
/*
* mongo_check_qual
* Check the given qual expression and find the columns used in it. We
* recursively traverse until we get a Var node and then retrieve the
* required information from it.
*/
void
mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info)
{
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_Var:
mongo_check_var((Var *) node, qual_info);
break;
case T_OpExpr:
mongo_check_op_expr((OpExpr *) node, qual_info);
break;
case T_List:
{
ListCell *lc;
foreach(lc, (List *) node)
mongo_check_qual((Expr *) lfirst(lc), qual_info);
}
break;
case T_RelabelType:
mongo_check_qual(((RelabelType *) node)->arg, qual_info);
break;
case T_BoolExpr:
mongo_check_qual((Expr *) ((BoolExpr *) node)->args, qual_info);
break;
case T_Aggref:
{
ListCell *lc;
char *func_name = get_func_name(((Aggref *) node)->aggfnoid);
/* Save aggregation operation name */
qual_info->aggTypeList = lappend(qual_info->aggTypeList,
makeString(func_name));
qual_info->is_agg_column = true;
/* Save information whether this is a HAVING clause or not */
if (qual_info->is_having)
qual_info->isHavingList = lappend_int(qual_info->isHavingList,
true);
else
qual_info->isHavingList = lappend_int(qual_info->isHavingList,
false);
/*
* The aggregation over '*' doesn't need column information.
* Hence, only to maintain the length of column information
* lists add dummy members into it.
*
* For aggregation over the column, add required information
* into the column information lists.
*/
if (((Aggref *) node)->aggstar)
{
qual_info->colNameList = lappend(qual_info->colNameList,
makeString("*"));
qual_info->colNumList = lappend_int(qual_info->colNumList,
0);
qual_info->rtiList = lappend_int(qual_info->rtiList, 0);
qual_info->isOuterList = lappend_int(qual_info->isOuterList,
0);
/* Append dummy var */
qual_info->aggColList = lappend(qual_info->aggColList,
makeVar(0, 0, 0, 0, 0, 0));
qual_info->is_agg_column = false;
}
else
{
foreach(lc, ((Aggref *) node)->args)
{
Node *n = (Node *) lfirst(lc);
/* If TargetEntry, extract the expression from it */
if (IsA(n, TargetEntry))
{
TargetEntry *tle = (TargetEntry *) n;
n = (Node *) tle->expr;
}
mongo_check_qual((Expr *) n, qual_info);
}
}
}
break;
case T_Const:
case T_Param:
/* Nothing to do here because we are looking only for Var's */
break;
default:
elog(ERROR, "unsupported expression type to check: %d",
(int) nodeTag(node));
break;
}
}
/*
* mongo_check_op_expr
* Check given operator expression.
*/
static void
mongo_check_op_expr(OpExpr *node, MongoRelQualInfo *qual_info)
{
HeapTuple tuple;
Form_pg_operator form;
char oprkind;
ListCell *arg;
/* Retrieve information about the operator from the system catalog. */
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for operator %u", node->opno);
form = (Form_pg_operator) GETSTRUCT(tuple);
oprkind = form->oprkind;
/* Sanity check. */
Assert((oprkind == 'r' && list_length(node->args) == 1) ||
(oprkind == 'l' && list_length(node->args) == 1) ||
(oprkind == 'b' && list_length(node->args) == 2));
/* Deparse left operand. */
if (oprkind == 'r' || oprkind == 'b')
{
arg = list_head(node->args);
mongo_check_qual(lfirst(arg), qual_info);
}
/* Deparse right operand. */
if (oprkind == 'l' || oprkind == 'b')
{
arg = list_tail(node->args);
mongo_check_qual(lfirst(arg), qual_info);
}
ReleaseSysCache(tuple);
}
/*
* mongo_check_var
* Check the given Var and append required information related to columns
* involved in qual clauses to separate lists in context. Prepare separate
* list for aggregated columns directly (not related information).
*
* Save required information in the form of a list in MongoRelQualInfo
* structure. Prepare a hash table to avoid duplication of entry if one column
* is involved in the multiple qual expressions.
*/
static void
mongo_check_var(Var *column, MongoRelQualInfo *qual_info)
{
RangeTblEntry *rte;
char *colname;
ColumnHashKey key;
bool found;
bool is_outerrel = false;
if (!(bms_is_member(column->varno, qual_info->foreignRel->relids) &&
column->varlevelsup == 0))
return; /* Var does not belong to foreign table */
Assert(!IS_SPECIAL_VARNO(column->varno));
if (!qual_info->exprColHash)
{
HASHCTL hashInfo;
memset(&hashInfo, 0, sizeof(hashInfo));
hashInfo.keysize = sizeof(ColumnHashKey);
hashInfo.entrysize = sizeof(ColumnHashKey);
hashInfo.hcxt = CurrentMemoryContext;
qual_info->exprColHash = hash_create("Join Expression Column Hash",
MaxHashTableSize,
&hashInfo,
(HASH_ELEM | HASH_BLOBS | HASH_CONTEXT));
}
key.varno = column->varno;
key.varattno = column->varattno;
hash_search(qual_info->exprColHash, (void *) &key, HASH_ENTER, &found);
/*
* Add aggregated column in the aggColList even if it's already available
* in the hash table. This is because multiple aggregation operations can
* be done on the same column. So, to maintain the same length of
* aggregation functions and their columns, add each aggregation column.
*/
if (qual_info->is_agg_column)
{
qual_info->aggColList = lappend(qual_info->aggColList, column);
qual_info->is_agg_column = false;
if (found)
return;
}
/*
* Don't add the duplicate column. The Aggregated column is already taken
* care of.
*/
if (found)
return;
/* Get RangeTblEntry from array in PlannerInfo. */
rte = planner_rt_fetch(column->varno, qual_info->root);
colname = get_attname(rte->relid, column->varattno, false);
/* Is relation inner or outer? */
if (bms_is_member(column->varno, qual_info->outerRelids))
is_outerrel = true;
/* Fill the lists with elements */
qual_info->colNameList = lappend(qual_info->colNameList, makeString(colname));
qual_info->colNumList = lappend_int(qual_info->colNumList, column->varattno);
qual_info->rtiList = lappend_int(qual_info->rtiList, column->varno);
qual_info->isOuterList = lappend_int(qual_info->isOuterList, is_outerrel);
}
/*
* mongo_get_jointype_name
* Output join name for given join type
*/
const char *
mongo_get_jointype_name(JoinType jointype)
{
switch (jointype)
{
case JOIN_INNER:
return "INNER";
case JOIN_LEFT:
return "LEFT";
case JOIN_RIGHT:
return "RIGHT";
default:
/* Shouldn't come here, but protect from buggy code. */
elog(ERROR, "unsupported join type %d", jointype);
}
/* Keep compiler happy */
return NULL;
}
/*
* mongo_append_expr
* Append given expression node.
*/
void
mongo_append_expr(Expr *node, BSON *child_doc, pipeline_cxt *context)
{
if (node == NULL)
return;
switch (nodeTag(node))
{
case T_Var:
mongo_append_column_name((Var *) node, child_doc, context);
break;
case T_Const:
append_constant_value(child_doc,
psprintf("%d", context->arrayIndex),
(Const *) node);
break;
case T_OpExpr:
mongo_append_op_expr((OpExpr *) node, child_doc, context);
break;
case T_RelabelType:
mongo_append_expr(((RelabelType *) node)->arg, child_doc, context);
break;
case T_BoolExpr:
mongo_append_bool_expr((BoolExpr *) node, child_doc, context);
break;
case T_Param:
append_param_value(child_doc, psprintf("%d", context->arrayIndex),
(Param *) node, context->scanStateNode);
break;
case T_Aggref:
bsonAppendUTF8(child_doc, "0", "$v_having");
break;
default:
elog(ERROR, "unsupported expression type to append: %d",
(int) nodeTag(node));
break;
}
}
/*
* mongo_append_bool_expr
* Recurse through a BoolExpr node to form MongoDB query pipeline.
*/
static void
mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context)
{
BSON child;
BSON expr;
const char *op = NULL;
ListCell *lc;
int saved_array_index;
int reset_index = 0;
switch (node->boolop)
{
case AND_EXPR:
op = "$and";
break;
case OR_EXPR:
op = "$or";
break;
case NOT_EXPR:
op = "$not";
break;
}
bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex), &expr);
bsonAppendStartArray(&expr, op, &child);
/* Save array index */
saved_array_index = context->arrayIndex;
/* Reset to zero to be used for nested arrays */
context->arrayIndex = reset_index;
/* Save join expression type boolean "TRUE" */
context->isBoolExpr = true;
foreach(lc, node->args)
{
mongo_append_expr((Expr *) lfirst(lc), &child, context);
context->arrayIndex++;
}
bsonAppendFinishArray(&expr, &child);
bsonAppendFinishObject(child_doc, &expr);
/* Retain array index */
context->arrayIndex = saved_array_index;
}
/*
* mongo_append_op_expr
* Deparse given operator expression.
*
* Build and append following syntax into $and array:
*
* {"$eq": [ "$$v_age", "$old" ] }
*
* Each element of operator (e.g. "$eq") array is appended by function called
* mongo_append_column_name.
*
* In MongoDB, (null = null), (null < 1) is TRUE but that is FALSE in Postgres.
* To eliminate null value rows, add equality check for null values for columns
* involved in JOIN and WHERE clauses. E.g. add the following syntax:
*
* {"$ne": [ "$$v_age", null ]},
* {"$ne": [ "$old", null ]}
*/
static void
mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context)
{
HeapTuple tuple;
Form_pg_operator form;
char oprkind;
ListCell *arg;
BSON expr;
BSON child1;
char *mongo_operator;
int saved_array_index;
int reset_index = 0;
int and_index = 0;
BSON and_op;
BSON and_obj;
/* Increament operator expression count */
context->opExprCount++;
/* Retrieve information about the operator from the system catalog. */
tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for operator %u", node->opno);
form = (Form_pg_operator) GETSTRUCT(tuple);
oprkind = form->oprkind;
/* Sanity check. */
Assert((oprkind == 'r' && list_length(node->args) == 1) ||
(oprkind == 'l' && list_length(node->args) == 1) ||
(oprkind == 'b' && list_length(node->args) == 2));
if (context->isBoolExpr == true)
{
bsonAppendStartObject(child_doc, psprintf("%d", and_index++),
&and_obj);
bsonAppendStartArray(&and_obj, "$and", &and_op);
bsonAppendStartObject(&and_op, psprintf("%d", context->arrayIndex),
&expr);
}
else
bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex),
&expr);
/* Deparse operator name. */
mongo_operator = mongo_operator_name(get_opname(node->opno));
bsonAppendStartArray(&expr, mongo_operator, &child1);
/* Save array index */
saved_array_index = context->arrayIndex;
/* Reset to zero to be used for nested arrays */
context->arrayIndex = reset_index;
/* Deparse left operand. */
if (oprkind == 'r' || oprkind == 'b')
{
arg = list_head(node->args);
mongo_append_expr(lfirst(arg), &child1, context);
}
/* Deparse right operand. */
if (oprkind == 'l' || oprkind == 'b')
{
if (oprkind == 'l')
context->arrayIndex = reset_index;
else
context->arrayIndex++;
arg = list_tail(node->args);
mongo_append_expr(lfirst(arg), &child1, context);
}
/* Decreament operator expression count */
context->opExprCount--;
bsonAppendFinishArray(&expr, &child1);
if (context->isBoolExpr)
bsonAppendFinishObject(&and_op, &expr);
else
bsonAppendFinishObject(child_doc, &expr);
/*
* Add equality check for null values for columns involved in JOIN and
* WHERE clauses.
*/
if (context->opExprCount == 0)
{
List *var_list;
ListCell *lc;
var_list = pull_var_clause((Node *) node, PVC_RECURSE_PLACEHOLDERS ||
PVC_RECURSE_AGGREGATES);
foreach(lc, var_list)
{
Var *var = (Var *) lfirst(lc);
if (context->isBoolExpr)
bsonAppendStartObject(&and_op, psprintf("%d", and_index++),
&expr);
else
bsonAppendStartObject(child_doc,
psprintf("%d", context->arrayIndex++),
&expr);
mongo_add_null_check(var, &expr, context);
if (context->isBoolExpr)
bsonAppendFinishObject(&and_op, &expr);
else
bsonAppendFinishObject(child_doc, &expr);
}
}
if (context->isBoolExpr == true)
{
bsonAppendFinishArray(&and_obj, &and_op);
bsonAppendFinishObject(child_doc, &and_obj);
}
/* Retain array index */
context->arrayIndex = saved_array_index;
ReleaseSysCache(tuple);
}
/*
* mongo_append_column_name
* Deparse Var and append corresponding column name to operator array.
*
* The elements of the operator array are appended by this function.
*/
static void
mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context)
{
bool found = false;
ColInfoHashKey key;
ColInfoHashEntry *columnInfo;
char *field;
key.varNo = column->varno;
key.varAttno = column->varattno;
columnInfo = (ColInfoHashEntry *) hash_search(context->colInfoHash,
(void *) &key,
HASH_FIND,
&found);
if (!found)
return;
if (columnInfo->isOuter && context->isJoinClause)
field = psprintf("$$%s",
get_varname_for_outer_col(columnInfo->colName));
else
field = psprintf("$%s", columnInfo->colName);
bsonAppendUTF8(child_doc, psprintf("%d", context->arrayIndex), field);
}
/*
* mongo_add_null_check
* Eliminate null value rows of columns involved in the join and WHERE
* clauses.
*/
static void
mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context)
{
BSON ne_expr;
bool found = false;
ColInfoHashKey key;
ColInfoHashEntry *columnInfo;
char *field;
key.varNo = column->varno;
key.varAttno = column->varattno;
columnInfo = (ColInfoHashEntry *) hash_search(context->colInfoHash,
(void *) &key,
HASH_FIND,
&found);
if (!found)
return;
if (columnInfo->isOuter && context->isJoinClause)
field = psprintf("$$%s",
get_varname_for_outer_col(columnInfo->colName));
else
field = psprintf("$%s", columnInfo->colName);
bsonAppendStartArray(expr, "$ne", &ne_expr);
bsonAppendUTF8(&ne_expr, "0", field);
bsonAppendNull(&ne_expr, "1");
bsonAppendFinishArray(expr, &ne_expr);
}
/*
* mongo_is_foreign_pathkey
* Returns true if it's safe to push down the sort expression described by
* 'pathkey' to the foreign server.
*/
bool
mongo_is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel,
PathKey *pathkey)
{
EquivalenceMember *em;
EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
Expr *em_expr;
/*
* mongo_is_foreign_expr would detect volatile expressions as well, but
* checking ec_has_volatile here saves some cycles.
*/
if (pathkey_ec->ec_has_volatile)
return false;
/* can push if a suitable EC member exists */
if (!(em = mongo_find_em_for_rel(root, pathkey_ec, baserel)))
return false;
/* Ignore binary-compatible relabeling */
em_expr = em->em_expr;
while (em_expr && IsA(em_expr, RelabelType))
em_expr = ((RelabelType *) em_expr)->arg;
/* Only Vars are allowed per MongoDB. */
if (!IsA(em_expr, Var))
return false;
/* Check for sort operator pushability. */
if (!mongo_is_default_sort_operator(em, pathkey))
return false;
return true;
}
/*
* mongo_is_builtin
* Return true if given object is one of PostgreSQL's built-in objects.
*
* We use FirstBootstrapObjectId as the cutoff, so that we only consider
* objects with hand-assigned OIDs to be "built in", not for instance any
* function or type defined in the information_schema.
*
* Our constraints for dealing with types are tighter than they are for
* functions or operators: we want to accept only types that are in pg_catalog,
* else format_type might incorrectly fail to schema-qualify their names.
* (This could be fixed with some changes to format_type, but for now there's
* no need.) Thus we must exclude information_schema types.
*
* XXX there is a problem with this, which is that the set of built-in
* objects expands over time. Something that is built-in to us might not
* be known to the remote server, if it's of an older version. But keeping
* track of that would be a huge exercise.
*/
bool
mongo_is_builtin(Oid oid)
{
return (oid < FirstGenbkiObjectId);
}
mongo_fdw-REL-5_5_3/expected/ 0000775 0000000 0000000 00000000000 15066665201 0016075 5 ustar 00root root 0000000 0000000 mongo_fdw-REL-5_5_3/expected/aggregate_pushdown.out 0000664 0000000 0000000 00000245645 15066665201 0022523 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables.
CREATE FOREIGN TABLE fdw137_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE fdw137_t2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
INSERT INTO fdw137_t1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO fdw137_t1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO fdw137_t2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO fdw137_t2 VALUES (0);
-- Create local table.
CREATE TABLE fdw137_local AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1;
-- Simple aggregates. ORDER BY push-down not possible because only column names allowed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Result
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), ((sum(c1)) * ((random() <= '1'::double precision))::integer), c4
-> Sort
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Sort Key: (count(*)) NULLS FIRST, (sum(fdw137_t1.c1)) NULLS FIRST
-> Foreign Scan
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
count | sum | avg | min | max | sum2
-------+------+------------------+------+------+------
1 | 1100 | 1100 | 800 | 1100 | 1100
1 | 1400 | 1400 | 700 | 1400 | 1400
2 | 1600 | 800 | 1300 | 1500 | 1600
3 | 1700 | 566.666666666667 | 900 | 700 | 1700
(4 rows)
-- GROUP BY clause HAVING expressions
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c1, (sum(c1)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum | count
------+------+-------
600 | 600 | 1
700 | 700 | 1
800 | 800 | 1
900 | 900 | 1
1000 | 1000 | 1
1100 | 1100 | 1
1200 | 1200 | 1
1300 | 1300 | 1
1400 | 1400 | 1
1500 | 1500 | 1
1600 | 1600 | 1
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c8, (min(c2))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
c8 | min
----+------
20 | EMP1
(1 row)
-- Multi-column GROUP BY clause. Push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregation on expression. Don't push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
GroupAggregate
Output: c1, sum((c1 + 2))
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum
------+------
600 | 602
700 | 702
800 | 802
900 | 902
1000 | 1002
1100 | 1102
1200 | 1202
1300 | 1302
1400 | 1402
1500 | 1502
1600 | 1602
(11 rows)
-- Aggregate with unshippable GROUP BY clause are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer))
Sort Key: (avg(fdw137_t1.c4))
-> HashAggregate
Output: avg(c4), ((c4 * ((random() <= '1'::double precision))::integer))
Group Key: (fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)
-> Foreign Scan on public.fdw137_t1
Output: (c4 * ((random() <= '1'::double precision))::integer), c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
avg
-----------------------
400.0000000000000000
600.0000000000000000
700.0000000000000000
800.0000000000000000
900.0000000000000000
1300.0000000000000000
(7 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c1, (sum(c1))
Sort Key: fdw137_t1.c1
-> HashAggregate
Output: c1, sum(c1)
Group Key: fdw137_t1.c1
Filter: (min((fdw137_t1.c1 * 3)) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
c1 | sum
------+------
200 | 200
300 | 300
400 | 400
500 | 500
600 | 600
700 | 700
800 | 800
900 | 900
1000 | 1000
1100 | 1100
1200 | 1200
1300 | 1300
1400 | 1400
1500 | 1500
1600 | 1600
(15 rows)
-- FDW-134: Test ORDER BY with COLLATE. Shouldn't push-down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), ((c2)::text), c1
Sort Key: fdw137_t1.c2 COLLATE "en_US" NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c2, c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- Using expressions in HAVING clause. Pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c3, (count(*))
Sort Key: fdw137_t1.c3, (count(*))
-> Foreign Scan
Output: c3, (count(*))
Filter: (abs((max(fdw137_t1.c8))) = 10)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(7 rows)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
c3 | count
-----------+-------
HEAD | 1
(1 row)
-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Aggregate
Output: count(*)
-> Foreign Scan
Output: fdw137_t1.c3, NULL::bigint
Filter: (((((avg(fdw137_t1.c1)) / (avg(fdw137_t1.c1))))::double precision * random()) <= '1'::double precision)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
count
-------
0
(1 row)
-- Aggregate over join query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8)), (avg(t2.c1))
Sort Key: (sum(t1.c8)) DESC NULLS LAST
-> Foreign Scan
Output: (sum(t1.c8)), (avg(t2.c1))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
sum | avg
-----+------------------
310 | 22.1428571428571
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, (count(*)), t2.c4
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2))
(3 rows)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | count | c4
----+-------+------
10 | 1 |
10 | 1 | 700
10 | 1 | 900
20 | 2 | 400
20 | 1 | 800
20 | 1 | 900
20 | 1 | 1300
30 | 5 | 600
30 | 1 | 900
(9 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregate is not pushed down as aggregation contains random()
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------
Sort
Output: (sum((c1 * ((random() <= '1'::double precision))::integer))), (avg(c1))
Sort Key: (sum((fdw137_t1.c1 * ((random() <= '1'::double precision))::integer)))
-> Aggregate
Output: sum((c1 * ((random() <= '1'::double precision))::integer)), avg(c1)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
sum | avg
-------+----------------------
13600 | 850.0000000000000000
(1 row)
-- Not pushed down due to local conditions present in underneath input rel
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8))
Sort Key: (sum(t1.c8))
-> Aggregate
Output: sum(t1.c8)
-> Foreign Scan
Output: t1.c8
Filter: (((((t1.c8 * t2.c1) / (t1.c8 * t2.c1)))::double precision * random()) <= '1'::double precision)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
sum
-----
310
(1 row)
-- Aggregates in subquery are pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
QUERY PLAN
---------------------------------------------------------------------------------------
Aggregate
Output: count(fdw137_t1.c8), sum(fdw137_t1.c8)
-> Sort
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Sort Key: fdw137_t1.c8, (sum(fdw137_t1.c1))
-> Foreign Scan
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
count | sum
-------+-----
4 | 120
(1 row)
-- Aggregate is still pushed down by taking unshippable expression out
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Sort
Output: ((c4 * ((random() <= '1'::double precision))::integer)), (sum(c1)), c4
Sort Key: ((fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)), (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (c4 * ((random() <= '1'::double precision))::integer), (sum(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
sum1 | sum2
------+------
400 | 2100
600 | 4800
700 | 1400
800 | 1100
900 | 1700
1300 | 1600
| 900
(7 rows)
-- Testing ORDER BY, DISTINCT, FILTER and Ordered-sets within aggregates
-- ORDER BY within aggregates (same column used to order) are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: (sum(c1 ORDER BY c1)), c2
Sort Key: (sum(fdw137_t1.c1 ORDER BY fdw137_t1.c1))
-> GroupAggregate
Output: sum(c1 ORDER BY c1), c2
Group Key: fdw137_t1.c2
-> Sort
Output: c2, c1
Sort Key: fdw137_t1.c2
-> Foreign Scan on public.fdw137_t1
Output: c2, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
sum
-----
100
200
300
400
(4 rows)
-- ORDER BY within aggregate (different column used to order also using DESC)
-- are not pushed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: sum(c8 ORDER BY c1 DESC)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
sum
-----
90
(1 row)
-- DISTINCT within aggregate. Don't push down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: sum(DISTINCT c1)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
sum
-----
500
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(DISTINCT t1.c1)), t2.c1
Sort Key: (sum(DISTINCT t1.c1))
-> GroupAggregate
Output: sum(DISTINCT t1.c1), t2.c1
Group Key: t2.c1
-> Sort
Output: t2.c1, t1.c1
Sort Key: t2.c1
-> Foreign Scan
Output: t2.c1, t1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(12 rows)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
sum
------
3000
3700
(2 rows)
-- DISTINCT, ORDER BY and FILTER within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
QUERY PLAN
-----------------------------------------------------------------------------------
GroupAggregate
Output: sum(c1), sum(DISTINCT c1 ORDER BY c1) FILTER (WHERE ((c1 % 3) < 2)), c4
Group Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
sum | sum | c4
------+------+-----
4800 | 4100 | 600
(1 row)
-- FILTER within aggregate, not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Sort
Output: (sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500)))), c4
Sort Key: (sum(fdw137_t1.c1) FILTER (WHERE ((fdw137_t1.c1 < 1000) AND (fdw137_t1.c4 > 500))))
-> HashAggregate
Output: sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500))), c4
Group Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
sum
------
100
1000
1700
(7 rows)
-- Outer query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Aggregate
Output: (SubPlan 1)
-> Foreign Scan on public.fdw137_t2 t2
Output: t2._id, t2.c1, t2.c2, t2.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Foreign Scan on public.fdw137_t1 t1
Output: count(*) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
1
(1 row)
-- Inner query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Foreign Scan on public.fdw137_t2 t2
Output: (SubPlan 1)
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Aggregate
Output: count(t1.c1) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
0
10
(2 rows)
-- Ordered-sets within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: c8, rank('10'::bpchar) WITHIN GROUP (ORDER BY c3), percentile_cont((((c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))
Group Key: fdw137_t1.c8
Filter: (percentile_cont((((fdw137_t1.c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((fdw137_t1.c1)::double precision)) < '500'::double precision)
-> Sort
Output: c8, c3, c1
Sort Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: c8, c3, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
c8 | rank | percentile_cont
----+------+-----------------
20 | 1 | 220
30 | 1 | 275
(2 rows)
-- Subquery in FROM clause HAVING aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: (count(*)), x.b
Sort Key: (count(*)), x.b
-> HashAggregate
Output: count(*), x.b
Group Key: x.b
-> Hash Join
Output: x.b
Inner Unique: true
Hash Cond: (fdw137_t1.c8 = x.a)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: x.b, x.a
-> Subquery Scan on x
Output: x.b, x.a
-> Foreign Scan
Output: fdw137_t2.c1, (sum(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(20 rows)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
count | b
-------+----
3 | 10
5 | 20
6 | 30
(3 rows)
-- Join with IS NULL check in HAVING
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Sort Key: (avg(t1.c1)), (sum(t2.c1))
-> Foreign Scan
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Filter: ((avg(t1.c1)) IS NULL)
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(7 rows)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
avg | sum
-----+-----
(0 rows)
-- ORDER BY expression is part of the target list but not pushed down to
-- foreign server.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------
Sort
Output: (((sum(c1)) * ((random() <= '1'::double precision))::integer))
Sort Key: (((sum(fdw137_t1.c1)) * ((random() <= '1'::double precision))::integer))
-> Foreign Scan
Output: ((sum(c1)) * ((random() <= '1'::double precision))::integer)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
sum
-------
13600
(1 row)
-- LATERAL join, with parameterization
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 t2 GROUP BY t2.c1) qry WHERE t1.c8 * 2 = qry.sum ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------------
Sort
Output: t1.c8, qry.sum
Sort Key: t1.c8
-> Hash Join
Output: t1.c8, qry.sum
Hash Cond: ((t1.c8 * 2) = qry.sum)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: qry.sum
-> Subquery Scan on qry
Output: qry.sum
-> Foreign Scan
Output: (sum(t2.c1)), t2.c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 t2)
(16 rows)
-- Check with placeHolderVars
EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: q.b, (count(fdw137_t1.c1)), (sum(q.a))
Sort Key: q.b, (count(fdw137_t1.c1))
-> GroupAggregate
Output: q.b, count(fdw137_t1.c1), sum(q.a)
Group Key: q.b
-> Sort
Output: q.b, fdw137_t1.c1, q.a
Sort Key: q.b
-> Hash Left Join
Output: q.b, fdw137_t1.c1, q.a
Inner Unique: true
Hash Cond: ((fdw137_t1.c8)::numeric = q.b)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: q.b, q.a
-> Subquery Scan on q
Output: q.b, q.a
-> Aggregate
Output: min(13), avg(fdw137_t1_1.c1), NULL::bigint
-> Foreign Scan
Output: fdw137_t1_1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)
(25 rows)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
b | count | sum
---+-------+-----
| 5 |
(1 row)
-- Not supported cases
-- The COUNT of column
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c8) FROM fdw137_t1 ;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: count(c8)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT count(c8) FROM fdw137_t1 ;
count
-------
15
(1 row)
-- Grouping sets
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
c8 | sum
----+------
20 | 3700
30 | 3800
60 | 1500
| 9000
(4 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
c8 | sum
----+-------
10 | 3000
20 | 3700
30 | 3800
60 | 1500
| 12000
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, c4, (sum(c1))
Sort Key: fdw137_t1.c8, fdw137_t1.c4
-> HashAggregate
Output: c8, c4, sum(c1)
Hash Key: fdw137_t1.c8
Hash Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
c8 | c4 | sum
----+------+------
30 | | 3800
60 | | 1500
| 600 | 3200
| 900 | 600
| 1300 | 1500
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1)), (GROUPING(c8))
Sort Key: fdw137_t1.c8
-> HashAggregate
Output: c8, sum(c1), GROUPING(c8)
Group Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
c8 | sum | grouping
----+------+----------
20 | 3700 | 0
30 | 3800 | 0
60 | 1500 | 0
(3 rows)
-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: (sum(c1)), c1
-> Sort
Output: (sum(c1)), c1
Sort Key: (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
s
------
1100
1200
1300
1400
1500
1600
(6 rows)
-- WindowAgg
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (sum(c8)), (count(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, (sum(c8)), count(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2)), (sum(c8))
Sort Key: ((fdw137_t1.c8 % 2))
-> Foreign Scan
Output: c8, (c8 % 2), (sum(c8))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | sum | count
----+-----+-------
20 | 100 | 3
30 | 180 | 3
60 | 60 | 3
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8 DESC
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {60,30,20}
30 | {60,30}
60 | {60}
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {20,30,60}
30 | {30,60}
60 | {60}
(3 rows)
-- User defined function for user defined aggregate, VARIADIC
CREATE FUNCTION least_accum(anyelement, variadic anyarray)
returns anyelement language sql AS
'SELECT least($1, min($2[i])) FROM generate_subscripts($2,2) g(i)';
CREATE aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
-- Not pushed down due to user defined aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (least_agg(VARIADIC ARRAY[c1]))
Sort Key: fdw137_t1.c2
-> HashAggregate
Output: c2, least_agg(VARIADIC ARRAY[c1])
Group Key: fdw137_t1.c2
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
c2 | least_agg
-------+-----------
EMP1 |
EMP10 |
EMP11 |
EMP12 |
EMP13 |
EMP14 |
EMP15 |
EMP16 |
EMP2 |
EMP3 |
EMP4 |
EMP5 |
EMP6 |
EMP7 |
EMP8 |
EMP9 |
(16 rows)
-- Test partition-wise aggregate
SET enable_partitionwise_aggregate TO ON;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
-- Plan with partitionwise aggregates is enabled
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
QUERY PLAN
-------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c1))
Sort Key: (sum(fprt1.c1))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
c1 | sum
----+-----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Sort Key: (sum(fprt1.c2))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c2)), (min(fprt1_1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
c1 | sum | min | count
----+-----+-----+-------
1 | 1 | 1 | 1
2 | 2 | 2 | 1
3 | 3 | 3 | 1
4 | 4 | 4 | 1
5 | 5 | 5 | 1
6 | 6 | 6 | 1
7 | 7 | 7 | 1
8 | 8 | 8 | 1
(8 rows)
-- Check with whole-row reference
-- Should have all the columns in the target list for the given relation
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------
Sort
Output: t1.c1, (count(((t1.*)::fprt1)))
Sort Key: t1.c1
-> Append
-> HashAggregate
Output: t1.c1, count(((t1.*)::fprt1))
Group Key: t1.c1
Filter: (avg(t1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p1 t1
Output: t1.c1, t1.*, t1.c2
Foreign Namespace: mongo_fdw_regress.test1
-> HashAggregate
Output: t1_1.c1, count(((t1_1.*)::fprt1))
Group Key: t1_1.c1
Filter: (avg(t1_1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p2 t1_1
Output: t1_1.c1, t1_1.*, t1_1.c2
Foreign Namespace: mongo_fdw_regress.test2
(18 rows)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
c1 | count
----+-------
1 | 1
2 | 1
3 | 1
4 | 1
5 | 1
6 | 1
7 | 1
8 | 1
(8 rows)
SET enable_partitionwise_aggregate TO OFF;
-- Support enable_aggregate_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'non-bolean');
ERROR: enable_aggregate_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test the option at table level. Setting option at table level does not
-- affect the setting at server level.
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test option for aggregation over join. Allow aggregation only if enabled for
-- both the relations involved in the join.
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
-- FDW-560: Aggregation over nested join. As nested join push down is not
-- supported, aggregation shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t3)
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
sum | c8
-----+----
30 | 10
100 | 20
180 | 30
| 60
|
(5 rows)
-- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON.
-- Shouldn't push down join as well as aggregation.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan on public.fdw137_t1 t1
Output: t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Sort Key: f_test_large.a01 NULLS FIRST, f_test_large.a02 NULLS FIRST, f_test_large.a03 NULLS FIRST, f_test_large.a04 NULLS FIRST, f_test_large.a05 NULLS FIRST, f_test_large.a06 NULLS FIRST, f_test_large.a07 NULLS FIRST, f_test_large.a08 NULLS FIRST, f_test_large.a09 NULLS FIRST, f_test_large.a10 NULLS FIRST, f_test_large.a11 NULLS FIRST, f_test_large.a12 NULLS FIRST, f_test_large.a13 NULLS FIRST, f_test_large.a14 NULLS FIRST, f_test_large.a15 NULLS FIRST, f_test_large.a16 NULLS FIRST, f_test_large.a17 NULLS FIRST, f_test_large.a18 NULLS FIRST, f_test_large.a19 NULLS FIRST, f_test_large.a20 NULLS FIRST, f_test_large.a21 NULLS FIRST, f_test_large.a22 NULLS FIRST, f_test_large.a23 NULLS FIRST, f_test_large.a24 NULLS FIRST, f_test_large.a25 NULLS FIRST, f_test_large.a26 NULLS FIRST, f_test_large.a27 NULLS FIRST, f_test_large.a28 NULLS FIRST, f_test_large.a29 NULLS FIRST, f_test_large.a30 NULLS FIRST, f_test_large.a31 NULLS FIRST, f_test_large.a32 NULLS FIRST, f_test_large.a33 NULLS FIRST, f_test_large.a34 DESC NULLS LAST, f_test_large.a35 NULLS FIRST
-> Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(6 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 32
32 | 32
32 | 32
132 | 132
(5 rows)
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(3 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 96
132 | 132
(3 rows)
-- FDW-131: Limit and offset pushdown with Aggregate pushdown.
SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1;
avg | c1
-----+----
10 | 10
20 | 20
30 | 30
40 | 40
50 | 50
|
(6 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
sum | c1
-----+----
10 | 10
(1 row)
-- Limit 0, Offset 0 with aggregates.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
sum | c1
-----+----
(0 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (avg(c1))
-> Foreign Scan
Output: c1, (avg(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
-> Foreign Scan
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(9 rows)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
ERROR: LIMIT must not be negative
-- FDW-559: Test mongo_fdw.enable_aggregate_pushdown GUC.
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_aggregate_pushdown;
mongo_fdw.enable_aggregate_pushdown
-------------------------------------
on
(1 row)
-- Negative testing for GUC value.
SET mongo_fdw.enable_aggregate_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_aggregate_pushdown" requires a Boolean value
--Disable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Shouldn't pushdown aggregate because GUC is OFF.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
--Enable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Should pushdown aggregate because GUC is ON.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
-- Test for aggregation over join when server and table options for both the
-- tables is true and guc is enabled. Should pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
SET mongo_fdw.enable_join_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (count(*)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
--Disable the GUC enable_join_pushdown. Shouldn't pushdown aggregate.
SET mongo_fdw.enable_join_pushdown to off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8
Merge Cond: (t1.c8 = t2.c1)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1 NULLS FIRST
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(15 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
SET mongo_fdw.enable_join_pushdown to on;
--Disable the GUC enable_aggregate_pushdown. Shouldn't pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(6 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_aggregate_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- When option enable_aggregate_pushdown is disabled. Shouldn't pushdown
-- aggregate as well as ORDER BY too.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> HashAggregate
Output: c2, sum(c1), c1
Group Key: fdw137_t1.c2, fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Cleanup
DELETE FROM fdw137_t1 WHERE c8 IS NULL;
DELETE FROM fdw137_t1 WHERE c8 = 60;
DELETE FROM fdw137_t2 WHERE c1 IS NULL;
DELETE FROM fdw137_t2 WHERE c1 = 50;
DROP FOREIGN TABLE fdw137_t1;
DROP FOREIGN TABLE fdw137_t2;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE f_test_large;
DROP TABLE fprt1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/aggregate_pushdown_1.out 0000664 0000000 0000000 00000246711 15066665201 0022736 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables.
CREATE FOREIGN TABLE fdw137_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE fdw137_t2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
INSERT INTO fdw137_t1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO fdw137_t1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO fdw137_t2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO fdw137_t2 VALUES (0);
-- Create local table.
CREATE TABLE fdw137_local AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1;
-- Simple aggregates. ORDER BY push-down not possible because only column names allowed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Result
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), ((sum(c1)) * ((random() <= '1'::double precision))::integer), c4
-> Sort
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Sort Key: (count(*)) NULLS FIRST, (sum(fdw137_t1.c1)) NULLS FIRST
-> Foreign Scan
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
count | sum | avg | min | max | sum2
-------+------+------------------+------+------+------
1 | 1100 | 1100 | 800 | 1100 | 1100
1 | 1400 | 1400 | 700 | 1400 | 1400
2 | 1600 | 800 | 1300 | 1500 | 1600
3 | 1700 | 566.666666666667 | 900 | 700 | 1700
(4 rows)
-- GROUP BY clause HAVING expressions
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c1, (sum(c1)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum | count
------+------+-------
600 | 600 | 1
700 | 700 | 1
800 | 800 | 1
900 | 900 | 1
1000 | 1000 | 1
1100 | 1100 | 1
1200 | 1200 | 1
1300 | 1300 | 1
1400 | 1400 | 1
1500 | 1500 | 1
1600 | 1600 | 1
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c8, (min(c2))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
c8 | min
----+------
20 | EMP1
(1 row)
-- Multi-column GROUP BY clause. Push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregation on expression. Don't push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
GroupAggregate
Output: c1, sum((c1 + 2))
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum
------+------
600 | 602
700 | 702
800 | 802
900 | 902
1000 | 1002
1100 | 1102
1200 | 1202
1300 | 1302
1400 | 1402
1500 | 1502
1600 | 1602
(11 rows)
-- Aggregate with unshippable GROUP BY clause are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer))
Sort Key: (avg(fdw137_t1.c4))
-> HashAggregate
Output: avg(c4), ((c4 * ((random() <= '1'::double precision))::integer))
Group Key: (fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)
-> Foreign Scan on public.fdw137_t1
Output: (c4 * ((random() <= '1'::double precision))::integer), c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
avg
-----------------------
400.0000000000000000
600.0000000000000000
700.0000000000000000
800.0000000000000000
900.0000000000000000
1300.0000000000000000
(7 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c1, (sum(c1))
Sort Key: fdw137_t1.c1
-> HashAggregate
Output: c1, sum(c1)
Group Key: fdw137_t1.c1
Filter: (min((fdw137_t1.c1 * 3)) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
c1 | sum
------+------
200 | 200
300 | 300
400 | 400
500 | 500
600 | 600
700 | 700
800 | 800
900 | 900
1000 | 1000
1100 | 1100
1200 | 1200
1300 | 1300
1400 | 1400
1500 | 1500
1600 | 1600
(15 rows)
-- FDW-134: Test ORDER BY with COLLATE. Shouldn't push-down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), ((c2)::text), c1
Sort Key: fdw137_t1.c2 COLLATE "en_US" NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c2, c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- Using expressions in HAVING clause. Pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c3, (count(*))
Sort Key: fdw137_t1.c3, (count(*))
-> Foreign Scan
Output: c3, (count(*))
Filter: (abs((max(fdw137_t1.c8))) = 10)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(7 rows)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
c3 | count
-----------+-------
HEAD | 1
(1 row)
-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Aggregate
Output: count(*)
-> Foreign Scan
Output: fdw137_t1.c3, NULL::bigint
Filter: (((((avg(fdw137_t1.c1)) / (avg(fdw137_t1.c1))))::double precision * random()) <= '1'::double precision)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
count
-------
0
(1 row)
-- Aggregate over join query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8)), (avg(t2.c1))
Sort Key: (sum(t1.c8)) DESC NULLS LAST
-> Foreign Scan
Output: (sum(t1.c8)), (avg(t2.c1))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
sum | avg
-----+------------------
310 | 22.1428571428571
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, (count(*)), t2.c4
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2))
(3 rows)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | count | c4
----+-------+------
10 | 1 |
10 | 1 | 700
10 | 1 | 900
20 | 2 | 400
20 | 1 | 800
20 | 1 | 900
20 | 1 | 1300
30 | 5 | 600
30 | 1 | 900
(9 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregate is not pushed down as aggregation contains random()
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------
Sort
Output: (sum((c1 * ((random() <= '1'::double precision))::integer))), (avg(c1))
Sort Key: (sum((fdw137_t1.c1 * ((random() <= '1'::double precision))::integer)))
-> Aggregate
Output: sum((c1 * ((random() <= '1'::double precision))::integer)), avg(c1)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
sum | avg
-------+----------------------
13600 | 850.0000000000000000
(1 row)
-- Not pushed down due to local conditions present in underneath input rel
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8))
Sort Key: (sum(t1.c8))
-> Aggregate
Output: sum(t1.c8)
-> Foreign Scan
Output: t1.c8
Filter: (((((t1.c8 * t2.c1) / (t1.c8 * t2.c1)))::double precision * random()) <= '1'::double precision)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
sum
-----
310
(1 row)
-- Aggregates in subquery are pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
QUERY PLAN
---------------------------------------------------------------------------------------
Aggregate
Output: count(fdw137_t1.c8), sum(fdw137_t1.c8)
-> Sort
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Sort Key: fdw137_t1.c8, (sum(fdw137_t1.c1))
-> Foreign Scan
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
count | sum
-------+-----
4 | 120
(1 row)
-- Aggregate is still pushed down by taking unshippable expression out
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Sort
Output: ((c4 * ((random() <= '1'::double precision))::integer)), (sum(c1)), c4
Sort Key: ((fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)), (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (c4 * ((random() <= '1'::double precision))::integer), (sum(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
sum1 | sum2
------+------
400 | 2100
600 | 4800
700 | 1400
800 | 1100
900 | 1700
1300 | 1600
| 900
(7 rows)
-- Testing ORDER BY, DISTINCT, FILTER and Ordered-sets within aggregates
-- ORDER BY within aggregates (same column used to order) are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: (sum(c1 ORDER BY c1)), c2
Sort Key: (sum(fdw137_t1.c1 ORDER BY fdw137_t1.c1))
-> GroupAggregate
Output: sum(c1 ORDER BY c1), c2
Group Key: fdw137_t1.c2
-> Sort
Output: c2, c1
Sort Key: fdw137_t1.c2, fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c2, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
sum
-----
100
200
300
400
(4 rows)
-- ORDER BY within aggregate (different column used to order also using DESC)
-- are not pushed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(c8 ORDER BY c1 DESC)
-> Sort
Output: c8, c1
Sort Key: fdw137_t1.c1 DESC
-> Foreign Scan on public.fdw137_t1
Output: c8, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
sum
-----
90
(1 row)
-- DISTINCT within aggregate. Don't push down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(DISTINCT c1)
-> Sort
Output: c1
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
sum
-----
500
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(DISTINCT t1.c1)), t2.c1
Sort Key: (sum(DISTINCT t1.c1))
-> GroupAggregate
Output: sum(DISTINCT t1.c1), t2.c1
Group Key: t2.c1
-> Sort
Output: t2.c1, t1.c1
Sort Key: t2.c1, t1.c1
-> Foreign Scan
Output: t2.c1, t1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(12 rows)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
sum
------
3000
3700
(2 rows)
-- DISTINCT, ORDER BY and FILTER within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
QUERY PLAN
-----------------------------------------------------------------------------------
GroupAggregate
Output: sum(c1), sum(DISTINCT c1 ORDER BY c1) FILTER (WHERE ((c1 % 3) < 2)), c4
-> Sort
Output: c1, c4
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
sum | sum | c4
------+------+-----
4800 | 4100 | 600
(1 row)
-- FILTER within aggregate, not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Sort
Output: (sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500)))), c4
Sort Key: (sum(fdw137_t1.c1) FILTER (WHERE ((fdw137_t1.c1 < 1000) AND (fdw137_t1.c4 > 500))))
-> HashAggregate
Output: sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500))), c4
Group Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
sum
------
100
1000
1700
(7 rows)
-- Outer query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Aggregate
Output: (SubPlan 1)
-> Foreign Scan on public.fdw137_t2 t2
Output: t2._id, t2.c1, t2.c2, t2.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Foreign Scan on public.fdw137_t1 t1
Output: count(*) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
1
(1 row)
-- Inner query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Foreign Scan on public.fdw137_t2 t2
Output: (SubPlan 1)
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Aggregate
Output: count(t1.c1) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
0
10
(2 rows)
-- Ordered-sets within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: c8, rank('10'::bpchar) WITHIN GROUP (ORDER BY c3), percentile_cont((((c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))
Group Key: fdw137_t1.c8
Filter: (percentile_cont((((fdw137_t1.c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((fdw137_t1.c1)::double precision)) < '500'::double precision)
-> Sort
Output: c8, c3, c1
Sort Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: c8, c3, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
c8 | rank | percentile_cont
----+------+-----------------
20 | 1 | 220
30 | 1 | 275
(2 rows)
-- Subquery in FROM clause HAVING aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: (count(*)), x.b
Sort Key: (count(*)), x.b
-> HashAggregate
Output: count(*), x.b
Group Key: x.b
-> Hash Join
Output: x.b
Inner Unique: true
Hash Cond: (fdw137_t1.c8 = x.a)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: x.b, x.a
-> Subquery Scan on x
Output: x.b, x.a
-> Foreign Scan
Output: fdw137_t2.c1, (sum(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(20 rows)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
count | b
-------+----
3 | 10
5 | 20
6 | 30
(3 rows)
-- Join with IS NULL check in HAVING
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Sort Key: (avg(t1.c1)), (sum(t2.c1))
-> Foreign Scan
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Filter: ((avg(t1.c1)) IS NULL)
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(7 rows)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
avg | sum
-----+-----
(0 rows)
-- ORDER BY expression is part of the target list but not pushed down to
-- foreign server.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------
Sort
Output: (((sum(c1)) * ((random() <= '1'::double precision))::integer))
Sort Key: (((sum(fdw137_t1.c1)) * ((random() <= '1'::double precision))::integer))
-> Foreign Scan
Output: ((sum(c1)) * ((random() <= '1'::double precision))::integer)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
sum
-------
13600
(1 row)
-- LATERAL join, with parameterization
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 t2 GROUP BY t2.c1) qry WHERE t1.c8 * 2 = qry.sum ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------------
Sort
Output: t1.c8, qry.sum
Sort Key: t1.c8
-> Hash Join
Output: t1.c8, qry.sum
Hash Cond: ((t1.c8 * 2) = qry.sum)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: qry.sum
-> Subquery Scan on qry
Output: qry.sum
-> Foreign Scan
Output: (sum(t2.c1)), t2.c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 t2)
(16 rows)
-- Check with placeHolderVars
EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Incremental Sort
Output: q.b, (count(fdw137_t1.c1)), (sum(q.a))
Sort Key: q.b, (count(fdw137_t1.c1))
Presorted Key: q.b
-> GroupAggregate
Output: q.b, count(fdw137_t1.c1), sum(q.a)
Group Key: q.b
-> Sort
Output: q.b, fdw137_t1.c1, q.a
Sort Key: q.b
-> Hash Left Join
Output: q.b, fdw137_t1.c1, q.a
Inner Unique: true
Hash Cond: ((fdw137_t1.c8)::numeric = q.b)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: q.b, q.a
-> Subquery Scan on q
Output: q.b, q.a
-> Aggregate
Output: min(13), avg(fdw137_t1_1.c1), NULL::bigint
-> Foreign Scan
Output: fdw137_t1_1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)
(26 rows)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
b | count | sum
---+-------+-----
| 5 |
(1 row)
-- Not supported cases
-- The COUNT of column
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c8) FROM fdw137_t1 ;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: count(c8)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT count(c8) FROM fdw137_t1 ;
count
-------
15
(1 row)
-- Grouping sets
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
c8 | sum
----+------
20 | 3700
30 | 3800
60 | 1500
| 9000
(4 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
c8 | sum
----+-------
10 | 3000
20 | 3700
30 | 3800
60 | 1500
| 12000
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, c4, (sum(c1))
Sort Key: fdw137_t1.c8, fdw137_t1.c4
-> HashAggregate
Output: c8, c4, sum(c1)
Hash Key: fdw137_t1.c8
Hash Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
c8 | c4 | sum
----+------+------
30 | | 3800
60 | | 1500
| 600 | 3200
| 900 | 600
| 1300 | 1500
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1)), (GROUPING(c8))
Sort Key: fdw137_t1.c8
-> HashAggregate
Output: c8, sum(c1), GROUPING(c8)
Group Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
c8 | sum | grouping
----+------+----------
20 | 3700 | 0
30 | 3800 | 0
60 | 1500 | 0
(3 rows)
-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: (sum(c1)), c1
-> Sort
Output: (sum(c1)), c1
Sort Key: (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
s
------
1100
1200
1300
1400
1500
1600
(6 rows)
-- WindowAgg
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (sum(c8)), (count(c8) OVER w1), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, (sum(c8)), count(c8) OVER w1, ((c8 % 2))
Window: w1 AS (PARTITION BY ((fdw137_t1.c8 % 2)))
-> Sort
Output: c8, ((c8 % 2)), (sum(c8))
Sort Key: ((fdw137_t1.c8 % 2))
-> Foreign Scan
Output: c8, (c8 % 2), (sum(c8))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(12 rows)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | sum | count
----+-----+-------
20 | 100 | 3
30 | 180 | 3
60 | 60 | 3
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER w1), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER w1, ((c8 % 2))
Window: w1 AS (PARTITION BY ((fdw137_t1.c8 % 2)) ORDER BY fdw137_t1.c8)
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8 DESC
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(12 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {60,30,20}
30 | {60,30}
60 | {60}
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER w1), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER w1, ((c8 % 2))
Window: w1 AS (PARTITION BY ((fdw137_t1.c8 % 2)) ORDER BY fdw137_t1.c8 RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(12 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {20,30,60}
30 | {30,60}
60 | {60}
(3 rows)
-- User defined function for user defined aggregate, VARIADIC
CREATE FUNCTION least_accum(anyelement, variadic anyarray)
returns anyelement language sql AS
'SELECT least($1, min($2[i])) FROM generate_subscripts($2,2) g(i)';
CREATE aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
-- Not pushed down due to user defined aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (least_agg(VARIADIC ARRAY[c1]))
Sort Key: fdw137_t1.c2
-> HashAggregate
Output: c2, least_agg(VARIADIC ARRAY[c1])
Group Key: fdw137_t1.c2
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
c2 | least_agg
-------+-----------
EMP1 |
EMP10 |
EMP11 |
EMP12 |
EMP13 |
EMP14 |
EMP15 |
EMP16 |
EMP2 |
EMP3 |
EMP4 |
EMP5 |
EMP6 |
EMP7 |
EMP8 |
EMP9 |
(16 rows)
-- Test partition-wise aggregate
SET enable_partitionwise_aggregate TO ON;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
-- Plan with partitionwise aggregates is enabled
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
QUERY PLAN
-------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c1))
Sort Key: (sum(fprt1.c1))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
c1 | sum
----+-----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Sort Key: (sum(fprt1.c2))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c2)), (min(fprt1_1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
c1 | sum | min | count
----+-----+-----+-------
1 | 1 | 1 | 1
2 | 2 | 2 | 1
3 | 3 | 3 | 1
4 | 4 | 4 | 1
5 | 5 | 5 | 1
6 | 6 | 6 | 1
7 | 7 | 7 | 1
8 | 8 | 8 | 1
(8 rows)
-- Check with whole-row reference
-- Should have all the columns in the target list for the given relation
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------
Sort
Output: t1.c1, (count(((t1.*)::fprt1)))
Sort Key: t1.c1
-> Append
-> HashAggregate
Output: t1.c1, count(((t1.*)::fprt1))
Group Key: t1.c1
Filter: (avg(t1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p1 t1
Output: t1.c1, t1.*, t1.c2
Foreign Namespace: mongo_fdw_regress.test1
-> HashAggregate
Output: t1_1.c1, count(((t1_1.*)::fprt1))
Group Key: t1_1.c1
Filter: (avg(t1_1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p2 t1_1
Output: t1_1.c1, t1_1.*, t1_1.c2
Foreign Namespace: mongo_fdw_regress.test2
(18 rows)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
c1 | count
----+-------
1 | 1
2 | 1
3 | 1
4 | 1
5 | 1
6 | 1
7 | 1
8 | 1
(8 rows)
SET enable_partitionwise_aggregate TO OFF;
-- Support enable_aggregate_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'non-bolean');
ERROR: enable_aggregate_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test the option at table level. Setting option at table level does not
-- affect the setting at server level.
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test option for aggregation over join. Allow aggregation only if enabled for
-- both the relations involved in the join.
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
-- FDW-560: Aggregation over nested join. As nested join push down is not
-- supported, aggregation shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t3)
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
sum | c8
-----+----
30 | 10
100 | 20
180 | 30
| 60
|
(5 rows)
-- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON.
-- Shouldn't push down join as well as aggregation.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan on public.fdw137_t1 t1
Output: t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Sort Key: f_test_large.a01 NULLS FIRST, f_test_large.a02 NULLS FIRST, f_test_large.a03 NULLS FIRST, f_test_large.a04 NULLS FIRST, f_test_large.a05 NULLS FIRST, f_test_large.a06 NULLS FIRST, f_test_large.a07 NULLS FIRST, f_test_large.a08 NULLS FIRST, f_test_large.a09 NULLS FIRST, f_test_large.a10 NULLS FIRST, f_test_large.a11 NULLS FIRST, f_test_large.a12 NULLS FIRST, f_test_large.a13 NULLS FIRST, f_test_large.a14 NULLS FIRST, f_test_large.a15 NULLS FIRST, f_test_large.a16 NULLS FIRST, f_test_large.a17 NULLS FIRST, f_test_large.a18 NULLS FIRST, f_test_large.a19 NULLS FIRST, f_test_large.a20 NULLS FIRST, f_test_large.a21 NULLS FIRST, f_test_large.a22 NULLS FIRST, f_test_large.a23 NULLS FIRST, f_test_large.a24 NULLS FIRST, f_test_large.a25 NULLS FIRST, f_test_large.a26 NULLS FIRST, f_test_large.a27 NULLS FIRST, f_test_large.a28 NULLS FIRST, f_test_large.a29 NULLS FIRST, f_test_large.a30 NULLS FIRST, f_test_large.a31 NULLS FIRST, f_test_large.a32 NULLS FIRST, f_test_large.a33 NULLS FIRST, f_test_large.a34 DESC NULLS LAST, f_test_large.a35 NULLS FIRST
-> Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(6 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 32
32 | 32
32 | 32
132 | 132
(5 rows)
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(3 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 96
132 | 132
(3 rows)
-- FDW-131: Limit and offset pushdown with Aggregate pushdown.
SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1;
avg | c1
-----+----
10 | 10
20 | 20
30 | 30
40 | 40
50 | 50
|
(6 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
sum | c1
-----+----
10 | 10
(1 row)
-- Limit 0, Offset 0 with aggregates.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
sum | c1
-----+----
(0 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (avg(c1))
-> Foreign Scan
Output: c1, (avg(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
-> Foreign Scan
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(9 rows)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
ERROR: LIMIT must not be negative
-- FDW-559: Test mongo_fdw.enable_aggregate_pushdown GUC.
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_aggregate_pushdown;
mongo_fdw.enable_aggregate_pushdown
-------------------------------------
on
(1 row)
-- Negative testing for GUC value.
SET mongo_fdw.enable_aggregate_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_aggregate_pushdown" requires a Boolean value
--Disable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Shouldn't pushdown aggregate because GUC is OFF.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
--Enable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Should pushdown aggregate because GUC is ON.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
-- Test for aggregation over join when server and table options for both the
-- tables is true and guc is enabled. Should pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
SET mongo_fdw.enable_join_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (count(*)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
--Disable the GUC enable_join_pushdown. Shouldn't pushdown aggregate.
SET mongo_fdw.enable_join_pushdown to off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8
Merge Cond: (t1.c8 = t2.c1)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1 NULLS FIRST
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(15 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
SET mongo_fdw.enable_join_pushdown to on;
--Disable the GUC enable_aggregate_pushdown. Shouldn't pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(6 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_aggregate_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- When option enable_aggregate_pushdown is disabled. Shouldn't pushdown
-- aggregate as well as ORDER BY too.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> HashAggregate
Output: c2, sum(c1), c1
Group Key: fdw137_t1.c2, fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Cleanup
DELETE FROM fdw137_t1 WHERE c8 IS NULL;
DELETE FROM fdw137_t1 WHERE c8 = 60;
DELETE FROM fdw137_t2 WHERE c1 IS NULL;
DELETE FROM fdw137_t2 WHERE c1 = 50;
DROP FOREIGN TABLE fdw137_t1;
DROP FOREIGN TABLE fdw137_t2;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE f_test_large;
DROP TABLE fprt1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/aggregate_pushdown_2.out 0000664 0000000 0000000 00000246164 15066665201 0022741 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables.
CREATE FOREIGN TABLE fdw137_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE fdw137_t2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
INSERT INTO fdw137_t1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO fdw137_t1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO fdw137_t2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO fdw137_t2 VALUES (0);
-- Create local table.
CREATE TABLE fdw137_local AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1;
-- Simple aggregates. ORDER BY push-down not possible because only column names allowed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Result
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), ((sum(c1)) * ((random() <= '1'::double precision))::integer), c4
-> Sort
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Sort Key: (count(*)) NULLS FIRST, (sum(fdw137_t1.c1)) NULLS FIRST
-> Foreign Scan
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
count | sum | avg | min | max | sum2
-------+------+------------------+------+------+------
1 | 1100 | 1100 | 800 | 1100 | 1100
1 | 1400 | 1400 | 700 | 1400 | 1400
2 | 1600 | 800 | 1300 | 1500 | 1600
3 | 1700 | 566.666666666667 | 900 | 700 | 1700
(4 rows)
-- GROUP BY clause HAVING expressions
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c1, (sum(c1)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum | count
------+------+-------
600 | 600 | 1
700 | 700 | 1
800 | 800 | 1
900 | 900 | 1
1000 | 1000 | 1
1100 | 1100 | 1
1200 | 1200 | 1
1300 | 1300 | 1
1400 | 1400 | 1
1500 | 1500 | 1
1600 | 1600 | 1
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c8, (min(c2))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
c8 | min
----+------
20 | EMP1
(1 row)
-- Multi-column GROUP BY clause. Push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregation on expression. Don't push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
GroupAggregate
Output: c1, sum((c1 + 2))
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum
------+------
600 | 602
700 | 702
800 | 802
900 | 902
1000 | 1002
1100 | 1102
1200 | 1202
1300 | 1302
1400 | 1402
1500 | 1502
1600 | 1602
(11 rows)
-- Aggregate with unshippable GROUP BY clause are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer))
Sort Key: (avg(fdw137_t1.c4))
-> HashAggregate
Output: avg(c4), ((c4 * ((random() <= '1'::double precision))::integer))
Group Key: (fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)
-> Foreign Scan on public.fdw137_t1
Output: (c4 * ((random() <= '1'::double precision))::integer), c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
avg
-----------------------
400.0000000000000000
600.0000000000000000
700.0000000000000000
800.0000000000000000
900.0000000000000000
1300.0000000000000000
(7 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c1, (sum(c1))
Sort Key: fdw137_t1.c1
-> HashAggregate
Output: c1, sum(c1)
Group Key: fdw137_t1.c1
Filter: (min((fdw137_t1.c1 * 3)) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
c1 | sum
------+------
200 | 200
300 | 300
400 | 400
500 | 500
600 | 600
700 | 700
800 | 800
900 | 900
1000 | 1000
1100 | 1100
1200 | 1200
1300 | 1300
1400 | 1400
1500 | 1500
1600 | 1600
(15 rows)
-- FDW-134: Test ORDER BY with COLLATE. Shouldn't push-down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), ((c2)::text), c1
Sort Key: fdw137_t1.c2 COLLATE "en_US" NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c2, c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- Using expressions in HAVING clause. Pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c3, (count(*))
Sort Key: fdw137_t1.c3, (count(*))
-> Foreign Scan
Output: c3, (count(*))
Filter: (abs((max(fdw137_t1.c8))) = 10)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(7 rows)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
c3 | count
-----------+-------
HEAD | 1
(1 row)
-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Aggregate
Output: count(*)
-> Foreign Scan
Output: fdw137_t1.c3, NULL::bigint
Filter: (((((avg(fdw137_t1.c1)) / (avg(fdw137_t1.c1))))::double precision * random()) <= '1'::double precision)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
count
-------
0
(1 row)
-- Aggregate over join query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8)), (avg(t2.c1))
Sort Key: (sum(t1.c8)) DESC NULLS LAST
-> Foreign Scan
Output: (sum(t1.c8)), (avg(t2.c1))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
sum | avg
-----+------------------
310 | 22.1428571428571
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, (count(*)), t2.c4
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2))
(3 rows)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | count | c4
----+-------+------
10 | 1 |
10 | 1 | 700
10 | 1 | 900
20 | 2 | 400
20 | 1 | 800
20 | 1 | 900
20 | 1 | 1300
30 | 5 | 600
30 | 1 | 900
(9 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregate is not pushed down as aggregation contains random()
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------
Sort
Output: (sum((c1 * ((random() <= '1'::double precision))::integer))), (avg(c1))
Sort Key: (sum((fdw137_t1.c1 * ((random() <= '1'::double precision))::integer)))
-> Aggregate
Output: sum((c1 * ((random() <= '1'::double precision))::integer)), avg(c1)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
sum | avg
-------+----------------------
13600 | 850.0000000000000000
(1 row)
-- Not pushed down due to local conditions present in underneath input rel
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8))
Sort Key: (sum(t1.c8))
-> Aggregate
Output: sum(t1.c8)
-> Foreign Scan
Output: t1.c8
Filter: (((((t1.c8 * t2.c1) / (t1.c8 * t2.c1)))::double precision * random()) <= '1'::double precision)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
sum
-----
310
(1 row)
-- Aggregates in subquery are pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
QUERY PLAN
---------------------------------------------------------------------------------------
Aggregate
Output: count(fdw137_t1.c8), sum(fdw137_t1.c8)
-> Sort
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Sort Key: fdw137_t1.c8, (sum(fdw137_t1.c1))
-> Foreign Scan
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
count | sum
-------+-----
4 | 120
(1 row)
-- Aggregate is still pushed down by taking unshippable expression out
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Sort
Output: ((c4 * ((random() <= '1'::double precision))::integer)), (sum(c1)), c4
Sort Key: ((fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)), (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (c4 * ((random() <= '1'::double precision))::integer), (sum(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
sum1 | sum2
------+------
400 | 2100
600 | 4800
700 | 1400
800 | 1100
900 | 1700
1300 | 1600
| 900
(7 rows)
-- Testing ORDER BY, DISTINCT, FILTER and Ordered-sets within aggregates
-- ORDER BY within aggregates (same column used to order) are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: (sum(c1 ORDER BY c1)), c2
Sort Key: (sum(fdw137_t1.c1 ORDER BY fdw137_t1.c1))
-> GroupAggregate
Output: sum(c1 ORDER BY c1), c2
Group Key: fdw137_t1.c2
-> Sort
Output: c2, c1
Sort Key: fdw137_t1.c2, fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c2, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
sum
-----
100
200
300
400
(4 rows)
-- ORDER BY within aggregate (different column used to order also using DESC)
-- are not pushed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(c8 ORDER BY c1 DESC)
-> Sort
Output: c8, c1
Sort Key: fdw137_t1.c1 DESC
-> Foreign Scan on public.fdw137_t1
Output: c8, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
sum
-----
90
(1 row)
-- DISTINCT within aggregate. Don't push down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(DISTINCT c1)
-> Sort
Output: c1
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
sum
-----
500
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(DISTINCT t1.c1)), t2.c1
Sort Key: (sum(DISTINCT t1.c1))
-> GroupAggregate
Output: sum(DISTINCT t1.c1), t2.c1
Group Key: t2.c1
-> Sort
Output: t2.c1, t1.c1
Sort Key: t2.c1, t1.c1
-> Foreign Scan
Output: t2.c1, t1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(12 rows)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
sum
------
3000
3700
(2 rows)
-- DISTINCT, ORDER BY and FILTER within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
QUERY PLAN
-----------------------------------------------------------------------------------
GroupAggregate
Output: sum(c1), sum(DISTINCT c1 ORDER BY c1) FILTER (WHERE ((c1 % 3) < 2)), c4
-> Sort
Output: c1, c4
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
sum | sum | c4
------+------+-----
4800 | 4100 | 600
(1 row)
-- FILTER within aggregate, not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Sort
Output: (sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500)))), c4
Sort Key: (sum(fdw137_t1.c1) FILTER (WHERE ((fdw137_t1.c1 < 1000) AND (fdw137_t1.c4 > 500))))
-> HashAggregate
Output: sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500))), c4
Group Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
sum
------
100
1000
1700
(7 rows)
-- Outer query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Aggregate
Output: (SubPlan 1)
-> Foreign Scan on public.fdw137_t2 t2
Output: t2._id, t2.c1, t2.c2, t2.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Foreign Scan on public.fdw137_t1 t1
Output: count(*) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
1
(1 row)
-- Inner query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Foreign Scan on public.fdw137_t2 t2
Output: (SubPlan 1)
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Aggregate
Output: count(t1.c1) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
0
10
(2 rows)
-- Ordered-sets within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: c8, rank('10'::bpchar) WITHIN GROUP (ORDER BY c3), percentile_cont((((c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))
Group Key: fdw137_t1.c8
Filter: (percentile_cont((((fdw137_t1.c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((fdw137_t1.c1)::double precision)) < '500'::double precision)
-> Sort
Output: c8, c3, c1
Sort Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: c8, c3, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
c8 | rank | percentile_cont
----+------+-----------------
20 | 1 | 220
30 | 1 | 275
(2 rows)
-- Subquery in FROM clause HAVING aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: (count(*)), x.b
Sort Key: (count(*)), x.b
-> HashAggregate
Output: count(*), x.b
Group Key: x.b
-> Hash Join
Output: x.b
Inner Unique: true
Hash Cond: (fdw137_t1.c8 = x.a)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: x.b, x.a
-> Subquery Scan on x
Output: x.b, x.a
-> Foreign Scan
Output: fdw137_t2.c1, (sum(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(20 rows)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
count | b
-------+----
3 | 10
5 | 20
6 | 30
(3 rows)
-- Join with IS NULL check in HAVING
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Sort Key: (avg(t1.c1)), (sum(t2.c1))
-> Foreign Scan
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Filter: ((avg(t1.c1)) IS NULL)
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(7 rows)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
avg | sum
-----+-----
(0 rows)
-- ORDER BY expression is part of the target list but not pushed down to
-- foreign server.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------
Sort
Output: (((sum(c1)) * ((random() <= '1'::double precision))::integer))
Sort Key: (((sum(fdw137_t1.c1)) * ((random() <= '1'::double precision))::integer))
-> Foreign Scan
Output: ((sum(c1)) * ((random() <= '1'::double precision))::integer)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
sum
-------
13600
(1 row)
-- LATERAL join, with parameterization
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 t2 GROUP BY t2.c1) qry WHERE t1.c8 * 2 = qry.sum ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------------
Sort
Output: t1.c8, qry.sum
Sort Key: t1.c8
-> Hash Join
Output: t1.c8, qry.sum
Hash Cond: ((t1.c8 * 2) = qry.sum)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: qry.sum
-> Subquery Scan on qry
Output: qry.sum
-> Foreign Scan
Output: (sum(t2.c1)), t2.c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 t2)
(16 rows)
-- Check with placeHolderVars
EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Incremental Sort
Output: q.b, (count(fdw137_t1.c1)), (sum(q.a))
Sort Key: q.b, (count(fdw137_t1.c1))
Presorted Key: q.b
-> GroupAggregate
Output: q.b, count(fdw137_t1.c1), sum(q.a)
Group Key: q.b
-> Sort
Output: q.b, fdw137_t1.c1, q.a
Sort Key: q.b
-> Hash Left Join
Output: q.b, fdw137_t1.c1, q.a
Inner Unique: true
Hash Cond: ((fdw137_t1.c8)::numeric = q.b)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: q.b, q.a
-> Subquery Scan on q
Output: q.b, q.a
-> Aggregate
Output: min(13), avg(fdw137_t1_1.c1), NULL::bigint
-> Foreign Scan
Output: fdw137_t1_1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)
(26 rows)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
b | count | sum
---+-------+-----
| 5 |
(1 row)
-- Not supported cases
-- The COUNT of column
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c8) FROM fdw137_t1 ;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: count(c8)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT count(c8) FROM fdw137_t1 ;
count
-------
15
(1 row)
-- Grouping sets
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
c8 | sum
----+------
20 | 3700
30 | 3800
60 | 1500
| 9000
(4 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
c8 | sum
----+-------
10 | 3000
20 | 3700
30 | 3800
60 | 1500
| 12000
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, c4, (sum(c1))
Sort Key: fdw137_t1.c8, fdw137_t1.c4
-> HashAggregate
Output: c8, c4, sum(c1)
Hash Key: fdw137_t1.c8
Hash Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
c8 | c4 | sum
----+------+------
30 | | 3800
60 | | 1500
| 600 | 3200
| 900 | 600
| 1300 | 1500
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1)), (GROUPING(c8))
Sort Key: fdw137_t1.c8
-> HashAggregate
Output: c8, sum(c1), GROUPING(c8)
Group Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
c8 | sum | grouping
----+------+----------
20 | 3700 | 0
30 | 3800 | 0
60 | 1500 | 0
(3 rows)
-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: (sum(c1)), c1
-> Sort
Output: (sum(c1)), c1
Sort Key: (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
s
------
1100
1200
1300
1400
1500
1600
(6 rows)
-- WindowAgg
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (sum(c8)), (count(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, (sum(c8)), count(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2)), (sum(c8))
Sort Key: ((fdw137_t1.c8 % 2))
-> Foreign Scan
Output: c8, (c8 % 2), (sum(c8))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | sum | count
----+-----+-------
20 | 100 | 3
30 | 180 | 3
60 | 60 | 3
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8 DESC
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {60,30,20}
30 | {60,30}
60 | {60}
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {20,30,60}
30 | {30,60}
60 | {60}
(3 rows)
-- User defined function for user defined aggregate, VARIADIC
CREATE FUNCTION least_accum(anyelement, variadic anyarray)
returns anyelement language sql AS
'SELECT least($1, min($2[i])) FROM generate_subscripts($2,2) g(i)';
CREATE aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
-- Not pushed down due to user defined aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (least_agg(VARIADIC ARRAY[c1]))
Sort Key: fdw137_t1.c2
-> HashAggregate
Output: c2, least_agg(VARIADIC ARRAY[c1])
Group Key: fdw137_t1.c2
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
c2 | least_agg
-------+-----------
EMP1 |
EMP10 |
EMP11 |
EMP12 |
EMP13 |
EMP14 |
EMP15 |
EMP16 |
EMP2 |
EMP3 |
EMP4 |
EMP5 |
EMP6 |
EMP7 |
EMP8 |
EMP9 |
(16 rows)
-- Test partition-wise aggregate
SET enable_partitionwise_aggregate TO ON;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
-- Plan with partitionwise aggregates is enabled
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
QUERY PLAN
-------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c1))
Sort Key: (sum(fprt1.c1))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
c1 | sum
----+-----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Sort Key: (sum(fprt1.c2))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c2)), (min(fprt1_1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
c1 | sum | min | count
----+-----+-----+-------
1 | 1 | 1 | 1
2 | 2 | 2 | 1
3 | 3 | 3 | 1
4 | 4 | 4 | 1
5 | 5 | 5 | 1
6 | 6 | 6 | 1
7 | 7 | 7 | 1
8 | 8 | 8 | 1
(8 rows)
-- Check with whole-row reference
-- Should have all the columns in the target list for the given relation
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------
Sort
Output: t1.c1, (count(((t1.*)::fprt1)))
Sort Key: t1.c1
-> Append
-> HashAggregate
Output: t1.c1, count(((t1.*)::fprt1))
Group Key: t1.c1
Filter: (avg(t1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p1 t1
Output: t1.c1, t1.*, t1.c2
Foreign Namespace: mongo_fdw_regress.test1
-> HashAggregate
Output: t1_1.c1, count(((t1_1.*)::fprt1))
Group Key: t1_1.c1
Filter: (avg(t1_1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p2 t1_1
Output: t1_1.c1, t1_1.*, t1_1.c2
Foreign Namespace: mongo_fdw_regress.test2
(18 rows)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
c1 | count
----+-------
1 | 1
2 | 1
3 | 1
4 | 1
5 | 1
6 | 1
7 | 1
8 | 1
(8 rows)
SET enable_partitionwise_aggregate TO OFF;
-- Support enable_aggregate_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'non-bolean');
ERROR: enable_aggregate_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test the option at table level. Setting option at table level does not
-- affect the setting at server level.
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test option for aggregation over join. Allow aggregation only if enabled for
-- both the relations involved in the join.
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
-- FDW-560: Aggregation over nested join. As nested join push down is not
-- supported, aggregation shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t3)
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
sum | c8
-----+----
30 | 10
100 | 20
180 | 30
| 60
|
(5 rows)
-- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON.
-- Shouldn't push down join as well as aggregation.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan on public.fdw137_t1 t1
Output: t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Sort Key: f_test_large.a01 NULLS FIRST, f_test_large.a02 NULLS FIRST, f_test_large.a03 NULLS FIRST, f_test_large.a04 NULLS FIRST, f_test_large.a05 NULLS FIRST, f_test_large.a06 NULLS FIRST, f_test_large.a07 NULLS FIRST, f_test_large.a08 NULLS FIRST, f_test_large.a09 NULLS FIRST, f_test_large.a10 NULLS FIRST, f_test_large.a11 NULLS FIRST, f_test_large.a12 NULLS FIRST, f_test_large.a13 NULLS FIRST, f_test_large.a14 NULLS FIRST, f_test_large.a15 NULLS FIRST, f_test_large.a16 NULLS FIRST, f_test_large.a17 NULLS FIRST, f_test_large.a18 NULLS FIRST, f_test_large.a19 NULLS FIRST, f_test_large.a20 NULLS FIRST, f_test_large.a21 NULLS FIRST, f_test_large.a22 NULLS FIRST, f_test_large.a23 NULLS FIRST, f_test_large.a24 NULLS FIRST, f_test_large.a25 NULLS FIRST, f_test_large.a26 NULLS FIRST, f_test_large.a27 NULLS FIRST, f_test_large.a28 NULLS FIRST, f_test_large.a29 NULLS FIRST, f_test_large.a30 NULLS FIRST, f_test_large.a31 NULLS FIRST, f_test_large.a32 NULLS FIRST, f_test_large.a33 NULLS FIRST, f_test_large.a34 DESC NULLS LAST, f_test_large.a35 NULLS FIRST
-> Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(6 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 32
32 | 32
32 | 32
132 | 132
(5 rows)
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(3 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 96
132 | 132
(3 rows)
-- FDW-131: Limit and offset pushdown with Aggregate pushdown.
SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1;
avg | c1
-----+----
10 | 10
20 | 20
30 | 30
40 | 40
50 | 50
|
(6 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
sum | c1
-----+----
10 | 10
(1 row)
-- Limit 0, Offset 0 with aggregates.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
sum | c1
-----+----
(0 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (avg(c1))
-> Foreign Scan
Output: c1, (avg(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
-> Foreign Scan
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(9 rows)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
ERROR: LIMIT must not be negative
-- FDW-559: Test mongo_fdw.enable_aggregate_pushdown GUC.
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_aggregate_pushdown;
mongo_fdw.enable_aggregate_pushdown
-------------------------------------
on
(1 row)
-- Negative testing for GUC value.
SET mongo_fdw.enable_aggregate_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_aggregate_pushdown" requires a Boolean value
--Disable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Shouldn't pushdown aggregate because GUC is OFF.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
--Enable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Should pushdown aggregate because GUC is ON.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
-- Test for aggregation over join when server and table options for both the
-- tables is true and guc is enabled. Should pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
SET mongo_fdw.enable_join_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (count(*)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
--Disable the GUC enable_join_pushdown. Shouldn't pushdown aggregate.
SET mongo_fdw.enable_join_pushdown to off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8
Merge Cond: (t1.c8 = t2.c1)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1 NULLS FIRST
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(15 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
SET mongo_fdw.enable_join_pushdown to on;
--Disable the GUC enable_aggregate_pushdown. Shouldn't pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(6 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_aggregate_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- When option enable_aggregate_pushdown is disabled. Shouldn't pushdown
-- aggregate as well as ORDER BY too.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> HashAggregate
Output: c2, sum(c1), c1
Group Key: fdw137_t1.c2, fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Cleanup
DELETE FROM fdw137_t1 WHERE c8 IS NULL;
DELETE FROM fdw137_t1 WHERE c8 = 60;
DELETE FROM fdw137_t2 WHERE c1 IS NULL;
DELETE FROM fdw137_t2 WHERE c1 = 50;
DROP FOREIGN TABLE fdw137_t1;
DROP FOREIGN TABLE fdw137_t2;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE f_test_large;
DROP TABLE fprt1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/aggregate_pushdown_3.out 0000664 0000000 0000000 00000246201 15066665201 0022732 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables.
CREATE FOREIGN TABLE fdw137_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE fdw137_t2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
INSERT INTO fdw137_t1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO fdw137_t1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO fdw137_t2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO fdw137_t2 VALUES (0);
-- Create local table.
CREATE TABLE fdw137_local AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1;
-- Simple aggregates. ORDER BY push-down not possible because only column names allowed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
Result
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), ((sum(c1)) * ((random() <= '1'::double precision))::integer), c4
-> Sort
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Sort Key: (count(*)) NULLS FIRST, (sum(fdw137_t1.c1)) NULLS FIRST
-> Foreign Scan
Output: (count(*)), (sum(c1)), (avg(c1)), (min(c4)), (max(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
count | sum | avg | min | max | sum2
-------+------+------------------+------+------+------
1 | 1100 | 1100 | 800 | 1100 | 1100
1 | 1400 | 1400 | 700 | 1400 | 1400
2 | 1600 | 800 | 1300 | 1500 | 1600
3 | 1700 | 566.666666666667 | 900 | 700 | 1700
(4 rows)
-- GROUP BY clause HAVING expressions
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c1, (sum(c1)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum | count
------+------+-------
600 | 600 | 1
700 | 700 | 1
800 | 800 | 1
900 | 900 | 1
1000 | 1000 | 1
1100 | 1100 | 1
1200 | 1200 | 1
1300 | 1300 | 1
1400 | 1400 | 1
1500 | 1500 | 1
1600 | 1600 | 1
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c8, (min(c2))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
c8 | min
----+------
20 | EMP1
(1 row)
-- Multi-column GROUP BY clause. Push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregation on expression. Don't push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
GroupAggregate
Output: c1, sum((c1 + 2))
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
c1 | sum
------+------
600 | 602
700 | 702
800 | 802
900 | 902
1000 | 1002
1100 | 1102
1200 | 1202
1300 | 1302
1400 | 1402
1500 | 1502
1600 | 1602
(11 rows)
-- Aggregate with unshippable GROUP BY clause are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer))
Sort Key: (avg(fdw137_t1.c4))
-> HashAggregate
Output: avg(c4), ((c4 * ((random() <= '1'::double precision))::integer))
Group Key: (fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)
-> Foreign Scan on public.fdw137_t1
Output: (c4 * ((random() <= '1'::double precision))::integer), c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
avg
-----------------------
400.0000000000000000
600.0000000000000000
700.0000000000000000
800.0000000000000000
900.0000000000000000
1300.0000000000000000
(7 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c1, (sum(c1))
Sort Key: fdw137_t1.c1
-> HashAggregate
Output: c1, sum(c1)
Group Key: fdw137_t1.c1
Filter: (min((fdw137_t1.c1 * 3)) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
c1 | sum
------+------
200 | 200
300 | 300
400 | 400
500 | 500
600 | 600
700 | 700
800 | 800
900 | 900
1000 | 1000
1100 | 1100
1200 | 1200
1300 | 1300
1400 | 1400
1500 | 1500
1600 | 1600
(15 rows)
-- FDW-134: Test ORDER BY with COLLATE. Shouldn't push-down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), ((c2)::text), c1
Sort Key: fdw137_t1.c2 COLLATE "en_US" NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c2, c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
-- Using expressions in HAVING clause. Pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c3, (count(*))
Sort Key: fdw137_t1.c3, (count(*))
-> Foreign Scan
Output: c3, (count(*))
Filter: (abs((max(fdw137_t1.c8))) = 10)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(7 rows)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
c3 | count
-----------+-------
HEAD | 1
(1 row)
-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Aggregate
Output: count(*)
-> Foreign Scan
Output: fdw137_t1.c3, NULL::bigint
Filter: (((((avg(fdw137_t1.c1)) / (avg(fdw137_t1.c1))))::double precision * random()) <= '1'::double precision)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
count
-------
0
(1 row)
-- Aggregate over join query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8)), (avg(t2.c1))
Sort Key: (sum(t1.c8)) DESC NULLS LAST
-> Foreign Scan
Output: (sum(t1.c8)), (avg(t2.c1))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
sum | avg
-----+------------------
310 | 22.1428571428571
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, (count(*)), t2.c4
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2))
(3 rows)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | count | c4
----+-------+------
10 | 1 |
10 | 1 | 700
10 | 1 | 900
20 | 2 | 400
20 | 1 | 800
20 | 1 | 900
20 | 1 | 1300
30 | 5 | 600
30 | 1 | 900
(9 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregate is not pushed down as aggregation contains random()
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------
Sort
Output: (sum((c1 * ((random() <= '1'::double precision))::integer))), (avg(c1))
Sort Key: (sum((fdw137_t1.c1 * ((random() <= '1'::double precision))::integer)))
-> Aggregate
Output: sum((c1 * ((random() <= '1'::double precision))::integer)), avg(c1)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
sum | avg
-------+----------------------
13600 | 850.0000000000000000
(1 row)
-- Not pushed down due to local conditions present in underneath input rel
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t1.c8))
Sort Key: (sum(t1.c8))
-> Aggregate
Output: sum(t1.c8)
-> Foreign Scan
Output: t1.c8
Filter: (((((t1.c8 * t2.c1) / (t1.c8 * t2.c1)))::double precision * random()) <= '1'::double precision)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
sum
-----
310
(1 row)
-- Aggregates in subquery are pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
QUERY PLAN
---------------------------------------------------------------------------------------
Aggregate
Output: count(fdw137_t1.c8), sum(fdw137_t1.c8)
-> Sort
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Sort Key: fdw137_t1.c8, (sum(fdw137_t1.c1))
-> Foreign Scan
Output: fdw137_t1.c8, (sum(fdw137_t1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
count | sum
-------+-----
4 | 120
(1 row)
-- Aggregate is still pushed down by taking unshippable expression out
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Sort
Output: ((c4 * ((random() <= '1'::double precision))::integer)), (sum(c1)), c4
Sort Key: ((fdw137_t1.c4 * ((random() <= '1'::double precision))::integer)), (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (c4 * ((random() <= '1'::double precision))::integer), (sum(c1)), c4
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
sum1 | sum2
------+------
400 | 2100
600 | 4800
700 | 1400
800 | 1100
900 | 1700
1300 | 1600
| 900
(7 rows)
-- Testing ORDER BY, DISTINCT, FILTER and Ordered-sets within aggregates
-- ORDER BY within aggregates (same column used to order) are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: (sum(c1 ORDER BY c1)), c2
Sort Key: (sum(fdw137_t1.c1 ORDER BY fdw137_t1.c1))
-> GroupAggregate
Output: sum(c1 ORDER BY c1), c2
Group Key: fdw137_t1.c2
-> Sort
Output: c2, c1
Sort Key: fdw137_t1.c2, fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c2, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
sum
-----
100
200
300
400
(4 rows)
-- ORDER BY within aggregate (different column used to order also using DESC)
-- are not pushed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(c8 ORDER BY c1 DESC)
-> Sort
Output: c8, c1
Sort Key: fdw137_t1.c1 DESC
-> Foreign Scan on public.fdw137_t1
Output: c8, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
sum
-----
90
(1 row)
-- DISTINCT within aggregate. Don't push down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
QUERY PLAN
--------------------------------------------------------------
Aggregate
Output: sum(DISTINCT c1)
-> Sort
Output: c1
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
sum
-----
500
(1 row)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(DISTINCT t1.c1)), t2.c1
Sort Key: (sum(DISTINCT t1.c1))
-> GroupAggregate
Output: sum(DISTINCT t1.c1), t2.c1
Group Key: t2.c1
-> Sort
Output: t2.c1, t1.c1
Sort Key: t2.c1, t1.c1
-> Foreign Scan
Output: t2.c1, t1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(12 rows)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
sum
------
3000
3700
(2 rows)
-- DISTINCT, ORDER BY and FILTER within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
QUERY PLAN
-----------------------------------------------------------------------------------
GroupAggregate
Output: sum(c1), sum(DISTINCT c1 ORDER BY c1) FILTER (WHERE ((c1 % 3) < 2)), c4
-> Sort
Output: c1, c4
Sort Key: fdw137_t1.c1
-> Foreign Scan on public.fdw137_t1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(8 rows)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
sum | sum | c4
------+------+-----
4800 | 4100 | 600
(1 row)
-- FILTER within aggregate, not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Sort
Output: (sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500)))), c4
Sort Key: (sum(fdw137_t1.c1) FILTER (WHERE ((fdw137_t1.c1 < 1000) AND (fdw137_t1.c4 > 500))))
-> HashAggregate
Output: sum(c1) FILTER (WHERE ((c1 < 1000) AND (c4 > 500))), c4
Group Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
sum
------
100
1000
1700
(7 rows)
-- Outer query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Aggregate
Output: (SubPlan 1)
-> Foreign Scan on public.fdw137_t2 t2
Output: t2._id, t2.c1, t2.c2, t2.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Foreign Scan on public.fdw137_t1 t1
Output: count(*) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
1
(1 row)
-- Inner query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Unique
Output: ((SubPlan 1))
-> Sort
Output: ((SubPlan 1))
Sort Key: ((SubPlan 1))
-> Foreign Scan on public.fdw137_t2 t2
Output: (SubPlan 1)
Foreign Namespace: mongo_fdw_regress.test_tbl2
SubPlan 1
-> Aggregate
Output: count(t1.c1) FILTER (WHERE ((t2.c1 = 20) AND (t2.c1 < 30)))
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
count
-------
0
10
(2 rows)
-- Ordered-sets within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: c8, rank('10'::bpchar) WITHIN GROUP (ORDER BY c3), percentile_cont((((c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((c1)::double precision))
Group Key: fdw137_t1.c8
Filter: (percentile_cont((((fdw137_t1.c8)::numeric / '200'::numeric))::double precision) WITHIN GROUP (ORDER BY ((fdw137_t1.c1)::double precision)) < '500'::double precision)
-> Sort
Output: c8, c3, c1
Sort Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: c8, c3, c1
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
c8 | rank | percentile_cont
----+------+-----------------
20 | 1 | 220
30 | 1 | 275
(2 rows)
-- Subquery in FROM clause HAVING aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: (count(*)), x.b
Sort Key: (count(*)), x.b
-> HashAggregate
Output: count(*), x.b
Group Key: x.b
-> Hash Join
Output: x.b
Inner Unique: true
Hash Cond: (fdw137_t1.c8 = x.a)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: x.b, x.a
-> Subquery Scan on x
Output: x.b, x.a
-> Foreign Scan
Output: fdw137_t2.c1, (sum(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(20 rows)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
count | b
-------+----
3 | 10
5 | 20
6 | 30
(3 rows)
-- Join with IS NULL check in HAVING
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Sort
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Sort Key: (avg(t1.c1)), (sum(t2.c1))
-> Foreign Scan
Output: (avg(t1.c1)), (sum(t2.c1)), t2.c1
Filter: ((avg(t1.c1)) IS NULL)
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2))
(7 rows)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
avg | sum
-----+-----
(0 rows)
-- ORDER BY expression is part of the target list but not pushed down to
-- foreign server.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------
Sort
Output: (((sum(c1)) * ((random() <= '1'::double precision))::integer))
Sort Key: (((sum(fdw137_t1.c1)) * ((random() <= '1'::double precision))::integer))
-> Foreign Scan
Output: ((sum(c1)) * ((random() <= '1'::double precision))::integer)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
sum
-------
13600
(1 row)
-- LATERAL join, with parameterization
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 t2 GROUP BY t2.c1) qry WHERE t1.c8 * 2 = qry.sum ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------------------------------
Sort
Output: t1.c8, qry.sum
Sort Key: t1.c8
-> Hash Join
Output: t1.c8, qry.sum
Hash Cond: ((t1.c8 * 2) = qry.sum)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: qry.sum
-> Subquery Scan on qry
Output: qry.sum
-> Foreign Scan
Output: (sum(t2.c1)), t2.c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 t2)
(16 rows)
-- Check with placeHolderVars
EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Incremental Sort
Output: q.b, (count(fdw137_t1.c1)), (sum(q.a))
Sort Key: q.b, (count(fdw137_t1.c1))
Presorted Key: q.b
-> GroupAggregate
Output: q.b, count(fdw137_t1.c1), sum(q.a)
Group Key: q.b
-> Sort
Output: q.b, fdw137_t1.c1, q.a
Sort Key: q.b
-> Hash Left Join
Output: q.b, fdw137_t1.c1, q.a
Inner Unique: true
Hash Cond: ((fdw137_t1.c8)::numeric = q.b)
-> Foreign Scan on public.fdw137_t1
Output: fdw137_t1._id, fdw137_t1.c1, fdw137_t1.c2, fdw137_t1.c3, fdw137_t1.c4, fdw137_t1.c5, fdw137_t1.c6, fdw137_t1.c7, fdw137_t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
Output: q.b, q.a
-> Subquery Scan on q
Output: q.b, q.a
-> Aggregate
Output: min(13), avg(fdw137_t1_1.c1), NULL::bigint
-> Foreign Scan
Output: fdw137_t1_1.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)
(26 rows)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
b | count | sum
---+-------+-----
| 5 |
(1 row)
-- Not supported cases
-- The COUNT of column
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c8) FROM fdw137_t1 ;
QUERY PLAN
--------------------------------------------------------
Aggregate
Output: count(c8)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(5 rows)
SELECT count(c8) FROM fdw137_t1 ;
count
-------
15
(1 row)
-- Grouping sets
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
c8 | sum
----+------
20 | 3700
30 | 3800
60 | 1500
| 9000
(4 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1))
Sort Key: fdw137_t1.c8
-> MixedAggregate
Output: c8, sum(c1)
Hash Key: fdw137_t1.c8
Group Key: ()
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
c8 | sum
----+-------
10 | 3000
20 | 3700
30 | 3800
60 | 1500
| 12000
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, c4, (sum(c1))
Sort Key: fdw137_t1.c8, fdw137_t1.c4
-> HashAggregate
Output: c8, c4, sum(c1)
Hash Key: fdw137_t1.c8
Hash Key: fdw137_t1.c4
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
c8 | c4 | sum
----+------+------
30 | | 3800
60 | | 1500
| 600 | 3200
| 900 | 600
| 1300 | 1500
(5 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c8, (sum(c1)), (GROUPING(c8))
Sort Key: fdw137_t1.c8
-> HashAggregate
Output: c8, sum(c1), GROUPING(c8)
Group Key: fdw137_t1.c8
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
c8 | sum | grouping
----+------+----------
20 | 3700 | 0
30 | 3800 | 0
60 | 1500 | 0
(3 rows)
-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------
Unique
Output: (sum(c1)), c1
-> Sort
Output: (sum(c1)), c1
Sort Key: (sum(fdw137_t1.c1))
-> Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(8 rows)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
s
------
1100
1200
1300
1400
1500
1600
(6 rows)
-- WindowAgg
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (sum(c8)), (count(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, (sum(c8)), count(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2)), (sum(c8))
Sort Key: ((fdw137_t1.c8 % 2))
-> Foreign Scan
Output: c8, (c8 % 2), (sum(c8))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | sum | count
----+-----+-------
20 | 100 | 3
30 | 180 | 3
60 | 60 | 3
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8 DESC
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {60,30,20}
30 | {60,30}
60 | {60}
(3 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------------------
Sort
Output: c8, (array_agg(c8) OVER (?)), ((c8 % 2))
Sort Key: fdw137_t1.c8
-> WindowAgg
Output: c8, array_agg(c8) OVER (?), ((c8 % 2))
-> Sort
Output: c8, ((c8 % 2))
Sort Key: ((fdw137_t1.c8 % 2)), fdw137_t1.c8
-> Foreign Scan
Output: c8, (c8 % 2)
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(11 rows)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
c8 | array_agg
----+------------
20 | {20,30,60}
30 | {30,60}
60 | {60}
(3 rows)
-- User defined function for user defined aggregate, VARIADIC
CREATE FUNCTION least_accum(anyelement, variadic anyarray)
returns anyelement language sql AS
'SELECT least($1, min($2[i])) FROM generate_subscripts($2,2) g(i)';
CREATE aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
-- Not pushed down due to user defined aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (least_agg(VARIADIC ARRAY[c1]))
Sort Key: fdw137_t1.c2
-> HashAggregate
Output: c2, least_agg(VARIADIC ARRAY[c1])
Group Key: fdw137_t1.c2
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(9 rows)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
c2 | least_agg
-------+-----------
EMP1 |
EMP10 |
EMP11 |
EMP12 |
EMP13 |
EMP14 |
EMP15 |
EMP16 |
EMP2 |
EMP3 |
EMP4 |
EMP5 |
EMP6 |
EMP7 |
EMP8 |
EMP9 |
(16 rows)
-- Test partition-wise aggregate
SET enable_partitionwise_aggregate TO ON;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
-- Plan with partitionwise aggregates is enabled
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
QUERY PLAN
-------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c1))
Sort Key: (sum(fprt1.c1))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
c1 | sum
----+-----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
QUERY PLAN
------------------------------------------------------------------------------------
Sort
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Sort Key: (sum(fprt1.c2))
-> Append
-> Foreign Scan
Output: fprt1.c1, (sum(fprt1.c2)), (min(fprt1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1)
-> Foreign Scan
Output: fprt1_1.c1, (sum(fprt1_1.c2)), (min(fprt1_1.c2)), (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test2 fprt1)
(10 rows)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
c1 | sum | min | count
----+-----+-----+-------
1 | 1 | 1 | 1
2 | 2 | 2 | 1
3 | 3 | 3 | 1
4 | 4 | 4 | 1
5 | 5 | 5 | 1
6 | 6 | 6 | 1
7 | 7 | 7 | 1
8 | 8 | 8 | 1
(8 rows)
-- Check with whole-row reference
-- Should have all the columns in the target list for the given relation
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
QUERY PLAN
----------------------------------------------------------------
Sort
Output: t1.c1, (count(((t1.*)::fprt1)))
Sort Key: t1.c1
-> Append
-> HashAggregate
Output: t1.c1, count(((t1.*)::fprt1))
Group Key: t1.c1
Filter: (avg(t1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p1 t1
Output: t1.c1, t1.*, t1.c2
Foreign Namespace: mongo_fdw_regress.test1
-> HashAggregate
Output: t1_1.c1, count(((t1_1.*)::fprt1))
Group Key: t1_1.c1
Filter: (avg(t1_1.c2) < '22'::numeric)
-> Foreign Scan on public.ftprt1_p2 t1_1
Output: t1_1.c1, t1_1.*, t1_1.c2
Foreign Namespace: mongo_fdw_regress.test2
(18 rows)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
c1 | count
----+-------
1 | 1
2 | 1
3 | 1
4 | 1
5 | 1
6 | 1
7 | 1
8 | 1
(8 rows)
SET enable_partitionwise_aggregate TO OFF;
-- Support enable_aggregate_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'non-bolean');
ERROR: enable_aggregate_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test the option at table level. Setting option at table level does not
-- affect the setting at server level.
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
-- Test option for aggregation over join. Allow aggregation only if enabled for
-- both the relations involved in the join.
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8
Sort Key: t1.c8
-> HashAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
-- FDW-560: Aggregation over nested join. As nested join push down is not
-- supported, aggregation shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t3)
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
sum | c8
-----+----
30 | 10
100 | 20
180 | 30
| 60
|
(5 rows)
-- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON.
-- Shouldn't push down join as well as aggregation.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
QUERY PLAN
--------------------------------------------------------------------
GroupAggregate
Output: sum(t2.c1), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8, t2.c1
Merge Cond: (t1.c8 = t2.c1)
-> Sort
Output: t1.c8
Sort Key: t1.c8
-> Foreign Scan on public.fdw137_t1 t1
Output: t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(18 rows)
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Sort Key: f_test_large.a01 NULLS FIRST, f_test_large.a02 NULLS FIRST, f_test_large.a03 NULLS FIRST, f_test_large.a04 NULLS FIRST, f_test_large.a05 NULLS FIRST, f_test_large.a06 NULLS FIRST, f_test_large.a07 NULLS FIRST, f_test_large.a08 NULLS FIRST, f_test_large.a09 NULLS FIRST, f_test_large.a10 NULLS FIRST, f_test_large.a11 NULLS FIRST, f_test_large.a12 NULLS FIRST, f_test_large.a13 NULLS FIRST, f_test_large.a14 NULLS FIRST, f_test_large.a15 NULLS FIRST, f_test_large.a16 NULLS FIRST, f_test_large.a17 NULLS FIRST, f_test_large.a18 NULLS FIRST, f_test_large.a19 NULLS FIRST, f_test_large.a20 NULLS FIRST, f_test_large.a21 NULLS FIRST, f_test_large.a22 NULLS FIRST, f_test_large.a23 NULLS FIRST, f_test_large.a24 NULLS FIRST, f_test_large.a25 NULLS FIRST, f_test_large.a26 NULLS FIRST, f_test_large.a27 NULLS FIRST, f_test_large.a28 NULLS FIRST, f_test_large.a29 NULLS FIRST, f_test_large.a30 NULLS FIRST, f_test_large.a31 NULLS FIRST, f_test_large.a32 NULLS FIRST, f_test_large.a33 NULLS FIRST, f_test_large.a34 DESC NULLS LAST, f_test_large.a35 NULLS FIRST
-> Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a33, a34, a35
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(6 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 32
32 | 32
32 | 32
132 | 132
(5 rows)
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: a32, (sum(a32)), a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test_large f_test_large)
(3 rows)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
a32 | sum
-----+-----
2 | 2
32 | 96
132 | 132
(3 rows)
-- FDW-131: Limit and offset pushdown with Aggregate pushdown.
SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1;
avg | c1
-----+----
10 | 10
20 | 20
30 | 30
40 | 40
50 | 50
|
(6 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
sum | c1
-----+----
10 | 10
(1 row)
-- Limit 0, Offset 0 with aggregates.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
sum | c1
-----+----
(0 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(3 rows)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
sum | c1
-----+----
20 | 20
30 | 30
40 | 40
50 | 50
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (sum(c1))
-> Foreign Scan
Output: c1, (sum(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
---------------------------------------------------------------------------------
Limit
Output: c1, (avg(c1))
-> Foreign Scan
Output: c1, (avg(c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(5 rows)
-- Should throw an error.
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
-> Foreign Scan
Output: fdw137_t2.c1, (avg(fdw137_t2.c1))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2)
(9 rows)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
ERROR: LIMIT must not be negative
-- FDW-559: Test mongo_fdw.enable_aggregate_pushdown GUC.
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_aggregate_pushdown;
mongo_fdw.enable_aggregate_pushdown
-------------------------------------
on
(1 row)
-- Negative testing for GUC value.
SET mongo_fdw.enable_aggregate_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_aggregate_pushdown" requires a Boolean value
--Disable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Shouldn't pushdown aggregate because GUC is OFF.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> HashAggregate
Output: count(*), c1
Group Key: fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
--Enable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Should pushdown aggregate because GUC is ON.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: (count(*)), c1
Sort Key: (count(*))
-> Foreign Scan
Output: (count(*)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
count
-------
1
1
1
1
1
1
1
1
1
1
1
(11 rows)
-- Test for aggregation over join when server and table options for both the
-- tables is true and guc is enabled. Should pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
SET mongo_fdw.enable_join_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (count(*)), t1.c8
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
--Disable the GUC enable_join_pushdown. Shouldn't pushdown aggregate.
SET mongo_fdw.enable_join_pushdown to off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Merge Left Join
Output: t1.c8
Merge Cond: (t1.c8 = t2.c1)
-> Foreign Scan on public.fdw137_t1 t1
Output: t1._id, t1.c1, t1.c2, t1.c3, t1.c4, t1.c5, t1.c6, t1.c7, t1.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Sort
Output: t2.c1
Sort Key: t2.c1 NULLS FIRST
-> Foreign Scan on public.fdw137_t2 t2
Output: t2.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(15 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
SET mongo_fdw.enable_join_pushdown to on;
--Disable the GUC enable_aggregate_pushdown. Shouldn't pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
GroupAggregate
Output: count(*), t1.c8
Group Key: t1.c8
-> Foreign Scan
Output: t1.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)
(6 rows)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
count | c8
-------+----
1 |
3 | 10
5 | 20
6 | 30
1 | 60
(5 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_aggregate_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(6 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Sort
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Sort Key: t1.c8 NULLS FIRST
-> Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(6 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------------------------
Foreign Scan
Output: c2, (sum(c1)), c1
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1)
(3 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Foreign Scan
Output: (sum(t2.c1)), t1.c8, (avg(t1.c8))
Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2))
(3 rows)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
sum | c8 | avg
-----+----+-----
100 | 20 | 20
180 | 30 | 30
0 | 60 | 60
(3 rows)
-- When option enable_aggregate_pushdown is disabled. Shouldn't pushdown
-- aggregate as well as ORDER BY too.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Output: c2, (sum(c1)), c1
Sort Key: fdw137_t1.c2 NULLS FIRST
-> HashAggregate
Output: c2, sum(c1), c1
Group Key: fdw137_t1.c2, fdw137_t1.c1
Filter: (min(fdw137_t1.c1) > 500)
-> Foreign Scan on public.fdw137_t1
Output: _id, c1, c2, c3, c4, c5, c6, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
c2 | sum
-------+------
EMP10 | 1000
EMP11 | 1100
EMP12 | 1200
EMP13 | 1300
EMP14 | 1400
EMP15 | 1500
EMP16 | 1600
EMP6 | 600
EMP7 | 700
EMP8 | 800
EMP9 | 900
(11 rows)
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Cleanup
DELETE FROM fdw137_t1 WHERE c8 IS NULL;
DELETE FROM fdw137_t1 WHERE c8 = 60;
DELETE FROM fdw137_t2 WHERE c1 IS NULL;
DELETE FROM fdw137_t2 WHERE c1 = 50;
DROP FOREIGN TABLE fdw137_t1;
DROP FOREIGN TABLE fdw137_t2;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE f_test_large;
DROP TABLE fprt1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/connection_validation.out 0000664 0000000 0000000 00000006074 15066665201 0023206 0 ustar 00root root 0000000 0000000 \set VERBOSITY terse
\set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables and validate
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
--
-- fdw-108: After a change to a pg_foreign_server or pg_user_mapping catalog
-- entry, connection should be invalidated.
--
-- Alter one of the SERVER option
-- Set wrong address for mongo_server
ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.10');
ALTER SERVER mongo_server OPTIONS (SET port '9999');
-- Should fail with an error
INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED');
ERROR: could not connect to server mongo_server
UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2;
ERROR: could not connect to server mongo_server
DELETE FROM f_mongo_test WHERE a = 2;
ERROR: could not connect to server mongo_server
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
ERROR: could not connect to server mongo_server
-- Set correct address for mongo_server
ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST);
ALTER SERVER mongo_server OPTIONS (SET port :MONGO_PORT);
-- Should able to insert the data
INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED');
DELETE FROM f_mongo_test WHERE a = 2;
-- Drop user mapping and create with invalid username and password for public
-- user mapping
DROP USER MAPPING FOR public SERVER mongo_server;
CREATE USER MAPPING FOR public SERVER mongo_server
OPTIONS (username 'wrong', password 'wrong');
-- Should fail with an error
INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED');
ERROR: could not connect to server mongo_server
UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3;
ERROR: could not connect to server mongo_server
DELETE FROM f_mongo_test WHERE a = 3;
ERROR: could not connect to server mongo_server
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
ERROR: could not connect to server mongo_server
-- Drop user mapping and create without username and password for public
-- user mapping
DROP USER MAPPING FOR public SERVER mongo_server;
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Should able to insert the data
INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED');
DELETE FROM f_mongo_test WHERE a = 3;
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/dml.out 0000664 0000000 0000000 00000025127 15066665201 0017411 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress,
-- mongo_fdw_regress1 and mongo_fdw_regress2 databases on MongoDB with all
-- permission for MONGO_USER_NAME user with MONGO_PASS password and ran
-- mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_mongo_test1 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress1', collection 'mongo_test1');
CREATE FOREIGN TABLE f_mongo_test2 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress2', collection 'mongo_test2');
-- Creating foreign table without specifying database.
CREATE FOREIGN TABLE f_mongo_test3 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (collection 'mongo_test3');
CREATE FOREIGN TABLE f_mongo_test6 (_id name, a int, b text[]) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl6');
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test)
-- exist in a database (mongo_fdw_regress) in mongoDB.
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
INSERT INTO f_mongo_test VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
a | b
----+-----------------------
0 | mongo_test collection
10 | INSERT
(2 rows)
UPDATE f_mongo_test SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
a | b
----+-----------------------
0 | mongo_test collection
10 | UPDATE
(2 rows)
DELETE FROM f_mongo_test WHERE a = 10;
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test1)
-- not exist in a database (mongo_fdw_regress1) in mongoDB.
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
a | b
---+---
(0 rows)
INSERT INTO f_mongo_test1 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
a | b
----+--------
10 | INSERT
(1 row)
UPDATE f_mongo_test1 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
a | b
----+--------
10 | UPDATE
(1 row)
DELETE FROM f_mongo_test1 WHERE a = 10;
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
a | b
---+---
(0 rows)
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test2)
-- not exist in a non exist database (mongo_fdw_regress2) in mongoDB.
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
a | b
---+---
(0 rows)
INSERT INTO f_mongo_test2 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
a | b
----+--------
10 | INSERT
(1 row)
UPDATE f_mongo_test2 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
a | b
----+--------
10 | UPDATE
(1 row)
DELETE FROM f_mongo_test2 WHERE a = 10;
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
a | b
---+---
(0 rows)
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test)
-- when foreign table created without database option.
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
a | b
---+---
(0 rows)
INSERT INTO f_mongo_test3 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
a | b
----+--------
10 | INSERT
(1 row)
UPDATE f_mongo_test3 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
a | b
----+--------
10 | UPDATE
(1 row)
DELETE FROM f_mongo_test3 WHERE a = 10;
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
a | b
---+---
(0 rows)
-- FDW-158: Fix server crash when analyzing a foreign table.
ANALYZE f_mongo_test;
-- Should give correct number of rows now.
SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test';
reltuples
-----------
1
(1 row)
-- Check count using select query on table.
SELECT count(*) FROM f_mongo_test;
count
-------
1
(1 row)
-- Some more variants of vacuum and analyze
VACUUM f_mongo_test;
WARNING: skipping "f_mongo_test" --- cannot vacuum non-tables or special system tables
VACUUM FULL f_mongo_test;
WARNING: skipping "f_mongo_test" --- cannot vacuum non-tables or special system tables
VACUUM FREEZE f_mongo_test;
WARNING: skipping "f_mongo_test" --- cannot vacuum non-tables or special system tables
ANALYZE f_mongo_test;
ANALYZE f_mongo_test(a);
VACUUM ANALYZE f_mongo_test;
WARNING: skipping "f_mongo_test" --- cannot vacuum non-tables or special system tables
-- FDW-226: Fix COPY FROM and foreign partition routing results in a
-- server crash
-- Should fail as foreign table direct copy is not supported
COPY f_mongo_test TO '/tmp/data.txt' delimiter ',';
ERROR: cannot copy from foreign table "f_mongo_test"
HINT: Try the COPY (SELECT ...) TO variant.
COPY f_mongo_test (a) TO '/tmp/data.txt' delimiter ',';
ERROR: cannot copy from foreign table "f_mongo_test"
HINT: Try the COPY (SELECT ...) TO variant.
COPY f_mongo_test (b) TO '/tmp/data.txt' delimiter ',';
ERROR: cannot copy from foreign table "f_mongo_test"
HINT: Try the COPY (SELECT ...) TO variant.
-- Should pass
COPY (SELECT * FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT a, b FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT a FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT b FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
-- Should throw an error as copy to foreign table is not supported
DO
$$
BEGIN
COPY f_mongo_test FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
NOTICE: ERROR: COPY and foreign partition routing not supported in mongo_fdw
DO
$$
BEGIN
COPY f_mongo_test(a, b) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
NOTICE: ERROR: COPY and foreign partition routing not supported in mongo_fdw
DO
$$
BEGIN
COPY f_mongo_test(a) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
NOTICE: ERROR: COPY and foreign partition routing not supported in mongo_fdw
DO
$$
BEGIN
COPY f_mongo_test(b) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
NOTICE: ERROR: COPY and foreign partition routing not supported in mongo_fdw
--FDW-466: Document update for array elements shouldn't lead to the crash
INSERT INTO f_mongo_test6 VALUES (0, 1, ARRAY ['INSERT', 'DELETE']);
SELECT a, b FROM f_mongo_test6 ORDER BY a;
a | b
---+-----------------
1 | {INSERT,DELETE}
(1 row)
UPDATE f_mongo_test6 SET b[1] = 'UPDATE' WHERE a = 1;
SELECT a, b FROM f_mongo_test6 ORDER BY a;
a | b
---+-----------------
1 | {UPDATE,DELETE}
(1 row)
DELETE FROM f_mongo_test6 WHERE b[2] = 'DELETE';
SELECT a, b FROM f_mongo_test6 ORDER BY a;
a | b
---+---
(0 rows)
-- If first column type is not NAME then UPDATE/DELETE should result into an error.
CREATE FOREIGN TABLE f_mongo_test7 (_id text, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl7');
SELECT a, b FROM f_mongo_test7 ORDER BY 1;
a | b
----+------
10 | ROW1
20 | ROW2
(2 rows)
UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE a = 10;
ERROR: type of first column of MongoDB's foreign table must be "NAME"
DELETE FROM f_mongo_test7 WHERE a = 10;
ERROR: type of first column of MongoDB's foreign table must be "NAME"
DROP FOREIGN TABLE f_mongo_test7;
-- If first column name is not _id then UPDATE/DELETE should result into an error.
CREATE FOREIGN TABLE f_mongo_test7 (id1 NAME, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl7');
SELECT a, b FROM f_mongo_test7 ORDER BY 1;
a | b
----+------
10 | ROW1
20 | ROW2
(2 rows)
UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE a = 10;
ERROR: first column of MongoDB's foreign table must be "_id"
DELETE FROM f_mongo_test7 WHERE a = 10;
ERROR: first column of MongoDB's foreign table must be "_id"
-- When _id is non-objectId type on MongoDB. Should result into an error.
CREATE FOREIGN TABLE f_mongo_test8 (_id NAME, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl8');
SELECT * FROM f_mongo_test8 ORDER BY 1;
ERROR: cannot convert BSON type to column type
HINT: Column type "NAME" is compatible only with BSON type "ObjectId".
UPDATE f_mongo_test8 SET b = 'UPDATED' WHERE a = 2;
ERROR: cannot convert BSON type to column type
HINT: Column type "NAME" is compatible only with BSON type "ObjectId".
DELETE FROM f_mongo_test8 WHERE a = 2;
ERROR: cannot convert BSON type to column type
HINT: Column type "NAME" is compatible only with BSON type "ObjectId".
SELECT a, b FROM f_mongo_test8 ORDER BY 1;
a | b
---+------
2 | ROW1
3 | ROW2
(2 rows)
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_mongo_test1;
DROP FOREIGN TABLE f_mongo_test2;
DROP FOREIGN TABLE f_mongo_test3;
DROP FOREIGN TABLE f_mongo_test6;
DROP FOREIGN TABLE f_mongo_test7;
DROP FOREIGN TABLE f_mongo_test8;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/join_pushdown.out 0000664 0000000 0000000 00000300362 15066665201 0021520 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server1;
-- Create foreign tables.
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
INSERT INTO f_test_tbl1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO f_test_tbl1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO f_test_tbl2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO f_test_tbl2 VALUES (0);
-- Create local table.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
-- Push down LEFT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1300 | EMP13 | 3000 | 20
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(20 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | | | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 200 | EMP2 | 1600 | 30
20 | ADMINISTRATION | 300 | EMP3 | 1250 | 30
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 500 | EMP5 | 1250.23 | 30
20 | ADMINISTRATION | 600 | EMP6 | 2850 | 30
20 | ADMINISTRATION | 700 | EMP7 | 2450.34 | 10
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 900 | EMP9 | 5000 | 10
20 | ADMINISTRATION | 1000 | EMP10 | 1500 | 30
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1200 | EMP12 | 950 | 30
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
20 | ADMINISTRATION | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 1500 | EMP15 | 950 | 60
20 | ADMINISTRATION | 1600 | EMP16 | |
30 | SALES | | | |
40 | HR | | | |
50 | TESTING | | | |
(21 rows)
-- Push down RIGHT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(20 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = 20 AND e.c2 = 'EMP1') ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 200 | EMP2 | 1600 | 30
| | 300 | EMP3 | 1250 | 30
| | 400 | EMP4 | 2975 | 20
| | 500 | EMP5 | 1250.23 | 30
| | 600 | EMP6 | 2850 | 30
| | 700 | EMP7 | 2450.34 | 10
| | 800 | EMP8 | 3000 | 20
| | 900 | EMP9 | 5000 | 10
| | 1000 | EMP10 | 1500 | 30
| | 1100 | EMP11 | 1100 | 20
| | 1200 | EMP12 | 950 | 30
| | 1300 | EMP13 | 3000 | 20
| | 1400 | EMP14 | 1300 | 10
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
(16 rows)
-- Push INNER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(9 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 100 | EMP1 | 800.3 | 20
40 | HR | 100 | EMP1 | 800.3 | 20
50 | TESTING | 100 | EMP1 | 800.3 | 20
| | 100 | EMP1 | 800.3 | 20
(10 rows)
-- INNER JOIN with WHERE clause. Should execute where condition separately
-- (NOT added into join clauses) on remote side.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(2 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- INNER JOIN in which join clause is not pushable but WHERE condition is
-- pushable with join clause 'TRUE'.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(3 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: e.c3 DESC NULLS LAST
-> Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
SET mongo_fdw.enable_order_by_pushdown TO ON;
SET enable_mergejoin TO OFF;
SET enable_nestloop TO OFF;
-- Local-Foreign table joins.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Hash Left Join
Hash Cond: (d.c1 = e.c8)
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Hash
-> Seq Scan on l_test_tbl1 e
(8 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
RESET enable_mergejoin;
RESET enable_nestloop;
-- JOIN in sub-query, should be pushed down.
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c1 NULLS FIRST, l.c8 NULLS FIRST
-> Hash Join
Hash Cond: (l.c1 = f1.c1)
-> Seq Scan on l_test_tbl1 l
-> Hash
-> HashAggregate
Group Key: f1.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
(10 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c6 | c8
------+---------+----
100 | 800.3 | 20
200 | 1600 | 30
300 | 1250 | 30
400 | 2975 | 20
500 | 1250.23 | 30
600 | 2850 | 30
700 | 2450.34 | 10
800 | 3000 | 20
900 | 5000 | 10
1000 | 1500 | 30
1100 | 1100 | 20
1200 | 950 | 30
1300 | 3000 | 20
1400 | 1300 | 10
1500 | 950 | 60
1600 | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1 (returns $0)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = $0)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1 (returns $0)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) INNER JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = $0)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
-- Execute JOIN through PREPARE statement.
PREPARE pre_stmt_left_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_left_join;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_left_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(7 rows)
PREPARE pre_stmt_inner_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_inner_join;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_inner_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(6 rows)
-- join + WHERE clause push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+------+-------+---------+----
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(6 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+-----+------+------+----
30 | SALES | 200 | EMP2 | 1600 | 30
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
------+-------+----+----------------+-------+----
100 | EMP1 | 20 | ADMINISTRATION | 800.3 | 20
400 | EMP4 | 20 | ADMINISTRATION | 2975 | 20
800 | EMP8 | 20 | ADMINISTRATION | 3000 | 20
1100 | EMP11 | 20 | ADMINISTRATION | 1100 | 20
1300 | EMP13 | 20 | ADMINISTRATION | 3000 | 20
(5 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, d.c5
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
200 | EMP2 | 02-20-1981 | |
300 | EMP3 | 02-22-1981 | 30 | SALES
400 | EMP4 | 04-02-1981 | |
500 | EMP5 | 09-28-1981 | |
600 | EMP6 | 05-01-1981 | |
700 | EMP7 | 06-09-1981 | |
800 | EMP8 | 04-19-1987 | |
900 | EMP9 | 11-17-1981 | |
1000 | EMP10 | 09-08-1980 | |
1100 | EMP11 | 05-23-1987 | |
1200 | EMP12 | 12-03-1981 | |
1300 | EMP13 | 12-03-1981 | |
1400 | EMP14 | 01-23-1982 | |
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+-------
300 | EMP3 | 02-22-1981 | 30 | SALES
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Filter: ((c1 = 10) OR (c8 = 30))
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(3 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
-- Natural join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
100 | EMP1 | 12-17-1980 | 100 | EMP1
200 | EMP2 | 02-20-1981 | 200 | EMP2
300 | EMP3 | 02-22-1981 | 300 | EMP3
400 | EMP4 | 04-02-1981 | 400 | EMP4
500 | EMP5 | 09-28-1981 | 500 | EMP5
600 | EMP6 | 05-01-1981 | 600 | EMP6
700 | EMP7 | 06-09-1981 | 700 | EMP7
800 | EMP8 | 04-19-1987 | 800 | EMP8
1000 | EMP10 | 09-08-1980 | 1000 | EMP10
1100 | EMP11 | 05-23-1987 | 1100 | EMP11
1200 | EMP12 | 12-03-1981 | 1200 | EMP12
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(14 rows)
-- Self join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
1300 | EMP13 | 12-03-1981 | 1100 | EMP11
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 700 | EMP7
1400 | EMP14 | 01-23-1982 | 900 | EMP9
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(6 rows)
-- Join in CTE.
-- Explain plan difference between v11 (or pre) and later.
EXPLAIN (COSTS false, VERBOSE)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c1, d.c3
Sort Key: d.c3, d.c1
-> Foreign Scan
Output: d.c1, e.c1, d.c3
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(6 rows)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
c1_1 | c2_1
------+------
100 | 20
1100 | 20
1200 | 30
1400 | 10
800 | 20
1300 | 20
900 | 10
400 | 20
600 | 30
700 | 10
200 | 30
300 | 30
500 | 30
1000 | 30
(14 rows)
-- WHERE with boolean expression. Should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 e) INNER JOIN (mongo_fdw_regress.test_tbl1 d)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
300 | EMP3 | 02-22-1981 | 30 | SALES
(2 rows)
-- Nested joins(Don't push-down nested join)
SET enable_mergejoin TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65 ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Hash Left Join
Hash Cond: (e.c1 = f.c8)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
-> Hash
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(7 rows)
RESET enable_mergejoin;
-- Not supported expressions won't push-down(e.g. function expression, etc.)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Merge Left Join
Merge Cond: ((abs(d.c1)) = e.c8)
-> Sort
Sort Key: (abs(d.c1))
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c8
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
| | | | |
(17 rows)
-- Don't pushdown when whole row reference is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT d, e
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY e.c1 OFFSET 65;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Merge Left Join
Merge Cond: (e.c1 = f.c8)
-> Sort
Sort Key: e.c1
-> Hash Left Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: f.c8
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(16 rows)
-- FDW-733: Don't pushdown when whole row reference is involved in the join
-- clause.
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON (test_varchar.*::text) = (f_test_tbl5._id) ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((test_varchar.*)::text)::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((test_varchar.*)::text)::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
-- Don't pushdown when full document retrieval is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: json_data.key COLLATE "C"
-> Nested Loop
-> Nested Loop
-> Foreign Scan on test_text
Foreign Namespace: mongo_fdw_regress.warehouse
-> Function Scan on json_each_text json_data
Filter: (key <> '_id'::text)
-> Materialize
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(11 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 2
warehouse_id | 1
warehouse_id | 1
warehouse_id | 2
warehouse_name | Laptop
warehouse_name | Laptop
warehouse_name | UPS
warehouse_name | UPS
(12 rows)
-- FDW-733: Don't pushdown when full document retrieval is involved in the
-- join clause.
EXPLAIN (COSTS OFF)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: ((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))
-> Merge Join
Merge Cond: ((((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) = f_test_tbl5._id)
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
(12 rows)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
?column?
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
-- Join two tables from two different foreign servers.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl3 e ON d.c1 = e.c1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Merge Left Join
Merge Cond: (d.c1 = e.c1)
-> Sort
Sort Key: d.c1
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c1
-> Foreign Scan on f_test_tbl3 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
-- SEMI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> HashAggregate
Group Key: e.c1
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(12 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP1
EMP10
EMP11
EMP12
EMP13
EMP14
EMP2
EMP3
EMP4
EMP5
(10 rows)
-- ANTI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Anti Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP15
EMP16
(2 rows)
-- FULL OUTER JOIN, should not pushdown.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Full Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c1 | c1
------+----
100 | 20
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
1500 |
1600 |
200 | 30
300 | 30
(10 rows)
-- CROSS JOIN can be pushed down
EXPLAIN (COSTS OFF)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: e.c1, d.c2
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
c1 | c2
----+-------
10 | EMP1
10 | EMP10
10 | EMP11
10 | EMP12
10 | EMP13
10 | EMP14
10 | EMP15
10 | EMP16
10 | EMP2
10 | EMP3
(10 rows)
-- FDW-131: Limit and offset pushdown with join pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
c1 | c1
-----+----
100 | 20
100 | 30
(2 rows)
-- Limit as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Limit as ALL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Offset as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
c1 | c1
-----+----
100 |
100 | 10
100 | 20
(3 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 f_test_tbl1)
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
ERROR: LIMIT must not be negative
-- Test partition-wise join
SET enable_partitionwise_join TO on;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2);
CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3');
CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4');
-- Inner join two tables
-- Different explain plan on v10 as partition-wise join is not supported there.
SET enable_mergejoin TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1
-> Append
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
c1 | c2
----+----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
-- Inner join three tables
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2, t3.c2
Sort Key: t1.c1
-> Append
-> Hash Join
Output: t1_1.c1, t2_1.c2, t3_1.c2
Hash Cond: (t1_1.c1 = t3_1.c1)
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Hash
Output: t3_1.c2, t3_1.c1
-> Foreign Scan on public.ftprt1_p1 t3_1
Output: t3_1.c2, t3_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Hash Join
Output: t1_2.c1, t2_2.c2, t3_2.c2
Hash Cond: (t1_2.c1 = t3_2.c1)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
-> Hash
Output: t3_2.c2, t3_2.c1
-> Foreign Scan on public.ftprt1_p2 t3_2
Output: t3_2.c2, t3_2.c1
Foreign Namespace: mongo_fdw_regress.test2
(26 rows)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
c1 | c2 | c2
----+----+----
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
(8 rows)
RESET enable_mergejoin;
-- Join with lateral reference
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t1.c2
Sort Key: t1.c1, t1.c2
-> Append
-> Foreign Scan
Output: t1_1.c1, t1_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t1_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
c1 | c2
----+----
2 | 2
4 | 4
6 | 6
8 | 8
(4 rows)
-- With PHVs, partitionwise join selected but no join pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
QUERY PLAN
--------------------------------------------------------------------------------
Incremental Sort
Output: fprt1.c1, 't1_phv'::text, fprt2.c2, ('t2_phv'::text)
Sort Key: fprt1.c1, fprt2.c2
Presorted Key: fprt1.c1
-> Merge Append
Sort Key: fprt1.c1
-> Merge Left Join
Output: fprt1_1.c1, 't1_phv'::text, fprt2_1.c2, ('t2_phv'::text)
Merge Cond: (fprt1_1.c1 = fprt2_1.c2)
-> Sort
Output: fprt1_1.c1
Sort Key: fprt1_1.c1
-> Foreign Scan on public.ftprt1_p1 fprt1_1
Output: fprt1_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Sort
Output: fprt2_1.c2, ('t2_phv'::text)
Sort Key: fprt2_1.c2
-> Foreign Scan on public.ftprt2_p1 fprt2_1
Output: fprt2_1.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test3
-> Merge Left Join
Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text)
Merge Cond: (fprt1_2.c1 = fprt2_2.c2)
-> Sort
Output: fprt1_2.c1
Sort Key: fprt1_2.c1
-> Foreign Scan on public.ftprt1_p2 fprt1_2
Output: fprt1_2.c1
Foreign Namespace: mongo_fdw_regress.test2
-> Sort
Output: fprt2_2.c2, ('t2_phv'::text)
Sort Key: fprt2_2.c2
-> Foreign Scan on public.ftprt2_p2 fprt2_2
Output: fprt2_2.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test4
(36 rows)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
c1 | phv | c2 | phv
----+--------+----+--------
2 | t1_phv | 2 | t2_phv
4 | t1_phv | 4 | t2_phv
6 | t1_phv | 6 | t2_phv
8 | t1_phv | 8 | t2_phv
(4 rows)
RESET enable_partitionwise_join;
-- FDW-445: Support enable_join_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'abc11');
ERROR: enable_join_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with outer rel.
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with inner rel.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT t1.c1, t2.c2
FROM f_test_tbl3 t1 JOIN f_test_tbl4 t2 ON (t1.c1 = t2.c8) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1, t2.c2
-> Foreign Scan
Output: t1.c1, t2.c2
Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2)
(6 rows)
-- FDW-558: Test mongo_fdw.enable_join_pushdown GUC.
-- Negative testing for GUC value.
SET mongo_fdw.enable_join_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_join_pushdown" requires a Boolean value
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_join_pushdown;
mongo_fdw.enable_join_pushdown
--------------------------------
on
(1 row)
-- Join pushdown should happen as the GUC enable_join_pushdown is true.
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c8
Sort Key: d.c1
-> Foreign Scan
Output: d.c1, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
--Disable the GUC enable_join_pushdown.
SET mongo_fdw.enable_join_pushdown to false;
-- Join pushdown shouldn't happen as the GUC enable_join_pushdown is false.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- Enable the GUC and table level option is set to false, should not pushdown.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
SET mongo_fdw.enable_join_pushdown to true;
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- One table level option is OFF. Shouldn't pushdown ORDER BY.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
-- When enable_join_pushdown option is disabled. Shouldn't pushdown join and
-- hence, ORDER BY too.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Hash Left Join
Hash Cond: (d.c1 = e.c8)
Join Filter: ((e.c4 > d.c1) AND (e.c2 < d.c3))
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Hash
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- FDW-721: Fix ORDER BY pushdown on the column of inner relation
CREATE FOREIGN TABLE fdw721_tbl1 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl1');
CREATE FOREIGN TABLE fdw721_tbl2 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl2');
INSERT INTO fdw721_tbl1 VALUES(0, 1, 1);
INSERT INTO fdw721_tbl1 VALUES(0, 2, 2);
INSERT INTO fdw721_tbl1 VALUES(0, 3, 3);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 4);
INSERT INTO fdw721_tbl2 VALUES(0, 1, 5);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 6);
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM fdw721_tbl1 t1 LEFT JOIN fdw721_tbl2 t2
ON (t1.c1 = t2.c1) ORDER BY 4 ASC NULLS FIRST;
c1 | c2 | c1 | c2
----+----+----+----
3 | 3 | |
2 | 2 | 2 | 4
1 | 1 | 1 | 5
2 | 2 | 2 | 6
(4 rows)
DELETE FROM f_test_tbl1 WHERE c8 IS NULL;
DELETE FROM f_test_tbl1 WHERE c8 = 60;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DELETE FROM f_test_tbl2 WHERE c1 = 50;
DELETE FROM fdw721_tbl1;
DELETE FROM fdw721_tbl2;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP TABLE l_test_tbl1;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE ftprt2_p1;
DROP FOREIGN TABLE ftprt2_p2;
DROP FOREIGN TABLE fdw721_tbl1;
DROP FOREIGN TABLE fdw721_tbl2;
DROP TABLE IF EXISTS fprt1;
DROP TABLE IF EXISTS fprt2;
DROP USER MAPPING FOR public SERVER mongo_server1;
DROP SERVER mongo_server1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/join_pushdown_2.out 0000664 0000000 0000000 00000300600 15066665201 0021734 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server1;
-- Create foreign tables.
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
INSERT INTO f_test_tbl1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO f_test_tbl1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO f_test_tbl2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO f_test_tbl2 VALUES (0);
-- Create local table.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
-- Push down LEFT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1300 | EMP13 | 3000 | 20
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(20 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | | | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 200 | EMP2 | 1600 | 30
20 | ADMINISTRATION | 300 | EMP3 | 1250 | 30
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 500 | EMP5 | 1250.23 | 30
20 | ADMINISTRATION | 600 | EMP6 | 2850 | 30
20 | ADMINISTRATION | 700 | EMP7 | 2450.34 | 10
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 900 | EMP9 | 5000 | 10
20 | ADMINISTRATION | 1000 | EMP10 | 1500 | 30
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1200 | EMP12 | 950 | 30
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
20 | ADMINISTRATION | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 1500 | EMP15 | 950 | 60
20 | ADMINISTRATION | 1600 | EMP16 | |
30 | SALES | | | |
40 | HR | | | |
50 | TESTING | | | |
(21 rows)
-- Push down RIGHT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(20 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = 20 AND e.c2 = 'EMP1') ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 200 | EMP2 | 1600 | 30
| | 300 | EMP3 | 1250 | 30
| | 400 | EMP4 | 2975 | 20
| | 500 | EMP5 | 1250.23 | 30
| | 600 | EMP6 | 2850 | 30
| | 700 | EMP7 | 2450.34 | 10
| | 800 | EMP8 | 3000 | 20
| | 900 | EMP9 | 5000 | 10
| | 1000 | EMP10 | 1500 | 30
| | 1100 | EMP11 | 1100 | 20
| | 1200 | EMP12 | 950 | 30
| | 1300 | EMP13 | 3000 | 20
| | 1400 | EMP14 | 1300 | 10
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
(16 rows)
-- Push INNER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(9 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 100 | EMP1 | 800.3 | 20
40 | HR | 100 | EMP1 | 800.3 | 20
50 | TESTING | 100 | EMP1 | 800.3 | 20
| | 100 | EMP1 | 800.3 | 20
(10 rows)
-- INNER JOIN with WHERE clause. Should execute where condition separately
-- (NOT added into join clauses) on remote side.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(2 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- INNER JOIN in which join clause is not pushable but WHERE condition is
-- pushable with join clause 'TRUE'.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(3 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: e.c3 DESC NULLS LAST
-> Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
SET mongo_fdw.enable_order_by_pushdown TO ON;
SET enable_mergejoin TO OFF;
SET enable_nestloop TO OFF;
-- Local-Foreign table joins.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Hash Left Join
Hash Cond: (d.c1 = e.c8)
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Hash
-> Seq Scan on l_test_tbl1 e
(8 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
RESET enable_mergejoin;
RESET enable_nestloop;
-- JOIN in sub-query, should be pushed down.
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c1 NULLS FIRST, l.c8 NULLS FIRST
-> Hash Join
Hash Cond: (l.c1 = f1.c1)
-> Seq Scan on l_test_tbl1 l
-> Hash
-> HashAggregate
Group Key: f1.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
(10 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c6 | c8
------+---------+----
100 | 800.3 | 20
200 | 1600 | 30
300 | 1250 | 30
400 | 2975 | 20
500 | 1250.23 | 30
600 | 2850 | 30
700 | 2450.34 | 10
800 | 3000 | 20
900 | 5000 | 10
1000 | 1500 | 30
1100 | 1100 | 20
1200 | 950 | 30
1300 | 3000 | 20
1400 | 1300 | 10
1500 | 950 | 60
1600 | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = (InitPlan 1).col1)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) INNER JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = (InitPlan 1).col1)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
-- Execute JOIN through PREPARE statement.
PREPARE pre_stmt_left_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_left_join;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_left_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(7 rows)
PREPARE pre_stmt_inner_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_inner_join;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_inner_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(6 rows)
-- join + WHERE clause push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+------+-------+---------+----
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(6 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+-----+------+------+----
30 | SALES | 200 | EMP2 | 1600 | 30
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
------+-------+----+----------------+-------+----
100 | EMP1 | 20 | ADMINISTRATION | 800.3 | 20
400 | EMP4 | 20 | ADMINISTRATION | 2975 | 20
800 | EMP8 | 20 | ADMINISTRATION | 3000 | 20
1100 | EMP11 | 20 | ADMINISTRATION | 1100 | 20
1300 | EMP13 | 20 | ADMINISTRATION | 3000 | 20
(5 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, d.c5
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
200 | EMP2 | 02-20-1981 | |
300 | EMP3 | 02-22-1981 | 30 | SALES
400 | EMP4 | 04-02-1981 | |
500 | EMP5 | 09-28-1981 | |
600 | EMP6 | 05-01-1981 | |
700 | EMP7 | 06-09-1981 | |
800 | EMP8 | 04-19-1987 | |
900 | EMP9 | 11-17-1981 | |
1000 | EMP10 | 09-08-1980 | |
1100 | EMP11 | 05-23-1987 | |
1200 | EMP12 | 12-03-1981 | |
1300 | EMP13 | 12-03-1981 | |
1400 | EMP14 | 01-23-1982 | |
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+-------
300 | EMP3 | 02-22-1981 | 30 | SALES
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Filter: ((c1 = 10) OR (c8 = 30))
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(3 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
-- Natural join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
100 | EMP1 | 12-17-1980 | 100 | EMP1
200 | EMP2 | 02-20-1981 | 200 | EMP2
300 | EMP3 | 02-22-1981 | 300 | EMP3
400 | EMP4 | 04-02-1981 | 400 | EMP4
500 | EMP5 | 09-28-1981 | 500 | EMP5
600 | EMP6 | 05-01-1981 | 600 | EMP6
700 | EMP7 | 06-09-1981 | 700 | EMP7
800 | EMP8 | 04-19-1987 | 800 | EMP8
1000 | EMP10 | 09-08-1980 | 1000 | EMP10
1100 | EMP11 | 05-23-1987 | 1100 | EMP11
1200 | EMP12 | 12-03-1981 | 1200 | EMP12
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(14 rows)
-- Self join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
1300 | EMP13 | 12-03-1981 | 1100 | EMP11
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 700 | EMP7
1400 | EMP14 | 01-23-1982 | 900 | EMP9
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(6 rows)
-- Join in CTE.
-- Explain plan difference between v11 (or pre) and later.
EXPLAIN (COSTS false, VERBOSE)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c1, d.c3
Sort Key: d.c3, d.c1
-> Foreign Scan
Output: d.c1, e.c1, d.c3
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(6 rows)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
c1_1 | c2_1
------+------
100 | 20
1100 | 20
1200 | 30
1400 | 10
800 | 20
1300 | 20
900 | 10
400 | 20
600 | 30
700 | 10
200 | 30
300 | 30
500 | 30
1000 | 30
(14 rows)
-- WHERE with boolean expression. Should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 e) INNER JOIN (mongo_fdw_regress.test_tbl1 d)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
300 | EMP3 | 02-22-1981 | 30 | SALES
(2 rows)
-- Nested joins(Don't push-down nested join)
SET enable_mergejoin TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65 ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Hash Left Join
Hash Cond: (e.c1 = f.c8)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
-> Hash
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(7 rows)
RESET enable_mergejoin;
-- Not supported expressions won't push-down(e.g. function expression, etc.)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Merge Left Join
Merge Cond: ((abs(d.c1)) = e.c8)
-> Sort
Sort Key: (abs(d.c1))
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c8
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
| | | | |
(17 rows)
-- Don't pushdown when whole row reference is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT d, e
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY e.c1 OFFSET 65;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Merge Left Join
Merge Cond: (e.c1 = f.c8)
-> Sort
Sort Key: e.c1
-> Hash Left Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: f.c8
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(16 rows)
-- FDW-733: Don't pushdown when whole row reference is involved in the join
-- clause.
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON (test_varchar.*::text) = (f_test_tbl5._id) ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((test_varchar.*)::text)::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((test_varchar.*)::text)::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
-- Don't pushdown when full document retrieval is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: json_data.key COLLATE "C"
-> Nested Loop
-> Nested Loop
-> Foreign Scan on test_text
Foreign Namespace: mongo_fdw_regress.warehouse
-> Function Scan on json_each_text json_data
Filter: (key <> '_id'::text)
-> Materialize
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(11 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 2
warehouse_id | 1
warehouse_id | 1
warehouse_id | 2
warehouse_name | Laptop
warehouse_name | Laptop
warehouse_name | UPS
warehouse_name | UPS
(12 rows)
-- FDW-733: Don't pushdown when full document retrieval is involved in the
-- join clause.
EXPLAIN (COSTS OFF)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: ((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))
-> Merge Join
Merge Cond: ((((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) = f_test_tbl5._id)
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
(12 rows)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
?column?
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
-- Join two tables from two different foreign servers.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl3 e ON d.c1 = e.c1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Merge Left Join
Merge Cond: (d.c1 = e.c1)
-> Sort
Sort Key: d.c1
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c1
-> Foreign Scan on f_test_tbl3 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
-- SEMI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> HashAggregate
Group Key: e.c1
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(12 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP1
EMP10
EMP11
EMP12
EMP13
EMP14
EMP2
EMP3
EMP4
EMP5
(10 rows)
-- ANTI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Anti Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP15
EMP16
(2 rows)
-- FULL OUTER JOIN, should not pushdown.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Full Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c1 | c1
------+----
100 | 20
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
1500 |
1600 |
200 | 30
300 | 30
(10 rows)
-- CROSS JOIN can be pushed down
EXPLAIN (COSTS OFF)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: e.c1, d.c2
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
c1 | c2
----+-------
10 | EMP1
10 | EMP10
10 | EMP11
10 | EMP12
10 | EMP13
10 | EMP14
10 | EMP15
10 | EMP16
10 | EMP2
10 | EMP3
(10 rows)
-- FDW-131: Limit and offset pushdown with join pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
c1 | c1
-----+----
100 | 20
100 | 30
(2 rows)
-- Limit as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Limit as ALL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Offset as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
c1 | c1
-----+----
100 |
100 | 10
100 | 20
(3 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 f_test_tbl1)
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
ERROR: LIMIT must not be negative
-- Test partition-wise join
SET enable_partitionwise_join TO on;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2);
CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3');
CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4');
-- Inner join two tables
-- Different explain plan on v10 as partition-wise join is not supported there.
SET enable_mergejoin TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1
-> Append
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
c1 | c2
----+----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
-- Inner join three tables
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2, t3.c2
Sort Key: t1.c1
-> Append
-> Hash Join
Output: t1_1.c1, t2_1.c2, t3_1.c2
Hash Cond: (t1_1.c1 = t3_1.c1)
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Hash
Output: t3_1.c2, t3_1.c1
-> Foreign Scan on public.ftprt1_p1 t3_1
Output: t3_1.c2, t3_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Hash Join
Output: t1_2.c1, t2_2.c2, t3_2.c2
Hash Cond: (t1_2.c1 = t3_2.c1)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
-> Hash
Output: t3_2.c2, t3_2.c1
-> Foreign Scan on public.ftprt1_p2 t3_2
Output: t3_2.c2, t3_2.c1
Foreign Namespace: mongo_fdw_regress.test2
(26 rows)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
c1 | c2 | c2
----+----+----
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
(8 rows)
RESET enable_mergejoin;
-- Join with lateral reference
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t1.c2
Sort Key: t1.c1, t1.c2
-> Append
-> Foreign Scan
Output: t1_1.c1, t1_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t1_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
c1 | c2
----+----
2 | 2
4 | 4
6 | 6
8 | 8
(4 rows)
-- With PHVs, partitionwise join selected but no join pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
QUERY PLAN
--------------------------------------------------------------------------------
Incremental Sort
Output: fprt1.c1, 't1_phv'::text, fprt2.c2, ('t2_phv'::text)
Sort Key: fprt1.c1, fprt2.c2
Presorted Key: fprt1.c1
-> Merge Append
Sort Key: fprt1.c1
-> Merge Left Join
Output: fprt1_1.c1, 't1_phv'::text, fprt2_1.c2, ('t2_phv'::text)
Merge Cond: (fprt1_1.c1 = fprt2_1.c2)
-> Sort
Output: fprt1_1.c1
Sort Key: fprt1_1.c1
-> Foreign Scan on public.ftprt1_p1 fprt1_1
Output: fprt1_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Sort
Output: fprt2_1.c2, ('t2_phv'::text)
Sort Key: fprt2_1.c2
-> Foreign Scan on public.ftprt2_p1 fprt2_1
Output: fprt2_1.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test3
-> Merge Left Join
Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text)
Merge Cond: (fprt1_2.c1 = fprt2_2.c2)
-> Sort
Output: fprt1_2.c1
Sort Key: fprt1_2.c1
-> Foreign Scan on public.ftprt1_p2 fprt1_2
Output: fprt1_2.c1
Foreign Namespace: mongo_fdw_regress.test2
-> Sort
Output: fprt2_2.c2, ('t2_phv'::text)
Sort Key: fprt2_2.c2
-> Foreign Scan on public.ftprt2_p2 fprt2_2
Output: fprt2_2.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test4
(36 rows)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
c1 | phv | c2 | phv
----+--------+----+--------
2 | t1_phv | 2 | t2_phv
4 | t1_phv | 4 | t2_phv
6 | t1_phv | 6 | t2_phv
8 | t1_phv | 8 | t2_phv
(4 rows)
RESET enable_partitionwise_join;
-- FDW-445: Support enable_join_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'abc11');
ERROR: enable_join_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with outer rel.
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with inner rel.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT t1.c1, t2.c2
FROM f_test_tbl3 t1 JOIN f_test_tbl4 t2 ON (t1.c1 = t2.c8) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1, t2.c2
-> Foreign Scan
Output: t1.c1, t2.c2
Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2)
(6 rows)
-- FDW-558: Test mongo_fdw.enable_join_pushdown GUC.
-- Negative testing for GUC value.
SET mongo_fdw.enable_join_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_join_pushdown" requires a Boolean value
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_join_pushdown;
mongo_fdw.enable_join_pushdown
--------------------------------
on
(1 row)
-- Join pushdown should happen as the GUC enable_join_pushdown is true.
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c8
Sort Key: d.c1
-> Foreign Scan
Output: d.c1, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
--Disable the GUC enable_join_pushdown.
SET mongo_fdw.enable_join_pushdown to false;
-- Join pushdown shouldn't happen as the GUC enable_join_pushdown is false.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- Enable the GUC and table level option is set to false, should not pushdown.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
SET mongo_fdw.enable_join_pushdown to true;
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- One table level option is OFF. Shouldn't pushdown ORDER BY.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
-- When enable_join_pushdown option is disabled. Shouldn't pushdown join and
-- hence, ORDER BY too.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
Presorted Key: d.c1
-> Merge Left Join
Merge Cond: (d.c1 = e.c8)
Join Filter: ((e.c4 > d.c1) AND (e.c2 < d.c3))
-> Sort
Sort Key: d.c1 NULLS FIRST
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c8 NULLS FIRST
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- FDW-721: Fix ORDER BY pushdown on the column of inner relation
CREATE FOREIGN TABLE fdw721_tbl1 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl1');
CREATE FOREIGN TABLE fdw721_tbl2 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl2');
INSERT INTO fdw721_tbl1 VALUES(0, 1, 1);
INSERT INTO fdw721_tbl1 VALUES(0, 2, 2);
INSERT INTO fdw721_tbl1 VALUES(0, 3, 3);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 4);
INSERT INTO fdw721_tbl2 VALUES(0, 1, 5);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 6);
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM fdw721_tbl1 t1 LEFT JOIN fdw721_tbl2 t2
ON (t1.c1 = t2.c1) ORDER BY 4 ASC NULLS FIRST;
c1 | c2 | c1 | c2
----+----+----+----
3 | 3 | |
2 | 2 | 2 | 4
1 | 1 | 1 | 5
2 | 2 | 2 | 6
(4 rows)
DELETE FROM f_test_tbl1 WHERE c8 IS NULL;
DELETE FROM f_test_tbl1 WHERE c8 = 60;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DELETE FROM f_test_tbl2 WHERE c1 = 50;
DELETE FROM fdw721_tbl1;
DELETE FROM fdw721_tbl2;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP TABLE l_test_tbl1;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE ftprt2_p1;
DROP FOREIGN TABLE ftprt2_p2;
DROP FOREIGN TABLE fdw721_tbl1;
DROP FOREIGN TABLE fdw721_tbl2;
DROP TABLE IF EXISTS fprt1;
DROP TABLE IF EXISTS fprt2;
DROP USER MAPPING FOR public SERVER mongo_server1;
DROP SERVER mongo_server1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/join_pushdown_3.out 0000664 0000000 0000000 00000300611 15066665201 0021737 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server1;
-- Create foreign tables.
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
INSERT INTO f_test_tbl1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO f_test_tbl1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO f_test_tbl2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO f_test_tbl2 VALUES (0);
-- Create local table.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
-- Push down LEFT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1300 | EMP13 | 3000 | 20
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(20 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | | | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 200 | EMP2 | 1600 | 30
20 | ADMINISTRATION | 300 | EMP3 | 1250 | 30
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 500 | EMP5 | 1250.23 | 30
20 | ADMINISTRATION | 600 | EMP6 | 2850 | 30
20 | ADMINISTRATION | 700 | EMP7 | 2450.34 | 10
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 900 | EMP9 | 5000 | 10
20 | ADMINISTRATION | 1000 | EMP10 | 1500 | 30
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1200 | EMP12 | 950 | 30
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
20 | ADMINISTRATION | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 1500 | EMP15 | 950 | 60
20 | ADMINISTRATION | 1600 | EMP16 | |
30 | SALES | | | |
40 | HR | | | |
50 | TESTING | | | |
(21 rows)
-- Push down RIGHT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(20 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = 20 AND e.c2 = 'EMP1') ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | 200 | EMP2 | 1600 | 30
| | 300 | EMP3 | 1250 | 30
| | 400 | EMP4 | 2975 | 20
| | 500 | EMP5 | 1250.23 | 30
| | 600 | EMP6 | 2850 | 30
| | 700 | EMP7 | 2450.34 | 10
| | 800 | EMP8 | 3000 | 20
| | 900 | EMP9 | 5000 | 10
| | 1000 | EMP10 | 1500 | 30
| | 1100 | EMP11 | 1100 | 20
| | 1200 | EMP12 | 950 | 30
| | 1300 | EMP13 | 3000 | 20
| | 1400 | EMP14 | 1300 | 10
| | 1500 | EMP15 | 950 | 60
| | 1600 | EMP16 | |
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
(16 rows)
-- Push INNER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+---------+----
40 | HR | 1400 | EMP14 | 1300 | 10
40 | HR | 1500 | EMP15 | 950 | 60
40 | HR | 1600 | EMP16 | |
50 | TESTING | 100 | EMP1 | 800.3 | 20
50 | TESTING | 200 | EMP2 | 1600 | 30
50 | TESTING | 300 | EMP3 | 1250 | 30
50 | TESTING | 400 | EMP4 | 2975 | 20
50 | TESTING | 500 | EMP5 | 1250.23 | 30
50 | TESTING | 600 | EMP6 | 2850 | 30
50 | TESTING | 700 | EMP7 | 2450.34 | 10
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(19 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 800 | EMP8 | 3000 | 20
50 | TESTING | 900 | EMP9 | 5000 | 10
50 | TESTING | 1000 | EMP10 | 1500 | 30
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(9 rows)
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 100 | EMP1 | 800.3 | 20
40 | HR | 100 | EMP1 | 800.3 | 20
50 | TESTING | 100 | EMP1 | 800.3 | 20
| | 100 | EMP1 | 800.3 | 20
(10 rows)
-- INNER JOIN with WHERE clause. Should execute where condition separately
-- (NOT added into join clauses) on remote side.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(2 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- INNER JOIN in which join clause is not pushable but WHERE condition is
-- pushable with join clause 'TRUE'.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------
Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(3 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: e.c3 DESC NULLS LAST
-> Foreign Scan
Filter: (abs(c8) = c1)
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
c1 | c1
-----+----
100 | 20
(1 row)
SET mongo_fdw.enable_order_by_pushdown TO ON;
SET enable_mergejoin TO OFF;
SET enable_nestloop TO OFF;
-- Local-Foreign table joins.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Hash Left Join
Hash Cond: (d.c1 = e.c8)
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Hash
-> Seq Scan on l_test_tbl1 e
(8 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(17 rows)
RESET enable_mergejoin;
RESET enable_nestloop;
-- JOIN in sub-query, should be pushed down.
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c1 NULLS FIRST, l.c8 NULLS FIRST
-> Hash Join
Hash Cond: (l.c1 = f1.c1)
-> Seq Scan on l_test_tbl1 l
-> Hash
-> HashAggregate
Group Key: f1.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
(10 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c6 | c8
------+---------+----
100 | 800.3 | 20
200 | 1600 | 30
300 | 1250 | 30
400 | 2975 | 20
500 | 1250.23 | 30
600 | 2850 | 30
700 | 2450.34 | 10
800 | 3000 | 20
900 | 5000 | 10
1000 | 1500 | 30
1100 | 1100 | 20
1200 | 950 | 30
1300 | 3000 | 20
1400 | 1300 | 10
1500 | 950 | 60
1600 | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1 (returns $0)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = $0)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------
Sort
Sort Key: l.c8
InitPlan 1 (returns $0)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) INNER JOIN (mongo_fdw_regress.test_tbl2 f2)
-> Seq Scan on l_test_tbl1 l
Filter: (c1 = $0)
(7 rows)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
c1 | c6 | c8
-----+-------+----
100 | 800.3 | 20
(1 row)
-- Execute JOIN through PREPARE statement.
PREPARE pre_stmt_left_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_left_join;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_left_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
| | | | |
(7 rows)
PREPARE pre_stmt_inner_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_inner_join;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
EXECUTE pre_stmt_inner_join;
c1 | c2 | c1 | c2 | c6 | c8
----+---------+------+-------+------+----
50 | TESTING | 1100 | EMP11 | 1100 | 20
50 | TESTING | 1200 | EMP12 | 950 | 30
50 | TESTING | 1300 | EMP13 | 3000 | 20
50 | TESTING | 1400 | EMP14 | 1300 | 10
50 | TESTING | 1500 | EMP15 | 950 | 60
50 | TESTING | 1600 | EMP16 | |
(6 rows)
-- join + WHERE clause push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 e) LEFT JOIN (mongo_fdw_regress.test_tbl2 d)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
(3 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+------+-------+---------+----
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(6 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------+-----+------+------+----
30 | SALES | 200 | EMP2 | 1600 | 30
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
------+-------+----+----------------+-------+----
100 | EMP1 | 20 | ADMINISTRATION | 800.3 | 20
400 | EMP4 | 20 | ADMINISTRATION | 2975 | 20
800 | EMP8 | 20 | ADMINISTRATION | 3000 | 20
1100 | EMP11 | 20 | ADMINISTRATION | 1100 | 20
1300 | EMP13 | 20 | ADMINISTRATION | 3000 | 20
(5 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1, d.c5
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
200 | EMP2 | 02-20-1981 | |
300 | EMP3 | 02-22-1981 | 30 | SALES
400 | EMP4 | 04-02-1981 | |
500 | EMP5 | 09-28-1981 | |
600 | EMP6 | 05-01-1981 | |
700 | EMP7 | 06-09-1981 | |
800 | EMP8 | 04-19-1987 | |
900 | EMP9 | 11-17-1981 | |
1000 | EMP10 | 09-08-1980 | |
1100 | EMP11 | 05-23-1987 | |
1200 | EMP12 | 12-03-1981 | |
1300 | EMP13 | 12-03-1981 | |
1400 | EMP14 | 01-23-1982 | |
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(16 rows)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+-------
300 | EMP3 | 02-22-1981 | 30 | SALES
(1 row)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Filter: ((c1 = 10) OR (c8 = 30))
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(3 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
c1 | c2 | c1 | c2 | c6 | c8
----+-------------+-----+------+-------+----
10 | DEVELOPMENT | 100 | EMP1 | 800.3 | 20
(1 row)
-- Natural join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
100 | EMP1 | 12-17-1980 | 100 | EMP1
200 | EMP2 | 02-20-1981 | 200 | EMP2
300 | EMP3 | 02-22-1981 | 300 | EMP3
400 | EMP4 | 04-02-1981 | 400 | EMP4
500 | EMP5 | 09-28-1981 | 500 | EMP5
600 | EMP6 | 05-01-1981 | 600 | EMP6
700 | EMP7 | 06-09-1981 | 700 | EMP7
800 | EMP8 | 04-19-1987 | 800 | EMP8
1000 | EMP10 | 09-08-1980 | 1000 | EMP10
1100 | EMP11 | 05-23-1987 | 1100 | EMP11
1200 | EMP12 | 12-03-1981 | 1200 | EMP12
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(14 rows)
-- Self join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(5 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+------+-------
1300 | EMP13 | 12-03-1981 | 1100 | EMP11
1300 | EMP13 | 12-03-1981 | 1300 | EMP13
1400 | EMP14 | 01-23-1982 | 700 | EMP7
1400 | EMP14 | 01-23-1982 | 900 | EMP9
1400 | EMP14 | 01-23-1982 | 1400 | EMP14
1500 | EMP15 | 12-25-2000 | 1500 | EMP15
(6 rows)
-- Join in CTE.
-- Explain plan difference between v11 (or pre) and later.
EXPLAIN (COSTS false, VERBOSE)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c1, d.c3
Sort Key: d.c3, d.c1
-> Foreign Scan
Output: d.c1, e.c1, d.c3
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(6 rows)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
c1_1 | c2_1
------+------
100 | 20
1100 | 20
1200 | 30
1400 | 10
800 | 20
1300 | 20
900 | 10
400 | 20
600 | 30
700 | 10
200 | 30
300 | 30
500 | 30
1000 | 30
(14 rows)
-- WHERE with boolean expression. Should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 e) INNER JOIN (mongo_fdw_regress.test_tbl1 d)
(4 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
c1 | c2 | c5 | c1 | c2
-----+------+------------+----+----------------
100 | EMP1 | 12-17-1980 | 20 | ADMINISTRATION
300 | EMP3 | 02-22-1981 | 30 | SALES
(2 rows)
-- Nested joins(Don't push-down nested join)
SET enable_mergejoin TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65 ;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c1
-> Hash Left Join
Hash Cond: (e.c1 = f.c8)
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) LEFT JOIN (mongo_fdw_regress.test_tbl2 e)
-> Hash
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(10 rows)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65;
c1 | c2 | c5 | c1 | c2
------+-------+------------+----+----------------
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1300 | EMP13 | 12-03-1981 | 20 | ADMINISTRATION
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1400 | EMP14 | 01-23-1982 | 10 | DEVELOPMENT
1500 | EMP15 | 12-25-2000 | |
1600 | EMP16 | | |
(7 rows)
RESET enable_mergejoin;
-- Not supported expressions won't push-down(e.g. function expression, etc.)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: d.c1, e.c1
-> Merge Left Join
Merge Cond: ((abs(d.c1)) = e.c8)
-> Sort
Sort Key: (abs(d.c1))
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c8
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(12 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
| | | | |
(17 rows)
-- Don't pushdown when whole row reference is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT d, e
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY e.c1 OFFSET 65;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Merge Left Join
Merge Cond: (e.c1 = f.c8)
-> Sort
Sort Key: e.c1
-> Hash Left Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: f.c8
-> Foreign Scan on f_test_tbl1 f
Foreign Namespace: mongo_fdw_regress.test_tbl1
(16 rows)
-- FDW-733: Don't pushdown when whole row reference is involved in the join
-- clause.
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON (test_varchar.*::text) = (f_test_tbl5._id) ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((test_varchar.*)::text)::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((test_varchar.*)::text)::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
-- Don't pushdown when full document retrieval is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
QUERY PLAN
--------------------------------------------------------------------
Sort
Sort Key: json_data.key COLLATE "C"
-> Nested Loop
-> Nested Loop
-> Foreign Scan on test_text
Foreign Namespace: mongo_fdw_regress.warehouse
-> Function Scan on json_each_text json_data
Filter: (key <> '_id'::text)
-> Materialize
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(11 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 2
warehouse_id | 1
warehouse_id | 1
warehouse_id | 2
warehouse_name | Laptop
warehouse_name | Laptop
warehouse_name | UPS
warehouse_name | UPS
(12 rows)
-- FDW-733: Don't pushdown when full document retrieval is involved in the
-- join clause.
EXPLAIN (COSTS OFF)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Sort
Sort Key: ((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))
-> Merge Join
Merge Cond: ((((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) = f_test_tbl5._id)
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
(12 rows)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
?column?
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Merge Join
Merge Cond: (f_test_tbl5._id = (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text))
-> Sort
Sort Key: f_test_tbl5._id
-> Foreign Scan on f_test_tbl5
Foreign Namespace: mongo_fdw_regress.warehouse
-> Sort
Sort Key: (((((test_varchar.__doc)::json -> '_id'::text) ->> '$oid'::text))::text) COLLATE "C"
-> Foreign Scan on test_varchar
Foreign Namespace: mongo_fdw_regress.warehouse
(10 rows)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
_id
--------------------------
58a1ebbaf543ec0b90545859
58a1ebbaf543ec0b9054585a
(2 rows)
-- Join two tables from two different foreign servers.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl3 e ON d.c1 = e.c1 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------------
Merge Left Join
Merge Cond: (d.c1 = e.c1)
-> Sort
Sort Key: d.c1
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c1
-> Foreign Scan on f_test_tbl3 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
-- SEMI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> HashAggregate
Group Key: e.c1
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(12 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP1
EMP10
EMP11
EMP12
EMP13
EMP14
EMP2
EMP3
EMP4
EMP5
(10 rows)
-- ANTI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Anti Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c2
-------
EMP15
EMP16
(2 rows)
-- FULL OUTER JOIN, should not pushdown.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
QUERY PLAN
--------------------------------------------------------------------------
Limit
-> Sort
Sort Key: d.c2
-> Hash Full Join
Hash Cond: (d.c8 = e.c1)
-> Foreign Scan on f_test_tbl1 d
Foreign Namespace: mongo_fdw_regress.test_tbl1
-> Hash
-> Foreign Scan on f_test_tbl2 e
Foreign Namespace: mongo_fdw_regress.test_tbl2
(10 rows)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
c1 | c1
------+----
100 | 20
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
1500 |
1600 |
200 | 30
300 | 30
(10 rows)
-- CROSS JOIN can be pushed down
EXPLAIN (COSTS OFF)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Limit
-> Sort
Sort Key: e.c1, d.c2
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e)
(5 rows)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
c1 | c2
----+-------
10 | EMP1
10 | EMP10
10 | EMP11
10 | EMP12
10 | EMP13
10 | EMP14
10 | EMP15
10 | EMP16
10 | EMP2
10 | EMP3
(10 rows)
-- FDW-131: Limit and offset pushdown with join pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
c1 | c1
-----+----
100 | 20
100 | 30
(2 rows)
-- Limit as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Limit as ALL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
c1 | c1
------+----
200 | 30
300 | 30
400 | 20
500 | 30
600 | 30
700 | 10
800 | 20
900 | 10
1000 | 30
1100 | 20
1200 | 30
1300 | 20
1400 | 10
(13 rows)
-- Offset as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(3 rows)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
c1 | c1
-----+----
100 |
100 | 10
100 | 20
(3 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(5 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Limit
Output: t1.c1, t2.c1
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 f_test_tbl1)
-> Foreign Scan
Output: t1.c1, t2.c1
Foreign Namespace: (mongo_fdw_regress.test_tbl1 t1) INNER JOIN (mongo_fdw_regress.test_tbl2 t2)
(9 rows)
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
ERROR: LIMIT must not be negative
-- Test partition-wise join
SET enable_partitionwise_join TO on;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2);
CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3');
CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4');
-- Inner join two tables
-- Different explain plan on v10 as partition-wise join is not supported there.
SET enable_mergejoin TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1
-> Append
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
c1 | c2
----+----
1 | 1
2 | 2
3 | 3
4 | 4
5 | 5
6 | 6
7 | 7
8 | 8
(8 rows)
-- Inner join three tables
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2, t3.c2
Sort Key: t1.c1
-> Append
-> Hash Join
Output: t1_1.c1, t2_1.c2, t3_1.c2
Hash Cond: (t1_1.c1 = t3_1.c1)
-> Foreign Scan
Output: t1_1.c1, t2_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Hash
Output: t3_1.c2, t3_1.c1
-> Foreign Scan on public.ftprt1_p1 t3_1
Output: t3_1.c2, t3_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Hash Join
Output: t1_2.c1, t2_2.c2, t3_2.c2
Hash Cond: (t1_2.c1 = t3_2.c1)
-> Foreign Scan
Output: t1_2.c1, t2_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
-> Hash
Output: t3_2.c2, t3_2.c1
-> Foreign Scan on public.ftprt1_p2 t3_2
Output: t3_2.c2, t3_2.c1
Foreign Namespace: mongo_fdw_regress.test2
(26 rows)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
c1 | c2 | c2
----+----+----
1 | 1 | 1
2 | 2 | 2
3 | 3 | 3
4 | 4 | 4
5 | 5 | 5
6 | 6 | 6
7 | 7 | 7
8 | 8 | 8
(8 rows)
RESET enable_mergejoin;
-- Join with lateral reference
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t1.c2
Sort Key: t1.c1, t1.c2
-> Append
-> Foreign Scan
Output: t1_1.c1, t1_1.c2
Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2)
-> Foreign Scan
Output: t1_2.c1, t1_2.c2
Foreign Namespace: (mongo_fdw_regress.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2)
(10 rows)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
c1 | c2
----+----
2 | 2
4 | 4
6 | 6
8 | 8
(4 rows)
-- With PHVs, partitionwise join selected but no join pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
QUERY PLAN
--------------------------------------------------------------------------------
Incremental Sort
Output: fprt1.c1, 't1_phv'::text, fprt2.c2, ('t2_phv'::text)
Sort Key: fprt1.c1, fprt2.c2
Presorted Key: fprt1.c1
-> Merge Append
Sort Key: fprt1.c1
-> Merge Left Join
Output: fprt1_1.c1, 't1_phv'::text, fprt2_1.c2, ('t2_phv'::text)
Merge Cond: (fprt1_1.c1 = fprt2_1.c2)
-> Sort
Output: fprt1_1.c1
Sort Key: fprt1_1.c1
-> Foreign Scan on public.ftprt1_p1 fprt1_1
Output: fprt1_1.c1
Foreign Namespace: mongo_fdw_regress.test1
-> Sort
Output: fprt2_1.c2, ('t2_phv'::text)
Sort Key: fprt2_1.c2
-> Foreign Scan on public.ftprt2_p1 fprt2_1
Output: fprt2_1.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test3
-> Merge Left Join
Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text)
Merge Cond: (fprt1_2.c1 = fprt2_2.c2)
-> Sort
Output: fprt1_2.c1
Sort Key: fprt1_2.c1
-> Foreign Scan on public.ftprt1_p2 fprt1_2
Output: fprt1_2.c1
Foreign Namespace: mongo_fdw_regress.test2
-> Sort
Output: fprt2_2.c2, ('t2_phv'::text)
Sort Key: fprt2_2.c2
-> Foreign Scan on public.ftprt2_p2 fprt2_2
Output: fprt2_2.c2, 't2_phv'::text
Foreign Namespace: mongo_fdw_regress.test4
(36 rows)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
c1 | phv | c2 | phv
----+--------+----+--------
2 | t1_phv | 2 | t2_phv
4 | t1_phv | 4 | t2_phv
6 | t1_phv | 6 | t2_phv
8 | t1_phv | 8 | t2_phv
(4 rows)
RESET enable_partitionwise_join;
-- FDW-445: Support enable_join_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'abc11');
ERROR: enable_join_pushdown requires a Boolean value
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with outer rel.
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test the option with inner rel.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
-> Foreign Scan
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Sort Key: d.c1, e.c1
Presorted Key: d.c1
-> Merge Join
Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1, d.c2
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1, d.c2
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c1, e.c2, e.c6, e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.c2, e.c6, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(19 rows)
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT t1.c1, t2.c2
FROM f_test_tbl3 t1 JOIN f_test_tbl4 t2 ON (t1.c1 = t2.c8) ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------------------------------------------------------
Sort
Output: t1.c1, t2.c2
Sort Key: t1.c1, t2.c2
-> Foreign Scan
Output: t1.c1, t2.c2
Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2)
(6 rows)
-- FDW-558: Test mongo_fdw.enable_join_pushdown GUC.
-- Negative testing for GUC value.
SET mongo_fdw.enable_join_pushdown to 'abc';
ERROR: parameter "mongo_fdw.enable_join_pushdown" requires a Boolean value
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_join_pushdown;
mongo_fdw.enable_join_pushdown
--------------------------------
on
(1 row)
-- Join pushdown should happen as the GUC enable_join_pushdown is true.
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Sort
Output: d.c1, e.c8
Sort Key: d.c1
-> Foreign Scan
Output: d.c1, e.c8
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) INNER JOIN (mongo_fdw_regress.test_tbl1 e)
(6 rows)
--Disable the GUC enable_join_pushdown.
SET mongo_fdw.enable_join_pushdown to false;
-- Join pushdown shouldn't happen as the GUC enable_join_pushdown is false.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- Enable the GUC and table level option is set to false, should not pushdown.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
SET mongo_fdw.enable_join_pushdown to true;
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
QUERY PLAN
--------------------------------------------------------------
Merge Join
Output: d.c1, e.c8
Merge Cond: (d.c1 = e.c8)
-> Sort
Output: d.c1
Sort Key: d.c1
-> Foreign Scan on public.f_test_tbl2 d
Output: d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Output: e.c8
Sort Key: e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(15 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- One table level option is OFF. Shouldn't pushdown ORDER BY.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
-> Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(4 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan
Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e)
(2 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
-- When enable_join_pushdown option is disabled. Shouldn't pushdown join and
-- hence, ORDER BY too.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------------------
Incremental Sort
Sort Key: d.c1 NULLS FIRST, e.c1 NULLS FIRST
Presorted Key: d.c1
-> Merge Left Join
Merge Cond: (d.c1 = e.c8)
Join Filter: ((e.c4 > d.c1) AND (e.c2 < d.c3))
-> Sort
Sort Key: d.c1 NULLS FIRST
-> Foreign Scan on f_test_tbl2 d
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Sort
Sort Key: e.c8 NULLS FIRST
-> Foreign Scan on f_test_tbl1 e
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
| | | | |
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | | | |
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
50 | TESTING | | | |
(12 rows)
-- FDW-721: Fix ORDER BY pushdown on the column of inner relation
CREATE FOREIGN TABLE fdw721_tbl1 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl1');
CREATE FOREIGN TABLE fdw721_tbl2 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl2');
INSERT INTO fdw721_tbl1 VALUES(0, 1, 1);
INSERT INTO fdw721_tbl1 VALUES(0, 2, 2);
INSERT INTO fdw721_tbl1 VALUES(0, 3, 3);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 4);
INSERT INTO fdw721_tbl2 VALUES(0, 1, 5);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 6);
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM fdw721_tbl1 t1 LEFT JOIN fdw721_tbl2 t2
ON (t1.c1 = t2.c1) ORDER BY 4 ASC NULLS FIRST;
c1 | c2 | c1 | c2
----+----+----+----
3 | 3 | |
2 | 2 | 2 | 4
1 | 1 | 1 | 5
2 | 2 | 2 | 6
(4 rows)
DELETE FROM f_test_tbl1 WHERE c8 IS NULL;
DELETE FROM f_test_tbl1 WHERE c8 = 60;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DELETE FROM f_test_tbl2 WHERE c1 = 50;
DELETE FROM fdw721_tbl1;
DELETE FROM fdw721_tbl2;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP TABLE l_test_tbl1;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE ftprt2_p1;
DROP FOREIGN TABLE ftprt2_p2;
DROP FOREIGN TABLE fdw721_tbl1;
DROP FOREIGN TABLE fdw721_tbl2;
DROP TABLE IF EXISTS fprt1;
DROP TABLE IF EXISTS fprt2;
DROP USER MAPPING FOR public SERVER mongo_server1;
DROP SERVER mongo_server1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/limit_offset_pushdown.out 0000664 0000000 0000000 00000032476 15066665201 0023255 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress,
-- mongo_fdw_regress1 and mongo_fdw_regress2 databases on MongoDB with all
-- permission for MONGO_USER_NAME user with MONGO_PASS password and ran
-- mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE FOREIGN TABLE fdw131_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- LIMIT/OFFSET pushdown.
-- Limit with Offset should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
-- If ORDER BY is not pushable then limit/Offset shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------------------
Limit
Output: c1, c2, c3
-> Sort
Output: c1, c2, c3
Sort Key: fdw131_t1.c1
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(8 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
-- With ORDER BY pushdown disabled, limit shouldn't get pushdown.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------------------
Limit
Output: c1, c2, c3
-> Sort
Output: c1, c2, c3
Sort Key: fdw131_t1.c1 NULLS FIRST
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(8 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Only limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
c1 | c2 | c3
----+-------------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(3 rows)
-- Expression in limit clause. Should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
c1 | c2 | c3
----+----------------+----------
30 | SALES | MUMBAI
20 | ADMINISTRATION | BANGLORE
(2 rows)
-- Only Offset without limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
c1 | c2 | c3
----+-------------+--------
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(2 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- Limit ALL with OFFSET
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(3 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(4 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
c1 | c2 | c3
----+-------------+--------
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(2 rows)
-- Limit 0 and Offset 0
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
c1 | c2 | c3
----+----+----
(0 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
c1 | c2 | c3
----+----+----
(0 rows)
-- Offset NULL.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
10 | DEVELOPMENT | PUNE
40 | HR | NAGPUR
30 | SALES | MUMBAI
(4 rows)
-- Limit with placeholder. Shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Sort
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Sort Key: fdw131_t1.c2
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(12 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
10 | DEVELOPMENT | PUNE
40 | HR | NAGPUR
30 | SALES | MUMBAI
(4 rows)
-- Limit with expression, shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(9 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1 (returns $0)
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(9 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
ERROR: LIMIT must not be negative
DROP FOREIGN TABLE fdw131_t1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/limit_offset_pushdown_1.out 0000664 0000000 0000000 00000032427 15066665201 0023471 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress,
-- mongo_fdw_regress1 and mongo_fdw_regress2 databases on MongoDB with all
-- permission for MONGO_USER_NAME user with MONGO_PASS password and ran
-- mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE FOREIGN TABLE fdw131_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- LIMIT/OFFSET pushdown.
-- Limit with Offset should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
-- If ORDER BY is not pushable then limit/Offset shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------------------
Limit
Output: c1, c2, c3
-> Sort
Output: c1, c2, c3
Sort Key: fdw131_t1.c1
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(8 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
-- With ORDER BY pushdown disabled, limit shouldn't get pushdown.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
QUERY PLAN
--------------------------------------------------------------
Limit
Output: c1, c2, c3
-> Sort
Output: c1, c2, c3
Sort Key: fdw131_t1.c1 NULLS FIRST
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(8 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
c1 | c2 | c3
----+-------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
(2 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Only limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
c1 | c2 | c3
----+-------------+--------
30 | SALES | MUMBAI
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(3 rows)
-- Expression in limit clause. Should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
c1 | c2 | c3
----+----------------+----------
30 | SALES | MUMBAI
20 | ADMINISTRATION | BANGLORE
(2 rows)
-- Only Offset without limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
c1 | c2 | c3
----+-------------+--------
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(2 rows)
-- Limit ALL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- Limit ALL with OFFSET
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(3 rows)
-- Limit NULL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(4 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
c1 | c2 | c3
----+-------------+--------
40 | HR | NAGPUR
10 | DEVELOPMENT | PUNE
(2 rows)
-- Limit 0 and Offset 0
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
c1 | c2 | c3
----+----+----
(0 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
c1 | c2 | c3
----+----+----
(0 rows)
-- Offset NULL.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
10 | DEVELOPMENT | PUNE
40 | HR | NAGPUR
30 | SALES | MUMBAI
(4 rows)
-- Limit with placeholder. Shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Sort
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Sort Key: fdw131_t1.c2
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(12 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
c1 | c2 | c3
----+----------------+----------
20 | ADMINISTRATION | BANGLORE
10 | DEVELOPMENT | PUNE
40 | HR | NAGPUR
30 | SALES | MUMBAI
(4 rows)
-- Limit with expression, shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(9 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
ERROR: LIMIT must not be negative
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
QUERY PLAN
--------------------------------------------------------
Limit
Output: c1, c2, c3
-> Foreign Scan on public.fdw131_t1
Output: c1, c2, c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(5 rows)
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
ERROR: OFFSET must not be negative
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
QUERY PLAN
-----------------------------------------------------------------------------------
Limit
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
InitPlan 1
-> Foreign Scan
Output: (count(*))
Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw131_t1)
-> Foreign Scan on public.fdw131_t1
Output: fdw131_t1.c1, fdw131_t1.c2, fdw131_t1.c3
Foreign Namespace: mongo_fdw_regress.test_tbl2
(9 rows)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
ERROR: LIMIT must not be negative
DROP FOREIGN TABLE fdw131_t1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/pushdown.out 0000664 0000000 0000000 00000102515 15066665201 0020501 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id name, c1 INTEGER, c2 VARCHAR(14), c3 VARCHAR(13))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id name, name TEXT, marks FLOAT ARRAY, pass BOOLEAN)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl3');
-- Inserts some values in mongo_test collection.
INSERT INTO f_mongo_test VALUES ('0', 1, 'One');
INSERT INTO f_mongo_test VALUES ('0', 2, 'Two');
INSERT INTO f_mongo_test VALUES ('0', 3, 'Three');
SET datestyle TO ISO;
-- Sample data
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
------+-------+-----------+------+------------+---------+------+----
100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.3 | 0 | 20
200 | EMP2 | SALESMAN | 600 | 1981-02-20 | 1600 | 300 | 30
300 | EMP3 | SALESMAN | 600 | 1981-02-22 | 1250 | 500 | 30
400 | EMP4 | MANAGER | 900 | 1981-04-02 | 2975 | 0 | 20
500 | EMP5 | SALESMAN | 600 | 1981-09-28 | 1250.23 | 1400 | 30
600 | EMP6 | MANAGER | 900 | 1981-05-01 | 2850 | 0 | 30
700 | EMP7 | MANAGER | 900 | 1981-06-09 | 2450.34 | 0 | 10
800 | EMP8 | FINANCE | 400 | 1987-04-19 | 3000 | 0 | 20
900 | EMP9 | HEAD | | 1981-11-17 | 5000 | 0 | 10
1000 | EMP10 | SALESMAN | 600 | 1980-09-08 | 1500 | 0 | 30
1100 | EMP11 | ADMIN | 800 | 1987-05-23 | 1100 | 0 | 20
1200 | EMP12 | ADMIN | 600 | 1981-12-03 | 950 | 0 | 30
1300 | EMP13 | FINANCE | 400 | 1981-12-03 | 3000 | 0 | 20
1400 | EMP14 | ADMIN | 700 | 1982-01-23 | 1300 | 0 | 10
(14 rows)
-- WHERE clause pushdown
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e
WHERE c6 IN (1600, 2450)
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Filter: (e.c6 = ANY ('{1600,2450}'::numeric[]))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(4 rows)
SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e
WHERE c6 IN (1600, 2450)
ORDER BY c1;
c1 | c2 | salary | c8
-----+------+--------+----
200 | EMP2 | 1600 | 30
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
c1 | c2 | c6
-----+------+------
900 | EMP9 | 5000
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 = 1500
ORDER BY c1 DESC NULLS LAST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 = 1500
ORDER BY c1 DESC NULLS LAST;
c1 | c2 | c6 | c8
------+-------+------+----
1000 | EMP10 | 1500 | 30
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 BETWEEN 1000 AND 4000
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 BETWEEN 1000 AND 4000
ORDER BY c1 ASC NULLS FIRST;
c1 | c2 | c6 | c8
------+-------+---------+----
200 | EMP2 | 1600 | 30
300 | EMP3 | 1250 | 30
400 | EMP4 | 2975 | 20
500 | EMP5 | 1250.23 | 30
600 | EMP6 | 2850 | 30
700 | EMP7 | 2450.34 | 10
800 | EMP8 | 3000 | 20
1000 | EMP10 | 1500 | 30
1100 | EMP11 | 1100 | 20
1300 | EMP13 | 3000 | 20
1400 | EMP14 | 1300 | 10
(11 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e
WHERE c4 IS NOT NULL
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c2, c4, c6, c8
Sort Key: e.c1
-> Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c4, c6, c8
Filter: (e.c4 IS NOT NULL)
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e
WHERE c4 IS NOT NULL
ORDER BY c1;
c1 | c2 | c4 | c6 | c8
------+-------+------+---------+----
100 | EMP1 | 1300 | 800.3 | 20
200 | EMP2 | 600 | 1600 | 30
300 | EMP3 | 600 | 1250 | 30
400 | EMP4 | 900 | 2975 | 20
500 | EMP5 | 600 | 1250.23 | 30
600 | EMP6 | 900 | 2850 | 30
700 | EMP7 | 900 | 2450.34 | 10
800 | EMP8 | 400 | 3000 | 20
1000 | EMP10 | 600 | 1500 | 30
1100 | EMP11 | 800 | 1100 | 20
1200 | EMP12 | 600 | 950 | 30
1300 | EMP13 | 400 | 3000 | 20
1400 | EMP14 | 700 | 1300 | 10
(13 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17'
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c5
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17'
ORDER BY c1 ASC NULLS FIRST;
c1 | c2 | c5
------+-------+------------
100 | EMP1 | 1980-12-17
1000 | EMP10 | 1980-09-08
(2 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c2 IN ('EMP6', 'EMP12', 'EMP5')
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: c1, c2, c6, c8
Sort Key: e.c1
-> Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Filter: ((e.c2)::text = ANY ('{EMP6,EMP12,EMP5}'::text[]))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c2 IN ('EMP6', 'EMP12', 'EMP5')
ORDER BY c1;
c1 | c2 | c6 | c8
------+-------+---------+----
500 | EMP5 | 1250.23 | 30
600 | EMP6 | 2850 | 30
1200 | EMP12 | 950 | 30
(3 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'SALESMAN'
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c2, c6, c8
Sort Key: e.c1
-> Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Filter: (e.c3 ~~ 'SALESMAN'::text)
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'SALESMAN'
ORDER BY c1;
c1 | c2 | c6 | c8
----+----+----+----
(0 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'MANA%'
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c2, c6, c8
Sort Key: e.c1
-> Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c6, c8
Filter: (e.c3 ~~ 'MANA%'::text)
Foreign Namespace: mongo_fdw_regress.test_tbl1
(7 rows)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'MANA%'
ORDER BY c1;
c1 | c2 | c6 | c8
-----+------+---------+----
400 | EMP4 | 2975 | 20
600 | EMP6 | 2850 | 30
700 | EMP7 | 2450.34 | 10
(3 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a FROM f_mongo_test
WHERE a%2 = 1
ORDER BY a ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------
Foreign Scan on public.f_mongo_test
Output: a
Foreign Namespace: mongo_fdw_regress.mongo_test
(3 rows)
SELECT a FROM f_mongo_test
WHERE a%2 = 1
ORDER BY a ASC NULLS FIRST;
a
---
1
3
(2 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a, b FROM f_mongo_test
WHERE a >= 1 AND b LIKE '%O%'
ORDER BY a;
QUERY PLAN
---------------------------------------------------------
Sort
Output: a, b
Sort Key: f_mongo_test.a
-> Foreign Scan on public.f_mongo_test
Output: a, b
Filter: ((f_mongo_test.b)::text ~~ '%O%'::text)
Foreign Namespace: mongo_fdw_regress.mongo_test
(7 rows)
SELECT a, b FROM f_mongo_test
WHERE a >= 1 AND b LIKE '%O%'
ORDER BY a;
a | b
---+-----
1 | One
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17' AND c2 IN ('EMP1', 'EMP5', 'EMP10') AND c1 = 100
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------------
Foreign Scan on public.f_test_tbl1 e
Output: c1, c2, c5
Filter: ((e.c2)::text = ANY ('{EMP1,EMP5,EMP10}'::text[]))
Foreign Namespace: mongo_fdw_regress.test_tbl1
(4 rows)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17' AND c2 IN ('EMP1', 'EMP5', 'EMP10') AND c1 = 100
ORDER BY c1;
c1 | c2 | c5
-----+------+------------
100 | EMP1 | 1980-12-17
(1 row)
-- The ORDER BY clause shouldn't push-down due to explicit COLLATE.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 = 'EMP10'
ORDER BY c2 COLLATE "en_US" DESC NULLS LAST;
QUERY PLAN
------------------------------------------------------------
Sort
Output: c1, c2, ((c2)::character varying(10))
Sort Key: f_test_tbl1.c2 COLLATE "en_US" DESC NULLS LAST
-> Foreign Scan on public.f_test_tbl1
Output: c1, c2, c2
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 = 'EMP10'
ORDER BY c2 COLLATE "en_US" DESC NULLS LAST;
c1 | c2
------+-------
1000 | EMP10
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 < 'EMP10'
ORDER BY c2 DESC NULLS LAST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c2
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 < 'EMP10'
ORDER BY c2 DESC NULLS LAST;
c1 | c2
-----+------
100 | EMP1
(1 row)
-- Should push down if two columns of same table are
-- involved in single WHERE clause operator expression.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
c1 | c4
------+-----
800 | 400
1000 | 600
1100 | 800
1200 | 600
1300 | 400
1400 | 700
(6 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4, c7, c8 FROM f_test_tbl1
WHERE c1 < c4 AND c7 < c8
ORDER BY c1;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c4, c7, c8
Sort Key: f_test_tbl1.c1
-> Foreign Scan on public.f_test_tbl1
Output: c1, c4, c7, c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
SELECT c1, c4, c7, c8 FROM f_test_tbl1
WHERE c1 < c4 AND c7 < c8
ORDER BY c1;
c1 | c4 | c7 | c8
-----+------+----+----
100 | 1300 | 0 | 20
400 | 900 | 0 | 20
600 | 900 | 0 | 30
700 | 900 | 0 | 10
(4 rows)
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c4
Sort Key: f_test_tbl1.c1 NULLS FIRST
-> Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
c1 | c4
------+-----
800 | 400
1000 | 600
1100 | 800
1200 | 600
1300 | 400
1400 | 700
(6 rows)
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Nested operator expression in WHERE clause. Should pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > FALSE
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c2
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > FALSE
ORDER BY c1 ASC NULLS FIRST;
c1 | c2
------+-------
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(4 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > 0::BOOLEAN
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c2
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > 0::BOOLEAN
ORDER BY c1 ASC NULLS FIRST;
c1 | c2
------+-------
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(4 rows)
-- Shouldn't push down operators where the constant is an array.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, marks FROM f_test_tbl3
WHERE marks = ARRAY[23::FLOAT, 24::FLOAT]
ORDER BY name;
QUERY PLAN
---------------------------------------------------------------------
Sort
Output: name, marks
Sort Key: f_test_tbl3.name
-> Foreign Scan on public.f_test_tbl3
Output: name, marks
Filter: (f_test_tbl3.marks = '{23,24}'::double precision[])
Foreign Namespace: mongo_fdw_regress.test_tbl3
(7 rows)
SELECT name, marks FROM f_test_tbl3
WHERE marks = ARRAY[23::FLOAT, 24::FLOAT]
ORDER BY name;
name | marks
------+---------
dvd | {23,24}
(1 row)
-- Pushdown in prepared statement.
PREPARE pre_stmt_f_mongo_test(int) AS
SELECT b FROM f_mongo_test WHERE a = $1 ORDER BY b;
EXPLAIN (VERBOSE, COSTS FALSE)
EXECUTE pre_stmt_f_mongo_test(1);
QUERY PLAN
---------------------------------------------------------
Sort
Output: b
Sort Key: f_mongo_test.b
-> Foreign Scan on public.f_mongo_test
Output: b
Foreign Namespace: mongo_fdw_regress.mongo_test
(6 rows)
EXECUTE pre_stmt_f_mongo_test(1);
b
-----
One
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
EXECUTE pre_stmt_f_mongo_test(2);
QUERY PLAN
---------------------------------------------------------
Sort
Output: b
Sort Key: f_mongo_test.b
-> Foreign Scan on public.f_mongo_test
Output: b
Foreign Namespace: mongo_fdw_regress.mongo_test
(6 rows)
EXECUTE pre_stmt_f_mongo_test(2);
b
-----
Two
(1 row)
-- FDW-297: Only operator expressions should be pushed down in WHERE clause.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, marks FROM f_test_tbl3
WHERE pass = true
ORDER BY name DESC NULLS LAST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl3
Output: name, marks
Foreign Namespace: mongo_fdw_regress.test_tbl3
(3 rows)
SELECT name, marks FROM f_test_tbl3
WHERE pass = true
ORDER BY name DESC NULLS LAST;
name | marks
------+---------
vdd | {29,31}
(1 row)
-- INSERT NULL values and check behaviour.
INSERT INTO f_test_tbl2 VALUES ('0', NULL, NULL, NULL);
-- Should pushdown and shouldn't result row with NULL VALUES.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c1 < 1;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl2
Output: c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1 FROM f_test_tbl2 WHERE c1 < 1;
c1
----
(0 rows)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c2 = c3;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl2
Output: c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
(3 rows)
SELECT c1 FROM f_test_tbl2 WHERE c2 = c3;
c1
----
(0 rows)
-- Test with IS NULL, shouldn't push down
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl2
Output: c1
Filter: (f_test_tbl2.c2 IS NULL)
Foreign Namespace: mongo_fdw_regress.test_tbl2
(4 rows)
SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL;
c1
----
(1 row)
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Sort
Output: _id, a01, a31, a32, a33, a34, a35, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30
Sort Key: f_test_large.a01 NULLS FIRST, f_test_large.a02 NULLS FIRST, f_test_large.a03 NULLS FIRST, f_test_large.a04 NULLS FIRST, f_test_large.a05 NULLS FIRST, f_test_large.a06 NULLS FIRST, f_test_large.a07 NULLS FIRST, f_test_large.a08 NULLS FIRST, f_test_large.a09 NULLS FIRST, f_test_large.a10 NULLS FIRST, f_test_large.a11 NULLS FIRST, f_test_large.a12 NULLS FIRST, f_test_large.a13 NULLS FIRST, f_test_large.a14 NULLS FIRST, f_test_large.a15 NULLS FIRST, f_test_large.a16 NULLS FIRST, f_test_large.a17 NULLS FIRST, f_test_large.a18 NULLS FIRST, f_test_large.a19 NULLS FIRST, f_test_large.a20 NULLS FIRST, f_test_large.a21 NULLS FIRST, f_test_large.a22 NULLS FIRST, f_test_large.a23 NULLS FIRST, f_test_large.a24 NULLS FIRST, f_test_large.a25 NULLS FIRST, f_test_large.a26 NULLS FIRST, f_test_large.a27 NULLS FIRST, f_test_large.a28 NULLS FIRST, f_test_large.a29 NULLS FIRST, f_test_large.a30 NULLS FIRST, f_test_large.a31 NULLS FIRST, f_test_large.a32 NULLS FIRST, f_test_large.a33 NULLS FIRST, f_test_large.a34 DESC NULLS LAST, f_test_large.a35 NULLS FIRST
-> Foreign Scan on public.f_test_large
Output: _id, a01, a31, a32, a33, a34, a35, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30
Foreign Namespace: mongo_fdw_regress.mongo_test_large
(6 rows)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
_id | a01 | a31 | a32 | a33 | a34 | a35
-----+-----+-----+-----+-----+-----+-----
1 | 1 | 31 | 2 | 3 | 4 | 5
3 | 1 | 31 | 32 | 3 | 34 | 35
0 | 1 | 31 | 32 | 33 | 134 | 35
4 | 1 | 31 | 32 | 33 | 34 | 35
2 | 1 | 31 | 132 | 133 | 134 | 135
(5 rows)
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Foreign Scan on public.f_test_large
Output: _id, a01, a31, a32, a33, a34, a35, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30
Foreign Namespace: mongo_fdw_regress.mongo_test_large
(3 rows)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
_id | a01 | a31 | a32 | a33 | a34 | a35
-----+-----+-----+-----+-----+-----+-----
1 | 1 | 31 | 2 | 3 | 4 | 5
0 | 1 | 31 | 32 | 33 | 134 | 35
3 | 1 | 31 | 32 | 3 | 34 | 35
4 | 1 | 31 | 32 | 33 | 34 | 35
2 | 1 | 31 | 132 | 133 | 134 | 135
(5 rows)
-- FDW-564: Test ORDER BY with user defined operators. Create the operator
-- family required for the test.
CREATE OPERATOR PUBLIC.<^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4EQ
);
CREATE OPERATOR PUBLIC.=^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4LT
);
CREATE OPERATOR PUBLIC.>^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4GT
);
CREATE OPERATOR FAMILY my_op_family USING btree;
CREATE FUNCTION MY_OP_CMP(A INT, B INT) RETURNS INT AS
$$ BEGIN RETURN BTINT4CMP(A, B); END $$ LANGUAGE PLPGSQL;
CREATE OPERATOR CLASS my_op_class FOR TYPE INT USING btree FAMILY my_op_family AS
OPERATOR 1 PUBLIC.<^,
OPERATOR 3 PUBLIC.=^,
OPERATOR 5 PUBLIC.>^,
FUNCTION 1 my_op_cmp(INT, INT);
-- FDW-564: User defined operators are not pushed down.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT * FROM f_mongo_test ORDER BY a USING OPERATOR(public.<^);
QUERY PLAN
---------------------------------------------------------
Sort
Output: _id, a, b
Sort Key: f_mongo_test.a USING <^
-> Foreign Scan on public.f_mongo_test
Output: _id, a, b
Foreign Namespace: mongo_fdw_regress.mongo_test
(6 rows)
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT MIN(a) FROM f_mongo_test GROUP BY b ORDER BY 1 USING OPERATOR(public.<^);
QUERY PLAN
-------------------------------------------------------------------------------------
Sort
Output: (min(a)), b
Sort Key: (min(f_mongo_test.a)) USING <^
-> Foreign Scan
Output: (min(a)), b
Foreign Namespace: Aggregate on (mongo_fdw_regress.mongo_test f_mongo_test)
(6 rows)
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
-- Test the option at server level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'abc11');
ERROR: enable_order_by_pushdown requires a Boolean value
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c4
Sort Key: f_test_tbl1.c1 NULLS FIRST
-> Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
-- Test the option at table level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------
Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(3 rows)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
c1 | c4
------+-----
800 | 400
1000 | 600
1100 | 800
1200 | 600
1300 | 400
1400 | 700
(6 rows)
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c1, c4
Sort Key: f_test_tbl1.c1 NULLS FIRST
-> Foreign Scan on public.f_test_tbl1
Output: c1, c4
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
c1 | c4
------+-----
800 | 400
1000 | 600
1100 | 800
1200 | 600
1300 | 400
1400 | 700
(6 rows)
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
-- FDW-631: Test pushdown of boolean expression
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, pass FROM f_test_tbl3 WHERE pass = false ORDER BY name;
QUERY PLAN
--------------------------------------------------------
Sort
Output: name, pass
Sort Key: f_test_tbl3.name
-> Foreign Scan on public.f_test_tbl3
Output: name, pass
Foreign Namespace: mongo_fdw_regress.test_tbl3
(6 rows)
SELECT name, pass FROM f_test_tbl3 WHERE pass = false ORDER BY name;
name | pass
------+------
dvd | f
(1 row)
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name;
QUERY PLAN
--------------------------------------------------------
Sort
Output: name, pass
Sort Key: f_test_tbl3.name
-> Foreign Scan on public.f_test_tbl3
Output: name, pass
Foreign Namespace: mongo_fdw_regress.test_tbl3
(6 rows)
SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name;
name | pass
------+------
vdd | t
(1 row)
-- FDW-729: print query pipeline to find remote query
SET mongo_fdw.log_remote_query TO true;
SET client_min_messages TO log;
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
LOG: remote query: db.test_tbl1.aggregate( [ { "$match" : { "$expr" : { "$and" : [ { "$gt" : [ "$c6", 3000.0 ] }, { "$ne" : [ "$c6", null ] } ] } } }, { "$sort" : { "c1" : 1 } } ] )
c1 | c2 | c6
-----+------+------
900 | EMP9 | 5000
(1 row)
RESET client_min_messages;
RESET mongo_fdw.log_remote_query;
-- Cleanup
DELETE FROM f_mongo_test WHERE a != 0;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_large;
DROP OPERATOR CLASS my_op_class USING btree;
DROP FUNCTION my_op_cmp(a INT, b INT);
DROP OPERATOR FAMILY my_op_family USING btree;
DROP OPERATOR public.>^(INT, INT);
DROP OPERATOR public.=^(INT, INT);
DROP OPERATOR public.<^(INT, INT);
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/select.out 0000664 0000000 0000000 00000136032 15066665201 0020112 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Check version
SELECT mongo_fdw_version();
mongo_fdw_version
-------------------
50503
(1 row)
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 VARCHAR(10), c3 CHAR(9),c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 VARCHAR(14), c3 VARCHAR(13))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE countries (_id NAME, name VARCHAR, population INTEGER, capital VARCHAR, hdi FLOAT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE country_elections (_id NAME, "lastElections.type" VARCHAR, "lastElections.date" pg_catalog.TIMESTAMP)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE main_exports (_id NAME, "mainExports" TEXT[] )
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE test_json ( __doc json)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_jsonb ( __doc jsonb)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, a NUMERIC(12, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME, a BOOLEAN)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE f_test_tbl6 (_id NAME, a INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl5');
CREATE FOREIGN TABLE f_test_tbl7 (_id NAME, a INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE testlog (_id NAME, log VARCHAR, "logMeta.logMac" VARCHAR, "logMeta.nestMore.level" INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'testlog');
CREATE FOREIGN TABLE testdevice (_id NAME, name VARCHAR, mac VARCHAR, level INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'testdevice');
SET datestyle TO ISO;
-- Retrieve data from foreign table using SELECT statement.
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
ORDER BY c1 DESC, c8;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
------+-------+-----------+------+------------+---------+------+----
1400 | EMP14 | ADMIN | 700 | 1982-01-23 | 1300 | 0 | 10
1300 | EMP13 | FINANCE | 400 | 1981-12-03 | 3000 | 0 | 20
1200 | EMP12 | ADMIN | 600 | 1981-12-03 | 950 | 0 | 30
1100 | EMP11 | ADMIN | 800 | 1987-05-23 | 1100 | 0 | 20
1000 | EMP10 | SALESMAN | 600 | 1980-09-08 | 1500 | 0 | 30
900 | EMP9 | HEAD | | 1981-11-17 | 5000 | 0 | 10
800 | EMP8 | FINANCE | 400 | 1987-04-19 | 3000 | 0 | 20
700 | EMP7 | MANAGER | 900 | 1981-06-09 | 2450.34 | 0 | 10
600 | EMP6 | MANAGER | 900 | 1981-05-01 | 2850 | 0 | 30
500 | EMP5 | SALESMAN | 600 | 1981-09-28 | 1250.23 | 1400 | 30
400 | EMP4 | MANAGER | 900 | 1981-04-02 | 2975 | 0 | 20
300 | EMP3 | SALESMAN | 600 | 1981-02-22 | 1250 | 500 | 30
200 | EMP2 | SALESMAN | 600 | 1981-02-20 | 1600 | 300 | 30
100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.3 | 0 | 20
(14 rows)
SELECT DISTINCT c8 FROM f_test_tbl1 ORDER BY 1;
c8
----
10
20
30
(3 rows)
SELECT c2 AS "Employee Name" FROM f_test_tbl1 ORDER BY c2 COLLATE "C";
Employee Name
---------------
EMP1
EMP10
EMP11
EMP12
EMP13
EMP14
EMP2
EMP3
EMP4
EMP5
EMP6
EMP7
EMP8
EMP9
(14 rows)
SELECT c8, c6, c7 FROM f_test_tbl1 ORDER BY 1, 2, 3;
c8 | c6 | c7
----+---------+------
10 | 1300 | 0
10 | 2450.34 | 0
10 | 5000 | 0
20 | 800.3 | 0
20 | 1100 | 0
20 | 2975 | 0
20 | 3000 | 0
20 | 3000 | 0
30 | 950 | 0
30 | 1250 | 500
30 | 1250.23 | 1400
30 | 1500 | 0
30 | 1600 | 300
30 | 2850 | 0
(14 rows)
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c1 = 100 ORDER BY 1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
-----+------+-----------+------+------------+-------+----+----
100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.3 | 0 | 20
(1 row)
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c1 = 100 OR c1 = 700 ORDER BY 1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
-----+------+-----------+------+------------+---------+----+----
100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.3 | 0 | 20
700 | EMP7 | MANAGER | 900 | 1981-06-09 | 2450.34 | 0 | 10
(2 rows)
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c3 like 'SALESMAN' ORDER BY 1;
c1 | c2 | c3
----+----+----
(0 rows)
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c1 IN (100, 700) ORDER BY 1;
c1 | c2 | c3
-----+------+-----------
100 | EMP1 | ADMIN
700 | EMP7 | MANAGER
(2 rows)
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c1 NOT IN (100, 700) ORDER BY 1 LIMIT 5;
c1 | c2 | c3
-----+------+-----------
200 | EMP2 | SALESMAN
300 | EMP3 | SALESMAN
400 | EMP4 | MANAGER
500 | EMP5 | SALESMAN
600 | EMP6 | MANAGER
(5 rows)
SELECT c1, c2, c8 FROM f_test_tbl1 WHERE c8 BETWEEN 10 AND 20 ORDER BY 1;
c1 | c2 | c8
------+-------+----
100 | EMP1 | 20
400 | EMP4 | 20
700 | EMP7 | 10
800 | EMP8 | 20
900 | EMP9 | 10
1100 | EMP11 | 20
1300 | EMP13 | 20
1400 | EMP14 | 10
(8 rows)
SELECT c1, c2, c6 FROM f_test_tbl1 ORDER BY 1 OFFSET 5;
c1 | c2 | c6
------+-------+---------
600 | EMP6 | 2850
700 | EMP7 | 2450.34
800 | EMP8 | 3000
900 | EMP9 | 5000
1000 | EMP10 | 1500
1100 | EMP11 | 1100
1200 | EMP12 | 950
1300 | EMP13 | 3000
1400 | EMP14 | 1300
(9 rows)
-- Retrieve data from foreign table using group by clause.
SELECT c8 "Department", COUNT(c1) "Total Employees" FROM f_test_tbl1
GROUP BY c8 ORDER BY c8;
Department | Total Employees
------------+-----------------
10 | 3
20 | 5
30 | 6
(3 rows)
SELECT c8, SUM(c6) FROM f_test_tbl1
GROUP BY c8 HAVING c8 IN (10, 30) ORDER BY c8;
c8 | sum
----+---------
10 | 8750.34
30 | 9400.23
(2 rows)
SELECT c8, SUM(c6) FROM f_test_tbl1
GROUP BY c8 HAVING SUM(c6) > 9400 ORDER BY c8;
c8 | sum
----+---------
20 | 10875.3
30 | 9400.23
(2 rows)
-- Retrieve data from foreign table using sub-queries.
SELECT c1, c2, c6 FROM f_test_tbl1
WHERE c8 <> ALL (SELECT c1 FROM f_test_tbl2 WHERE c1 IN (10, 30, 40))
ORDER BY c1;
c1 | c2 | c6
------+-------+-------
100 | EMP1 | 800.3
400 | EMP4 | 2975
800 | EMP8 | 3000
1100 | EMP11 | 1100
1300 | EMP13 | 3000
(5 rows)
SELECT c1, c2, c3 FROM f_test_tbl2
WHERE EXISTS (SELECT 1 FROM f_test_tbl1 WHERE f_test_tbl2.c1 = f_test_tbl1.c8)
ORDER BY 1, 2;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
(3 rows)
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c8 NOT IN (SELECT c1 FROM f_test_tbl2) ORDER BY c1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
----+----+----+----+----+----+----+----
(0 rows)
-- Retrieve data from foreign table using UNION operator.
SELECT c1, c2 FROM f_test_tbl2 UNION
SELECT c1, c2 FROM f_test_tbl1 ORDER BY c1;
c1 | c2
------+----------------
10 | DEVELOPMENT
20 | ADMINISTRATION
30 | SALES
40 | HR
100 | EMP1
200 | EMP2
300 | EMP3
400 | EMP4
500 | EMP5
600 | EMP6
700 | EMP7
800 | EMP8
900 | EMP9
1000 | EMP10
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(18 rows)
SELECT c1, c2 FROM f_test_tbl2 UNION ALL
SELECT c1, c2 FROM f_test_tbl1 ORDER BY c1;
c1 | c2
------+----------------
10 | DEVELOPMENT
20 | ADMINISTRATION
30 | SALES
40 | HR
100 | EMP1
200 | EMP2
300 | EMP3
400 | EMP4
500 | EMP5
600 | EMP6
700 | EMP7
800 | EMP8
900 | EMP9
1000 | EMP10
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(18 rows)
-- Retrieve data from foreign table using INTERSECT operator.
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 800 INTERSECT
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 400 ORDER BY c1;
c1 | c2
------+-------
800 | EMP8
900 | EMP9
1000 | EMP10
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(7 rows)
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 800 INTERSECT ALL
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 400 ORDER BY c1;
c1 | c2
------+-------
800 | EMP8
900 | EMP9
1000 | EMP10
1100 | EMP11
1200 | EMP12
1300 | EMP13
1400 | EMP14
(7 rows)
-- Retrieve data from foreign table using EXCEPT operator.
SELECT c1, c2 FROM f_test_tbl1 EXCEPT
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 > 900 ORDER BY c1;
c1 | c2
-----+------
100 | EMP1
200 | EMP2
300 | EMP3
400 | EMP4
500 | EMP5
600 | EMP6
700 | EMP7
800 | EMP8
900 | EMP9
(9 rows)
SELECT c1, c2 FROM f_test_tbl1 EXCEPT ALL
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 > 900 ORDER BY c1;
c1 | c2
-----+------
100 | EMP1
200 | EMP2
300 | EMP3
400 | EMP4
500 | EMP5
600 | EMP6
700 | EMP7
800 | EMP8
900 | EMP9
(9 rows)
-- Retrieve data from foreign table using CTE (with clause).
WITH
with_qry AS (SELECT c1, c2, c3 FROM f_test_tbl2)
SELECT e.c2, e.c6, w.c1, w.c2 FROM f_test_tbl1 e, with_qry w
WHERE e.c8 = w.c1 ORDER BY e.c8, e.c2 COLLATE "C";
c2 | c6 | c1 | c2
-------+---------+----+----------------
EMP14 | 1300 | 10 | DEVELOPMENT
EMP7 | 2450.34 | 10 | DEVELOPMENT
EMP9 | 5000 | 10 | DEVELOPMENT
EMP1 | 800.3 | 20 | ADMINISTRATION
EMP11 | 1100 | 20 | ADMINISTRATION
EMP13 | 3000 | 20 | ADMINISTRATION
EMP4 | 2975 | 20 | ADMINISTRATION
EMP8 | 3000 | 20 | ADMINISTRATION
EMP10 | 1500 | 30 | SALES
EMP12 | 950 | 30 | SALES
EMP2 | 1600 | 30 | SALES
EMP3 | 1250 | 30 | SALES
EMP5 | 1250.23 | 30 | SALES
EMP6 | 2850 | 30 | SALES
(14 rows)
WITH
test_tbl2_costs AS (SELECT d.c2, SUM(c6) test_tbl2_total FROM f_test_tbl1 e, f_test_tbl2 d
WHERE e.c8 = d.c1 GROUP BY 1),
avg_cost AS (SELECT SUM(test_tbl2_total)/COUNT(*) avg FROM test_tbl2_costs)
SELECT * FROM test_tbl2_costs
WHERE test_tbl2_total > (SELECT avg FROM avg_cost) ORDER BY c2 COLLATE "C";
c2 | test_tbl2_total
----------------+-----------------
ADMINISTRATION | 10875.3
(1 row)
-- Retrieve data from foreign table using window clause.
SELECT c8, c1, c6, AVG(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
ORDER BY c8, c1;
c8 | c1 | c6 | avg
----+------+---------+-----------------------
10 | 700 | 2450.34 | 2916.7800000000000000
10 | 900 | 5000 | 2916.7800000000000000
10 | 1400 | 1300 | 2916.7800000000000000
20 | 100 | 800.3 | 2175.0600000000000000
20 | 400 | 2975 | 2175.0600000000000000
20 | 800 | 3000 | 2175.0600000000000000
20 | 1100 | 1100 | 2175.0600000000000000
20 | 1300 | 3000 | 2175.0600000000000000
30 | 200 | 1600 | 1566.7050000000000000
30 | 300 | 1250 | 1566.7050000000000000
30 | 500 | 1250.23 | 1566.7050000000000000
30 | 600 | 2850 | 1566.7050000000000000
30 | 1000 | 1500 | 1566.7050000000000000
30 | 1200 | 950 | 1566.7050000000000000
(14 rows)
SELECT c8, c1, c6, COUNT(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
WHERE c8 IN (10, 30, 40, 50, 60, 70) ORDER BY c8, c1;
c8 | c1 | c6 | count
----+------+---------+-------
10 | 700 | 2450.34 | 3
10 | 900 | 5000 | 3
10 | 1400 | 1300 | 3
30 | 200 | 1600 | 6
30 | 300 | 1250 | 6
30 | 500 | 1250.23 | 6
30 | 600 | 2850 | 6
30 | 1000 | 1500 | 6
30 | 1200 | 950 | 6
(9 rows)
SELECT c8, c1, c6, SUM(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
ORDER BY c8, c1;
c8 | c1 | c6 | sum
----+------+---------+---------
10 | 700 | 2450.34 | 8750.34
10 | 900 | 5000 | 8750.34
10 | 1400 | 1300 | 8750.34
20 | 100 | 800.3 | 10875.3
20 | 400 | 2975 | 10875.3
20 | 800 | 3000 | 10875.3
20 | 1100 | 1100 | 10875.3
20 | 1300 | 3000 | 10875.3
30 | 200 | 1600 | 9400.23
30 | 300 | 1250 | 9400.23
30 | 500 | 1250.23 | 9400.23
30 | 600 | 2850 | 9400.23
30 | 1000 | 1500 | 9400.23
30 | 1200 | 950 | 9400.23
(14 rows)
-- Views
CREATE VIEW smpl_vw AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1;
SELECT * FROM smpl_vw ORDER BY 1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
------+-------+-----------+------+------------+---------+------+----
100 | EMP1 | ADMIN | 1300 | 1980-12-17 | 800.3 | 0 | 20
200 | EMP2 | SALESMAN | 600 | 1981-02-20 | 1600 | 300 | 30
300 | EMP3 | SALESMAN | 600 | 1981-02-22 | 1250 | 500 | 30
400 | EMP4 | MANAGER | 900 | 1981-04-02 | 2975 | 0 | 20
500 | EMP5 | SALESMAN | 600 | 1981-09-28 | 1250.23 | 1400 | 30
600 | EMP6 | MANAGER | 900 | 1981-05-01 | 2850 | 0 | 30
700 | EMP7 | MANAGER | 900 | 1981-06-09 | 2450.34 | 0 | 10
800 | EMP8 | FINANCE | 400 | 1987-04-19 | 3000 | 0 | 20
900 | EMP9 | HEAD | | 1981-11-17 | 5000 | 0 | 10
1000 | EMP10 | SALESMAN | 600 | 1980-09-08 | 1500 | 0 | 30
1100 | EMP11 | ADMIN | 800 | 1987-05-23 | 1100 | 0 | 20
1200 | EMP12 | ADMIN | 600 | 1981-12-03 | 950 | 0 | 30
1300 | EMP13 | FINANCE | 400 | 1981-12-03 | 3000 | 0 | 20
1400 | EMP14 | ADMIN | 700 | 1982-01-23 | 1300 | 0 | 10
(14 rows)
CREATE VIEW comp_vw (s1, s2, s3, s6, s7, s8, d2) AS
SELECT s.c1, s.c2, s.c3, s.c6, s.c7, s.c8, d.c2
FROM f_test_tbl2 d, f_test_tbl1 s WHERE d.c1 = s.c8 AND d.c1 = 10
ORDER BY s.c1;
SELECT * FROM comp_vw ORDER BY 1;
s1 | s2 | s3 | s6 | s7 | s8 | d2
------+-------+-----------+---------+----+----+-------------
700 | EMP7 | MANAGER | 2450.34 | 0 | 10 | DEVELOPMENT
900 | EMP9 | HEAD | 5000 | 0 | 10 | DEVELOPMENT
1400 | EMP14 | ADMIN | 1300 | 0 | 10 | DEVELOPMENT
(3 rows)
CREATE TEMPORARY VIEW temp_vw AS
SELECT c1, c2, c3 FROM f_test_tbl2;
SELECT * FROM temp_vw ORDER BY 1, 2;
c1 | c2 | c3
----+----------------+----------
10 | DEVELOPMENT | PUNE
20 | ADMINISTRATION | BANGLORE
30 | SALES | MUMBAI
40 | HR | NAGPUR
(4 rows)
CREATE VIEW mul_tbl_view AS
SELECT d.c1 dc1, d.c2 dc2, e.c1 ec1, e.c2 ec2, e.c6 ec6
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY d.c1;
SELECT * FROM mul_tbl_view ORDER BY 1, 2, 3;
dc1 | dc2 | ec1 | ec2 | ec6
-----+----------------+------+-------+---------
10 | DEVELOPMENT | 700 | EMP7 | 2450.34
10 | DEVELOPMENT | 900 | EMP9 | 5000
10 | DEVELOPMENT | 1400 | EMP14 | 1300
20 | ADMINISTRATION | 100 | EMP1 | 800.3
20 | ADMINISTRATION | 400 | EMP4 | 2975
20 | ADMINISTRATION | 800 | EMP8 | 3000
20 | ADMINISTRATION | 1100 | EMP11 | 1100
20 | ADMINISTRATION | 1300 | EMP13 | 3000
30 | SALES | 200 | EMP2 | 1600
30 | SALES | 300 | EMP3 | 1250
30 | SALES | 500 | EMP5 | 1250.23
30 | SALES | 600 | EMP6 | 2850
30 | SALES | 1000 | EMP10 | 1500
30 | SALES | 1200 | EMP12 | 950
(14 rows)
-- Foreign-Foreign table joins
-- CROSS JOIN.
SELECT f_test_tbl2.c2, f_test_tbl1.c2
FROM f_test_tbl2 CROSS JOIN f_test_tbl1 ORDER BY 1, 2;
c2 | c2
----------------+-------
ADMINISTRATION | EMP1
ADMINISTRATION | EMP10
ADMINISTRATION | EMP11
ADMINISTRATION | EMP12
ADMINISTRATION | EMP13
ADMINISTRATION | EMP14
ADMINISTRATION | EMP2
ADMINISTRATION | EMP3
ADMINISTRATION | EMP4
ADMINISTRATION | EMP5
ADMINISTRATION | EMP6
ADMINISTRATION | EMP7
ADMINISTRATION | EMP8
ADMINISTRATION | EMP9
DEVELOPMENT | EMP1
DEVELOPMENT | EMP10
DEVELOPMENT | EMP11
DEVELOPMENT | EMP12
DEVELOPMENT | EMP13
DEVELOPMENT | EMP14
DEVELOPMENT | EMP2
DEVELOPMENT | EMP3
DEVELOPMENT | EMP4
DEVELOPMENT | EMP5
DEVELOPMENT | EMP6
DEVELOPMENT | EMP7
DEVELOPMENT | EMP8
DEVELOPMENT | EMP9
HR | EMP1
HR | EMP10
HR | EMP11
HR | EMP12
HR | EMP13
HR | EMP14
HR | EMP2
HR | EMP3
HR | EMP4
HR | EMP5
HR | EMP6
HR | EMP7
HR | EMP8
HR | EMP9
SALES | EMP1
SALES | EMP10
SALES | EMP11
SALES | EMP12
SALES | EMP13
SALES | EMP14
SALES | EMP2
SALES | EMP3
SALES | EMP4
SALES | EMP5
SALES | EMP6
SALES | EMP7
SALES | EMP8
SALES | EMP9
(56 rows)
-- INNER JOIN.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d, f_test_tbl1 e WHERE d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
-- OUTER JOINS.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
(15 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d FULL OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
(15 rows)
-- Local-Foreign table joins.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
CREATE TABLE l_test_tbl2 AS
SELECT c1, c2, c3 FROM f_test_tbl2;
-- CROSS JOIN.
SELECT f_test_tbl2.c2, l_test_tbl1.c2 FROM f_test_tbl2 CROSS JOIN l_test_tbl1 ORDER BY 1, 2;
c2 | c2
----------------+-------
ADMINISTRATION | EMP1
ADMINISTRATION | EMP10
ADMINISTRATION | EMP11
ADMINISTRATION | EMP12
ADMINISTRATION | EMP13
ADMINISTRATION | EMP14
ADMINISTRATION | EMP2
ADMINISTRATION | EMP3
ADMINISTRATION | EMP4
ADMINISTRATION | EMP5
ADMINISTRATION | EMP6
ADMINISTRATION | EMP7
ADMINISTRATION | EMP8
ADMINISTRATION | EMP9
DEVELOPMENT | EMP1
DEVELOPMENT | EMP10
DEVELOPMENT | EMP11
DEVELOPMENT | EMP12
DEVELOPMENT | EMP13
DEVELOPMENT | EMP14
DEVELOPMENT | EMP2
DEVELOPMENT | EMP3
DEVELOPMENT | EMP4
DEVELOPMENT | EMP5
DEVELOPMENT | EMP6
DEVELOPMENT | EMP7
DEVELOPMENT | EMP8
DEVELOPMENT | EMP9
HR | EMP1
HR | EMP10
HR | EMP11
HR | EMP12
HR | EMP13
HR | EMP14
HR | EMP2
HR | EMP3
HR | EMP4
HR | EMP5
HR | EMP6
HR | EMP7
HR | EMP8
HR | EMP9
SALES | EMP1
SALES | EMP10
SALES | EMP11
SALES | EMP12
SALES | EMP13
SALES | EMP14
SALES | EMP2
SALES | EMP3
SALES | EMP4
SALES | EMP5
SALES | EMP6
SALES | EMP7
SALES | EMP8
SALES | EMP9
(56 rows)
-- INNER JOIN.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM l_test_tbl2 d, f_test_tbl1 e WHERE d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
-- OUTER JOINS.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
(15 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
(14 rows)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d FULL OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
c1 | c2 | c1 | c2 | c6 | c8
----+----------------+------+-------+---------+----
10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10
10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10
10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10
20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20
20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20
20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20
20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20
20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20
30 | SALES | 200 | EMP2 | 1600 | 30
30 | SALES | 300 | EMP3 | 1250 | 30
30 | SALES | 500 | EMP5 | 1250.23 | 30
30 | SALES | 600 | EMP6 | 2850 | 30
30 | SALES | 1000 | EMP10 | 1500 | 30
30 | SALES | 1200 | EMP12 | 950 | 30
40 | HR | | | |
(15 rows)
-- Retrieve complex data containing Sub-fields, dates, Arrays
SELECT * FROM countries ORDER BY _id;
_id | name | population | capital | hdi
--------------------------+---------+------------+----------+-------
5381ccf9d6d81c8e8bf0434f | Ukraine | 45590000 | Kyiv | 0.74
5381ccf9d6d81c8e8bf04350 | Poland | 38540000 | Warsaw | 0.821
5381ccf9d6d81c8e8bf04351 | Moldova | 3560000 | Chișinău | 0.66
(3 rows)
SELECT * FROM country_elections ORDER BY _id;
_id | lastElections.type | lastElections.date
--------------------------+--------------------+---------------------
5381ccf9d6d81c8e8bf0434f | presidential | 2014-05-25 00:00:00
5381ccf9d6d81c8e8bf04350 | parliamentary | 2011-10-09 00:00:00
5381ccf9d6d81c8e8bf04351 | parliamentary | 2010-11-28 00:00:00
(3 rows)
SELECT * FROM main_exports ORDER BY _id;
_id | mainExports
--------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5381ccf9d6d81c8e8bf0434f | {"Semi-finished products of iron or non-alloy steel","Flat-rolled products of iron or non-alloy steel","Sunflower-seed, safflower or cotton-seed oil"}
5381ccf9d6d81c8e8bf04350 | {"Parts and accessories of the motor vehicles of headings 87.01 to 87.0","Motor cars and other motor vehicles principally designed for the transport","Reception apparatus for television"}
5381ccf9d6d81c8e8bf04351 | {"Wine of fresh grapes, including fortified wines","Insulated (including enameled or anodized) wire, cable","Sunflower seeds, whether or not broken"}
(3 rows)
-- Retrieve complex data containing Json objects (__doc tests)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_json, json_each_text(test_json.__doc) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 1
warehouse_id | 2
warehouse_name | UPS
warehouse_name | Laptop
(6 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_jsonb, jsonb_each_text(test_jsonb.__doc) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+--------------------------
warehouse_created | {"$date": 1418368330000}
warehouse_created | {"$date": 1447229590000}
warehouse_id | 1
warehouse_id | 2
warehouse_name | UPS
warehouse_name | Laptop
(6 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, json_each_text(test_text.__doc::json) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 1
warehouse_id | 2
warehouse_name | UPS
warehouse_name | Laptop
(6 rows)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_varchar, json_each_text(test_varchar.__doc::json) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
key1 | value1
-------------------+-----------------------------
warehouse_created | { "$date" : 1418368330000 }
warehouse_created | { "$date" : 1447229590000 }
warehouse_id | 1
warehouse_id | 2
warehouse_name | UPS
warehouse_name | Laptop
(6 rows)
-- Inserts some values in mongo_test collection.
INSERT INTO f_mongo_test VALUES ('0', 1, 'One');
INSERT INTO f_mongo_test VALUES ('0', 2, 'Two');
INSERT INTO f_mongo_test VALUES ('0', 3, 'Three');
INSERT INTO f_mongo_test VALUES ('0', 4, 'Four');
INSERT INTO f_mongo_test VALUES ('0', 5, 'Five');
INSERT INTO f_mongo_test VALUES ('0', 6, 'Six');
INSERT INTO f_mongo_test VALUES ('0', 7, 'Seven');
INSERT INTO f_mongo_test VALUES ('0', 8, 'Eight');
INSERT INTO f_mongo_test VALUES ('0', 9, 'Nine');
INSERT INTO f_mongo_test VALUES ('0', 10, 'Ten');
-- Retrieve Data From foreign tables in functions.
CREATE OR REPLACE FUNCTION test_param_where() RETURNS void AS $$
DECLARE
n varchar;
BEGIN
FOR x IN 1..9 LOOP
SELECT b INTO n FROM f_mongo_test WHERE a = x;
RAISE NOTICE 'Found number %', n;
END LOOP;
return;
END
$$ LANGUAGE plpgsql;
SELECT test_param_where();
NOTICE: Found number One
NOTICE: Found number Two
NOTICE: Found number Three
NOTICE: Found number Four
NOTICE: Found number Five
NOTICE: Found number Six
NOTICE: Found number Seven
NOTICE: Found number Eight
NOTICE: Found number Nine
test_param_where
------------------
(1 row)
-- FDW-103: Parameter expression should work correctly with WHERE clause.
SELECT a, b FROM f_mongo_test WHERE a = (SELECT 2) ORDER BY a;
a | b
---+-----
2 | Two
(1 row)
SELECT a, b FROM f_mongo_test WHERE b = (SELECT 'Seven'::text) ORDER BY a;
a | b
---+-------
7 | Seven
(1 row)
-- Create local table and load data into it.
CREATE TABLE l_mongo_test AS SELECT a, b FROM f_mongo_test;
-- Check correlated query.
SELECT a, b FROM l_mongo_test lt
WHERE lt.b = (SELECT b FROM f_mongo_test ft WHERE lt.b = ft.b)
ORDER BY a;
a | b
----+-----------------------
0 | mongo_test collection
1 | One
2 | Two
3 | Three
4 | Four
5 | Five
6 | Six
7 | Seven
8 | Eight
9 | Nine
10 | Ten
(11 rows)
SELECT a, b FROM l_mongo_test lt
WHERE lt.a = (SELECT a FROM f_mongo_test ft WHERE lt.a = ft.a)
ORDER BY a;
a | b
----+-----------------------
0 | mongo_test collection
1 | One
2 | Two
3 | Three
4 | Four
5 | Five
6 | Six
7 | Seven
8 | Eight
9 | Nine
10 | Ten
(11 rows)
SELECT c1, c8 FROM f_test_tbl1 ft1
WHERE ft1.c8 = (SELECT c1 FROM f_test_tbl2 ft2 WHERE ft1.c8 = ft2.c1)
ORDER BY c1 LIMIT 2;
c1 | c8
-----+----
100 | 20
200 | 30
(2 rows)
-- FDW-197: Casting target list should give correct result.
SELECT a::float FROM f_mongo_test ORDER BY a LIMIT 2;
a
---
0
1
(2 rows)
SELECT a::boolean FROM f_mongo_test ORDER BY a LIMIT 2;
a
---
f
t
(2 rows)
SELECT a, b::varchar FROM f_mongo_test ORDER BY a LIMIT 3;
a | b
---+-----------------------
0 | mongo_test collection
1 | One
2 | Two
(3 rows)
SELECT a::float, b::varchar FROM f_mongo_test ORDER BY a LIMIT 2;
a | b
---+-----------------------
0 | mongo_test collection
1 | One
(2 rows)
SELECT a::real, b::char(20) FROM f_mongo_test ORDER BY a LIMIT 2;
a | b
---+----------------------
0 | mongo_test collectio
1 | One
(2 rows)
SELECT c1, c2::text FROM f_test_tbl1 ORDER BY c1 LIMIT 2;
c1 | c2
-----+------
100 | EMP1
200 | EMP2
(2 rows)
SELECT a, LENGTH(b) FROM f_mongo_test ORDER BY 1 LIMIT 2;
a | length
---+--------
0 | 21
1 | 3
(2 rows)
SELECT t1.c6::float, t1.c6::int, t1.c5::timestamptz, t1.c3::text, t2.c1::numeric, t2.c3
FROM f_test_tbl1 t1, f_test_tbl2 t2 WHERE t1.c8 = t2.c1
ORDER BY t2.c1, t1.c6 LIMIT 5;
c6 | c6 | c5 | c3 | c1 | c3
---------+------+------------------------+---------+----+----------
1300 | 1300 | 1982-01-23 00:00:00-08 | ADMIN | 10 | PUNE
2450.34 | 2450 | 1981-06-09 00:00:00-07 | MANAGER | 10 | PUNE
5000 | 5000 | 1981-11-17 00:00:00-08 | HEAD | 10 | PUNE
800.3 | 800 | 1980-12-17 00:00:00-08 | ADMIN | 20 | BANGLORE
1100 | 1100 | 1987-05-23 00:00:00-07 | ADMIN | 20 | BANGLORE
(5 rows)
SELECT SUM(a::float), SUM(a % 2), a % 2 AS "a % 2"FROM f_mongo_test
GROUP BY a % 2 ORDER BY 2;
sum | sum | a % 2
-----+-----+-------
30 | 0 | 0
25 | 5 | 1
(2 rows)
SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6
FROM f_test_tbl1 ORDER BY c1 LIMIT 5;
c1 + c6 | c1 | c6
---------+-----+---------
1300.3 | 100 | 800.3
3200 | 200 | 1600
3650 | 300 | 1250
5775 | 400 | 2975
5250.23 | 500 | 1250.23
(5 rows)
-- FDW-249; LEFT JOIN LATERAL should not crash
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.a, t1.b, t3.a, t1_a FROM f_mongo_test t1 LEFT JOIN LATERAL (
SELECT t2.a, t1.a AS t1_a FROM f_mongo_test t2) t3 ON t1.a = t3.a ORDER BY 1 ASC NULLS FIRST;
QUERY PLAN
---------------------------------------------------------
Nested Loop Left Join
Output: t1.a, t1.b, t2.a, (t1.a)
-> Foreign Scan on public.f_mongo_test t1
Output: t1._id, t1.a, t1.b
Foreign Namespace: mongo_fdw_regress.mongo_test
-> Foreign Scan on public.f_mongo_test t2
Output: t2.a, t1.a
Filter: (t1.a = t2.a)
Foreign Namespace: mongo_fdw_regress.mongo_test
(9 rows)
SELECT t1.a, t1.b, t3.a, t1_a FROM f_mongo_test t1 LEFT JOIN LATERAL (
SELECT t2.a, t1.a AS t1_a FROM f_mongo_test t2) t3 ON t1.a = t3.a ORDER BY 1 ASC NULLS FIRST;
a | b | a | t1_a
----+-----------------------+----+------
0 | mongo_test collection | 0 | 0
1 | One | 1 | 1
2 | Two | 2 | 2
3 | Three | 3 | 3
4 | Four | 4 | 4
5 | Five | 5 | 5
6 | Six | 6 | 6
7 | Seven | 7 | 7
8 | Eight | 8 | 8
9 | Nine | 9 | 9
10 | Ten | 10 | 10
(11 rows)
SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 INNER JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
c1 | c1 | t1_c8
------+----+-------
100 | 20 | 20
200 | 30 | 30
300 | 30 | 30
400 | 20 | 20
500 | 30 | 30
600 | 30 | 30
700 | 10 | 10
800 | 20 | 20
900 | 10 | 10
1000 | 30 | 30
1100 | 20 | 20
1200 | 30 | 30
1300 | 20 | 20
1400 | 10 | 10
(14 rows)
SELECT t1.c1, t3.c1, t3.t1_c8 FROM l_test_tbl1 t1 LEFT JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
c1 | c1 | t1_c8
------+----+-------
100 | 20 | 20
200 | 30 | 30
300 | 30 | 30
400 | 20 | 20
500 | 30 | 30
600 | 30 | 30
700 | 10 | 10
800 | 20 | 20
900 | 10 | 10
1000 | 30 | 30
1100 | 20 | 20
1200 | 30 | 30
1300 | 20 | 20
1400 | 10 | 10
(14 rows)
SELECT c1, c2, (SELECT r FROM (SELECT c1 AS c1) x, LATERAL (SELECT c1 AS r) y)
FROM f_test_tbl1 ORDER BY 1, 2, 3;
c1 | c2 | r
------+-------+------
100 | EMP1 | 100
200 | EMP2 | 200
300 | EMP3 | 300
400 | EMP4 | 400
500 | EMP5 | 500
600 | EMP6 | 600
700 | EMP7 | 700
800 | EMP8 | 800
900 | EMP9 | 900
1000 | EMP10 | 1000
1100 | EMP11 | 1100
1200 | EMP12 | 1200
1300 | EMP13 | 1300
1400 | EMP14 | 1400
(14 rows)
-- LATERAL JOIN with RIGHT should throw error
SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 RIGHT JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
ERROR: invalid reference to FROM-clause entry for table "t1"
LINE 2: SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3...
^
DETAIL: The combining JOIN type must be INNER or LEFT for a LATERAL reference.
-- FDW-262: Should throw an error when we select system attribute.
SELECT xmin FROM f_test_tbl1;
ERROR: system attribute "xmin" can't be fetched from remote relation
SELECT ctid, xmax, tableoid FROM f_test_tbl1;
ERROR: system attribute "ctid" can't be fetched from remote relation
SELECT xmax, c1 FROM f_test_tbl1;
ERROR: system attribute "xmax" can't be fetched from remote relation
SELECT count(tableoid) FROM f_test_tbl1;
ERROR: system attribute "tableoid" can't be fetched from remote relation
-- FDW-391: Support whole-row reference.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c2, t1 FROM f_test_tbl1 t1
WHERE c1 = 100 ORDER BY 1;
QUERY PLAN
--------------------------------------------------------
Sort
Output: c2, t1.*
Sort Key: t1.c2
-> Foreign Scan on public.f_test_tbl1 t1
Output: c2, t1.*
Foreign Namespace: mongo_fdw_regress.test_tbl1
(6 rows)
-- Force hash-join for consistent result.
SET enable_mergejoin TO off;
SET enable_nestloop TO off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT d, d.c2, e.c1, e
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
QUERY PLAN
--------------------------------------------------------------------
Sort
Output: d.*, d.c2, e.c1, e.*
Sort Key: d.*, e.c1
-> Hash Left Join
Output: d.*, d.c2, e.c1, e.*
Hash Cond: (d.c1 = e.c8)
-> Foreign Scan on public.f_test_tbl2 d
Output: d.*, d.c2, d.c1
Foreign Namespace: mongo_fdw_regress.test_tbl2
-> Hash
Output: e.c1, e.*, e.c8
-> Foreign Scan on public.f_test_tbl1 e
Output: e.c1, e.*, e.c8
Foreign Namespace: mongo_fdw_regress.test_tbl1
(14 rows)
RESET enable_mergejoin;
RESET enable_nestloop;
-- FDW-427: The numeric value should display correctly as per precision and
-- scale defined.
SELECT c1 FROM f_test5 ORDER BY 1;
c1
-----------
-1.23
12.345678
(2 rows)
-- Number with the required precision.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(8, 6))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
c1
-----------
-1.230000
12.345678
(2 rows)
-- Number with less scale. Should round-off the scale.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(6, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
c1
-------
-1.23
12.35
(2 rows)
-- Number only with precision.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
c1
----
-1
12
(2 rows)
-- Number with improper precision and scale,
-- resulting in error "numeric field overflow".
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(3, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
ERROR: numeric field overflow
DETAIL: A field with precision 3, scale 2 must round to an absolute value less than 10^1.
-- FDW-418: Resolve data compatibility.
SELECT a FROM f_test_tbl4 ORDER BY 1;
a
---------------
0.00
25.00
25.00
25.09
9999999999.00
(5 rows)
SELECT a FROM f_test_tbl5 ORDER BY 1;
a
---
f
t
t
t
t
(5 rows)
SELECT a FROM f_test_tbl6 ORDER BY 1;
a
----
1
25
25
25
(4 rows)
SELECT a FROM f_test_tbl7 ORDER BY 1;
ERROR: value "9999999999" is out of range for type integer
-- FDW-529: Fix server crash caused due to missed handling of Param node for
-- comparison expressions while preparing query filter.
CREATE OR REPLACE FUNCTION fdw529_test_param_where() RETURNS int AS $$
DECLARE
val1 INT := 5;
val2 INT := 10;
cnt INT;
BEGIN
SELECT count(*) INTO cnt FROM f_mongo_test WHERE a > val1 AND a < val2;
RETURN cnt;
END
$$ LANGUAGE plpgsql;
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
-- This should not crash
SELECT fdw529_test_param_where();
fdw529_test_param_where
-------------------------
4
(1 row)
-- FDW-669: Fix issue join pushdown doesn't return a result for join condition
-- on sub-column. This has been fixed by omitting a dot (".") from variables
-- used (declared by $let field) to form the MongoDB query pipeline.
SELECT * FROM testlog t INNER JOIN testdevice d
ON d.level = t."logMeta.nestMore.level";
_id | log | logMeta.logMac | logMeta.nestMore.level | _id | name | mac | level
--------------------------+-----------+----------------+------------------------+--------------------------+-------------+--------------+-------
658040214898199d6e0173d0 | hello log | 001122334455 | 3 | 6580400c4898199d6e0173cd | test device | 001122334455 | 3
(1 row)
-- Cleanup
DELETE FROM f_mongo_test WHERE a != 0;
DROP TABLE l_test_tbl1;
DROP TABLE l_test_tbl2;
DROP TABLE l_mongo_test;
DROP VIEW smpl_vw;
DROP VIEW comp_vw;
DROP VIEW temp_vw;
DROP VIEW mul_tbl_view;
DROP FUNCTION test_param_where();
DROP FUNCTION fdw529_test_param_where();
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE countries;
DROP FOREIGN TABLE country_elections;
DROP FOREIGN TABLE main_exports;
DROP FOREIGN TABLE test_json;
DROP FOREIGN TABLE test_jsonb;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP FOREIGN TABLE f_test5;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE f_test_tbl6;
DROP FOREIGN TABLE f_test_tbl7;
DROP FOREIGN TABLE testlog;
DROP FOREIGN TABLE testdevice;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/expected/server_options.out 0000664 0000000 0000000 00000014752 15066665201 0021720 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
NOTICE: extension "mongo_fdw" already exists, skipping
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Port outside ushort range. Error.
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port '65537');
ERROR: port value "65537" is out of range for type unsigned short
ALTER SERVER mongo_server OPTIONS (SET port '65537');
ERROR: port value "65537" is out of range for type unsigned short
-- Validate extension, server and mapping details
CREATE OR REPLACE FUNCTION show_details(host TEXT, port TEXT, uid TEXT, pwd TEXT) RETURNS int AS $$
DECLARE
ext TEXT;
srv TEXT;
sopts TEXT;
uopts TEXT;
BEGIN
SELECT e.fdwname, srvname, array_to_string(s.srvoptions, ','), array_to_string(u.umoptions, ',')
INTO ext, srv, sopts, uopts
FROM pg_foreign_data_wrapper e LEFT JOIN pg_foreign_server s ON e.oid = s.srvfdw LEFT JOIN pg_user_mapping u ON s.oid = u.umserver
WHERE e.fdwname = 'mongo_fdw'
ORDER BY 1, 2, 3, 4;
raise notice 'Extension : %', ext;
raise notice 'Server : %', srv;
IF strpos(sopts, host) <> 0 AND strpos(sopts, port) <> 0 THEN
raise notice 'Server_Options : matched';
END IF;
IF strpos(uopts, uid) <> 0 AND strpos(uopts, pwd) <> 0 THEN
raise notice 'User_Mapping_Options : matched';
END IF;
return 1;
END;
$$ language plpgsql;
SELECT show_details(:MONGO_HOST, :MONGO_PORT, :MONGO_USER_NAME, :MONGO_PASS);
NOTICE: Extension : mongo_fdw
NOTICE: Server : mongo_server
NOTICE: Server_Options : matched
show_details
--------------
1
(1 row)
-- Create foreign tables and perform basic SQL operations
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
INSERT INTO f_mongo_test VALUES ('0', 2, 'mongo_test insert');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
2 | mongo_test insert
(2 rows)
UPDATE f_mongo_test SET b = 'mongo_test update' WHERE a = 2;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
2 | mongo_test update
(2 rows)
DELETE FROM f_mongo_test WHERE a = 2;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
-- Test SSL option when MongoDB server running in non-SSL mode.
-- Set non-boolean value, should throw an error.
ALTER SERVER mongo_server OPTIONS (ssl '1');
ERROR: ssl requires a Boolean value
ALTER SERVER mongo_server OPTIONS (ssl 'x');
ERROR: ssl requires a Boolean value
-- Check for default value i.e. false
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
-- Set 'true'.
ALTER SERVER mongo_server OPTIONS (ssl 'true');
-- Results into an error as MongoDB server is running in non-SSL mode.
\set VERBOSITY terse
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
ERROR: could not connect to server mongo_server
\set VERBOSITY default
-- Switch back to 'false'.
ALTER SERVER mongo_server OPTIONS (SET ssl 'false');
-- Should now be successful.
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
-- Alter server to add authentication_database option
ALTER SERVER mongo_server OPTIONS (ADD authentication_database 'NOT_EXIST_DB');
ALTER USER MAPPING FOR public SERVER mongo_server
OPTIONS (ADD username :MONGO_USER_NAME, password :MONGO_PASS);
-- Below query will fail with authentication error as user cannot be
-- authenticated against given authentication_database.
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
ERROR: could not connect to server mongo_server
HINT: Mongo error: "Authentication failed."
-- Now changed to valid authentication_database so select query should work.
ALTER SERVER mongo_server
OPTIONS (SET authentication_database 'mongo_fdw_regress');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
a | b
---+-----------------------
0 | mongo_test collection
(1 row)
ALTER SERVER mongo_server
OPTIONS (DROP authentication_database);
ALTER USER MAPPING FOR public SERVER mongo_server
OPTIONS (DROP username, DROP password);
-- FDW-464: Support use_remote_estimate option at server level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD use_remote_estimate 'abc11');
ERROR: use_remote_estimate requires a Boolean value
-- Check default behaviour. Should be 'false'.
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------
Sort
Sort Key: a, b
-> Foreign Scan on f_mongo_test
Foreign Namespace: mongo_fdw_regress.mongo_test
(4 rows)
-- Enable remote estimation.
ALTER SERVER mongo_server OPTIONS (ADD use_remote_estimate 'true');
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------
Sort
Sort Key: a, b
-> Foreign Scan on f_mongo_test
Foreign Namespace: mongo_fdw_regress.mongo_test
(4 rows)
-- Disable remote estimation.
ALTER SERVER mongo_server OPTIONS (SET use_remote_estimate 'false');
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
QUERY PLAN
---------------------------------------------------------
Sort
Sort Key: a, b
-> Foreign Scan on f_mongo_test
Foreign Namespace: mongo_fdw_regress.mongo_test
(4 rows)
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/mongo-c-driver/ 0000775 0000000 0000000 00000000000 15066665201 0017124 5 ustar 00root root 0000000 0000000 mongo_fdw-REL-5_5_3/mongo_fdw--1.0--1.1.sql 0000664 0000000 0000000 00000000235 15066665201 0017717 0 ustar 00root root 0000000 0000000 /* mongo_fdw/mongo_fdw--1.0--1.1.sql */
CREATE OR REPLACE FUNCTION mongo_fdw_version()
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME' LANGUAGE C;
mongo_fdw-REL-5_5_3/mongo_fdw--1.0.sql 0000664 0000000 0000000 00000001121 15066665201 0017340 0 ustar 00root root 0000000 0000000 /* mongo_fdw/mongo_fdw--1.0.sql */
-- Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
-- Portions Copyright © 2012–2014 Citus Data, Inc.
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION mongo_fdw" to load this file. \quit
CREATE FUNCTION mongo_fdw_handler()
RETURNS fdw_handler
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION mongo_fdw_validator(text[], oid)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FOREIGN DATA WRAPPER mongo_fdw
HANDLER mongo_fdw_handler
VALIDATOR mongo_fdw_validator;
mongo_fdw-REL-5_5_3/mongo_fdw--1.1.sql 0000664 0000000 0000000 00000001305 15066665201 0017345 0 ustar 00root root 0000000 0000000 /* mongo_fdw/mongo_fdw--1.1.sql */
-- Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
-- Portions Copyright © 2012–2014 Citus Data, Inc.
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION mongo_fdw" to load this file. \quit
CREATE FUNCTION mongo_fdw_handler()
RETURNS fdw_handler
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FUNCTION mongo_fdw_validator(text[], oid)
RETURNS void
AS 'MODULE_PATHNAME'
LANGUAGE C STRICT;
CREATE FOREIGN DATA WRAPPER mongo_fdw
HANDLER mongo_fdw_handler
VALIDATOR mongo_fdw_validator;
CREATE OR REPLACE FUNCTION mongo_fdw_version()
RETURNS pg_catalog.int4 STRICT
AS 'MODULE_PATHNAME' LANGUAGE C;
mongo_fdw-REL-5_5_3/mongo_fdw.c 0000664 0000000 0000000 00000424653 15066665201 0016435 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_fdw.c
* Foreign-data wrapper for remote MongoDB servers
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_fdw.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "mongo_wrapper.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/heap.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#if PG_VERSION_NUM >= 180000
#include "commands/explain_format.h"
#include "commands/explain_state.h"
#endif
#include "common/hashfn.h"
#include "common/jsonapi.h"
#include "miscadmin.h"
#include "mongo_fdw.h"
#include "mongo_query.h"
#include "nodes/nodeFuncs.h"
#if PG_VERSION_NUM >= 140000
#include "optimizer/appendinfo.h"
#endif
#include "optimizer/optimizer.h"
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#if PG_VERSION_NUM >= 160000
#include "parser/parse_relation.h"
#endif
#include "storage/ipc.h"
#include "utils/guc.h"
#include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
#include "utils/rel.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
/* Declarations for dynamic loading */
PG_MODULE_MAGIC;
/*
* In PG 9.5.1 the number will be 90501,
* our version is 5.5.3 so number will be 50503
*/
#define CODE_VERSION 50503
/*
* Macro to check unsupported sorting methods. Currently, ASC NULLS FIRST and
* DESC NULLS LAST give the same sorting result on MongoDB and Postgres. So,
* sorting methods other than these are not pushed down.
*/
#if PG_VERSION_NUM >= 180000
#define IS_PATHKEY_PUSHABLE(pathkey) \
((pathkey->pk_cmptype == COMPARE_LT && pathkey->pk_nulls_first) || \
(pathkey->pk_cmptype != COMPARE_LT && !pathkey->pk_nulls_first))
#else
#define IS_PATHKEY_PUSHABLE(pathkey) \
((pathkey->pk_strategy == BTLessStrategyNumber && pathkey->pk_nulls_first) || \
(pathkey->pk_strategy != BTLessStrategyNumber && !pathkey->pk_nulls_first))
#endif
/* Maximum path keys supported by MongoDB */
#define MAX_PATHKEYS 32
/*
* The number of rows in a foreign relation are estimated to be so less that
* an in-memory sort on those many rows wouldn't cost noticeably higher than
* the underlying scan. Hence for now, cost sorts same as underlying scans.
*/
#define DEFAULT_MONGO_SORT_MULTIPLIER 1
/* GUC variables. */
static bool enable_join_pushdown = true;
static bool enable_order_by_pushdown = true;
static bool enable_aggregate_pushdown = true;
static bool log_remote_query = false;
/*
* This enum describes what's kept in the fdw_private list for a ForeignPath.
* We store:
*
* 1) Boolean flag showing if the remote query has the final sort
* 2) Boolean flag showing if the remote query has the LIMIT clause
*/
enum FdwPathPrivateIndex
{
/* has-final-sort flag (as an integer Value node) */
FdwPathPrivateHasFinalSort,
/* has-limit flag (as an integer Value node) */
FdwPathPrivateHasLimit
};
extern PGDLLEXPORT void _PG_init(void);
PG_FUNCTION_INFO_V1(mongo_fdw_handler);
PG_FUNCTION_INFO_V1(mongo_fdw_version);
/* FDW callback routines */
static void mongoGetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
static void mongoGetForeignPaths(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid);
static ForeignScan *mongoGetForeignPlan(PlannerInfo *root,
RelOptInfo *foreignrel,
Oid foreigntableid,
ForeignPath *best_path,
List *targetlist,
List *restrictionClauses,
Plan *outer_plan);
static void mongoExplainForeignScan(ForeignScanState *node, ExplainState *es);
static void mongoBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *mongoIterateForeignScan(ForeignScanState *node);
static void mongoEndForeignScan(ForeignScanState *node);
static void mongoReScanForeignScan(ForeignScanState *node);
static TupleTableSlot *mongoExecForeignUpdate(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);
static TupleTableSlot *mongoExecForeignDelete(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);
static void mongoEndForeignModify(EState *estate,
ResultRelInfo *resultRelInfo);
#if PG_VERSION_NUM >= 140000
static void mongoAddForeignUpdateTargets(PlannerInfo *root,
Index rtindex,
RangeTblEntry *target_rte,
Relation target_relation);
#else
static void mongoAddForeignUpdateTargets(Query *parsetree,
RangeTblEntry *target_rte,
Relation target_relation);
#endif
static void mongoBeginForeignModify(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
List *fdw_private,
int subplan_index,
int eflags);
static TupleTableSlot *mongoExecForeignInsert(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);
static List *mongoPlanForeignModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);
static void mongoExplainForeignModify(ModifyTableState *mtstate,
ResultRelInfo *rinfo,
List *fdw_private,
int subplan_index,
ExplainState *es);
static bool mongoAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
static void mongoBeginForeignInsert(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo);
static void mongoEndForeignInsert(EState *estate,
ResultRelInfo *resultRelInfo);
static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
static void mongoGetForeignUpperPaths(PlannerInfo *root,
UpperRelationKind stage,
RelOptInfo *input_rel,
RelOptInfo *output_rel,
void *extra);
/*
* Helper functions
*/
static double foreign_table_document_count(Oid foreignTableId);
static HTAB *column_mapping_hash(Oid foreignTableId, List *columnList,
List *colNameList, List *colIsInnerList,
uint32 relType);
static void fill_tuple_slot(const BSON *bsonDocument,
const char *bsonDocumentKey,
HTAB *columnMappingHash,
Datum *columnValues,
bool *columnNulls,
uint32 relType);
static bool column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId);
static Datum column_value_array(BSON_ITERATOR *bsonIterator, Oid valueTypeId);
static Datum column_value(BSON_ITERATOR *bsonIterator,
Oid columnTypeId,
int32 columnTypeMod);
static void mongo_free_scan_state(MongoFdwModifyState *fmstate);
static int mongo_acquire_sample_rows(Relation relation,
int errorLevel,
HeapTuple *sampleRows,
int targetRowCount,
double *totalRowCount,
double *totalDeadRowCount);
static void mongo_fdw_exit(int code, Datum arg);
static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinPathExtraData *extra);
static void mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info);
static bool mongo_foreign_grouping_ok(PlannerInfo *root,
RelOptInfo *grouped_rel,
Node *havingQual);
static void mongo_add_foreign_grouping_paths(PlannerInfo *root,
RelOptInfo *input_rel,
RelOptInfo *grouped_rel,
GroupPathExtraData *extra);
static void mongo_add_foreign_final_paths(PlannerInfo *root,
RelOptInfo *input_rel,
RelOptInfo *final_rel,
FinalPathExtraData *extra);
static void mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost,
Cost *total_cost, Oid foreigntableid);
static List *mongo_get_useful_ecs_for_relation(PlannerInfo *root,
RelOptInfo *rel);
static List *mongo_get_useful_pathkeys_for_relation(PlannerInfo *root,
RelOptInfo *rel);
#if PG_VERSION_NUM >= 170000
static void mongo_add_paths_with_pathkeys(PlannerInfo *root,
RelOptInfo *rel,
Path *epq_path,
Cost base_startup_cost,
Cost base_total_cost,
List *restrictlist);
#else
static void mongo_add_paths_with_pathkeys(PlannerInfo *root,
RelOptInfo *rel,
Path *epq_path,
Cost base_startup_cost,
Cost base_total_cost);
#endif
static EquivalenceMember *mongo_find_em_for_rel_target(PlannerInfo *root,
EquivalenceClass *ec,
RelOptInfo *rel);
static void mongo_add_foreign_ordered_paths(PlannerInfo *root,
RelOptInfo *input_rel,
RelOptInfo *ordered_rel);
/* The null action object used for pure validation */
#if PG_VERSION_NUM >= 180000
const JsonSemAction nullSemAction =
{
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL
};
#else
JsonSemAction nullSemAction =
{
NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL
};
#endif
/*
* Library load-time initalization, sets on_proc_exit() callback for
* backend shutdown.
*/
void
_PG_init(void)
{
/*
* Sometimes getting a join or sorted result from MongoDB server is slower
* than performing those operations locally. To have that flexibility add
* a few GUCs to control those push-downs.
*/
DefineCustomBoolVariable("mongo_fdw.enable_join_pushdown",
"enable/disable join pushdown",
NULL,
&enable_join_pushdown,
true,
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("mongo_fdw.enable_order_by_pushdown",
"Enable/Disable ORDER BY push down",
NULL,
&enable_order_by_pushdown,
true,
PGC_SUSET,
0,
NULL,
NULL,
NULL);
DefineCustomBoolVariable("mongo_fdw.enable_aggregate_pushdown",
"Enable/Disable aggregate push down",
NULL,
&enable_aggregate_pushdown,
true,
PGC_SUSET,
0,
NULL,
NULL,
NULL);
/*
* GUC to control whether remote queries are included in PostgreSQL logs.
* Disabled by default.
*/
DefineCustomBoolVariable("mongo_fdw.log_remote_query",
"Enable/Disable remote query logging",
NULL,
&log_remote_query,
false,
PGC_SUSET,
0,
NULL,
NULL,
NULL);
/* Initialize MongoDB C driver */
mongoc_init();
on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL));
}
/*
* mongo_fdw_handler
* Creates and returns a struct with pointers to foreign table callback
* functions.
*/
Datum
mongo_fdw_handler(PG_FUNCTION_ARGS)
{
FdwRoutine *fdwRoutine = makeNode(FdwRoutine);
/* Functions for scanning foreign tables */
fdwRoutine->GetForeignRelSize = mongoGetForeignRelSize;
fdwRoutine->GetForeignPaths = mongoGetForeignPaths;
fdwRoutine->GetForeignPlan = mongoGetForeignPlan;
fdwRoutine->BeginForeignScan = mongoBeginForeignScan;
fdwRoutine->IterateForeignScan = mongoIterateForeignScan;
fdwRoutine->ReScanForeignScan = mongoReScanForeignScan;
fdwRoutine->EndForeignScan = mongoEndForeignScan;
/* Support for insert/update/delete */
fdwRoutine->AddForeignUpdateTargets = mongoAddForeignUpdateTargets;
fdwRoutine->PlanForeignModify = mongoPlanForeignModify;
fdwRoutine->BeginForeignModify = mongoBeginForeignModify;
fdwRoutine->ExecForeignInsert = mongoExecForeignInsert;
fdwRoutine->ExecForeignUpdate = mongoExecForeignUpdate;
fdwRoutine->ExecForeignDelete = mongoExecForeignDelete;
fdwRoutine->EndForeignModify = mongoEndForeignModify;
/* Support for EXPLAIN */
fdwRoutine->ExplainForeignScan = mongoExplainForeignScan;
fdwRoutine->ExplainForeignModify = mongoExplainForeignModify;
/* Support for ANALYZE */
fdwRoutine->AnalyzeForeignTable = mongoAnalyzeForeignTable;
/* Partition routing and/or COPY from */
fdwRoutine->BeginForeignInsert = mongoBeginForeignInsert;
fdwRoutine->EndForeignInsert = mongoEndForeignInsert;
/* Support function for join push-down */
fdwRoutine->GetForeignJoinPaths = mongoGetForeignJoinPaths;
/* Support functions for upper relation push-down */
fdwRoutine->GetForeignUpperPaths = mongoGetForeignUpperPaths;
PG_RETURN_POINTER(fdwRoutine);
}
/*
* mongo_fdw_exit
* Exit callback function.
*/
static void
mongo_fdw_exit(int code, Datum arg)
{
mongo_cleanup_connection();
/* Release all memory and other resources allocated by the driver */
mongoc_cleanup();
}
/*
* MongoGetForeignRelSize
* Obtains relation size estimates for mongo foreign table.
*/
static void
mongoGetForeignRelSize(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid)
{
RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
MongoFdwRelationInfo *fpinfo;
MongoFdwOptions *options;
ListCell *lc;
char *relname;
char *database;
char *refname;
/*
* We use MongoFdwRelationInfo to pass various information to subsequent
* functions.
*/
fpinfo = (MongoFdwRelationInfo *) palloc0(sizeof(MongoFdwRelationInfo));
baserel->fdw_private = (void *) fpinfo;
/*
* Identify which baserestrictinfo clauses can be sent to the remote
* server and which can't. Only the OpExpr clauses are sent to the remote
* server.
*/
foreach(lc, baserel->baserestrictinfo)
{
RestrictInfo *ri = (RestrictInfo *) lfirst(lc);
if (mongo_is_foreign_expr(root, baserel, ri->clause, false))
fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri);
else
fpinfo->local_conds = lappend(fpinfo->local_conds, ri);
}
/* Base foreign tables need to be pushed down always. */
fpinfo->pushdown_safe = true;
/* Fetch options */
options = mongo_get_options(foreigntableid);
/*
* Retrieve exact document count for remote collection if asked,
* otherwise, use default estimate in planning.
*/
if (options->use_remote_estimate)
{
double documentCount = foreign_table_document_count(foreigntableid);
if (documentCount > 0.0)
{
double rowSelectivity;
/*
* We estimate the number of rows returned after restriction
* qualifiers are applied. This will be more accurate if analyze
* is run on this relation.
*/
rowSelectivity = clauselist_selectivity(root,
baserel->baserestrictinfo,
0, JOIN_INNER, NULL);
baserel->rows = clamp_row_est(documentCount * rowSelectivity);
}
else
ereport(DEBUG1,
(errmsg("could not retrieve document count for collection"),
errhint("Falling back to default estimates in planning.")));
}
relname = options->collectionName;
database = options->svr_database;
fpinfo->base_relname = relname;
/*
* Set the name of relation in fpinfo, while we are constructing it here.
* It will be used to build the string describing the join relation in
* EXPLAIN output. We can't know whether the VERBOSE option is specified
* or not, so always schema-qualify the foreign table name.
*/
fpinfo->relation_name = makeStringInfo();
refname = rte->eref->aliasname;
appendStringInfo(fpinfo->relation_name, "%s.%s",
quote_identifier(database),
quote_identifier(relname));
if (*refname && strcmp(refname, relname) != 0)
appendStringInfo(fpinfo->relation_name, " %s",
quote_identifier(rte->eref->aliasname));
/* Also store the options in fpinfo for further use */
fpinfo->options = options;
/*
* Store aggregation enable/disable option in the fpinfo directly for
* further use. This flag can be useful when options are not accessible
* in the recursive cases.
*/
fpinfo->is_agg_scanrel_pushable = options->enable_aggregate_pushdown;
/* Set the flag is_order_by_pushable of the base relation */
fpinfo->is_order_by_pushable = options->enable_order_by_pushdown;
}
/*
* mongoGetForeignPaths
* Creates the only scan path used to execute the query.
*
* Note that MongoDB may decide to use an underlying index for this scan, but
* that decision isn't deterministic or visible to us. We therefore create a
* single table scan path.
*/
static void
mongoGetForeignPaths(PlannerInfo *root,
RelOptInfo *baserel,
Oid foreigntableid)
{
Path *foreignPath;
MongoFdwOptions *options;
Cost startupCost = 0;
Cost totalCost = 0;
/* Fetch options */
options = mongo_get_options(foreigntableid);
/*
* Retrieve exact document count for remote collection if asked,
* otherwise, use default estimate in planning.
*/
if (options->use_remote_estimate)
{
double documentCount = foreign_table_document_count(foreigntableid);
if (documentCount > 0.0)
{
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) baserel->fdw_private;
double tupleFilterCost = baserel->baserestrictcost.per_tuple;
double inputRowCount;
double documentSelectivity;
double foreignTableSize;
int32 documentWidth;
BlockNumber pageCount;
double totalDiskAccessCost;
double cpuCostPerDoc;
double cpuCostPerRow;
double totalCpuCost;
double connectionCost;
List *opExpressionList;
/*
* We estimate the number of rows returned after restriction
* qualifiers are applied by MongoDB.
*/
opExpressionList = fpinfo->remote_conds;
documentSelectivity = clauselist_selectivity(root,
opExpressionList, 0,
JOIN_INNER, NULL);
inputRowCount = clamp_row_est(documentCount * documentSelectivity);
/*
* We estimate disk costs assuming a sequential scan over the
* data. This is an inaccurate assumption as Mongo scatters the
* data over disk pages, and may rely on an index to retrieve the
* data. Still, this should at least give us a relative cost.
*/
documentWidth = get_relation_data_width(foreigntableid,
baserel->attr_widths);
foreignTableSize = documentCount * documentWidth;
pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ);
totalDiskAccessCost = seq_page_cost * pageCount;
/*
* The cost of processing a document returned by Mongo (input row)
* is 5x the cost of processing a regular row.
*/
cpuCostPerDoc = cpu_tuple_cost;
cpuCostPerRow = (cpu_tuple_cost * MONGO_TUPLE_COST_MULTIPLIER) + tupleFilterCost;
totalCpuCost = (cpuCostPerDoc * documentCount) + (cpuCostPerRow * inputRowCount);
connectionCost = MONGO_CONNECTION_COST_MULTIPLIER * seq_page_cost;
startupCost = baserel->baserestrictcost.startup + connectionCost;
totalCost = startupCost + totalDiskAccessCost + totalCpuCost;
}
else
ereport(DEBUG1,
(errmsg("could not retrieve document count for collection"),
errhint("Falling back to default estimates in planning.")));
}
else
{
/* Estimate default costs */
mongoEstimateCosts(baserel, &startupCost, &totalCost, foreigntableid);
}
/* Create a foreign path node */
#if PG_VERSION_NUM >= 180000
foreignPath = (Path *) create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
baserel->rows,
0,
startupCost,
totalCost,
NIL, /* no pathkeys */
baserel->lateral_relids,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private data */
#elif PG_VERSION_NUM >= 170000
foreignPath = (Path *) create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
baserel->rows,
startupCost,
totalCost,
NIL, /* no pathkeys */
baserel->lateral_relids,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private data */
#else
foreignPath = (Path *) create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
baserel->rows,
startupCost,
totalCost,
NIL, /* no pathkeys */
baserel->lateral_relids,
NULL, /* no extra plan */
NIL); /* no fdw_private list */
#endif
/* Add foreign path as the only possible path */
add_path(baserel, foreignPath);
/* Add paths with pathkeys */
#if PG_VERSION_NUM >= 170000
mongo_add_paths_with_pathkeys(root, baserel, NULL, startupCost, totalCost,
NIL);
#else
mongo_add_paths_with_pathkeys(root, baserel, NULL, startupCost, totalCost);
#endif
}
/*
* mongoGetForeignPlan
* Creates a foreign scan plan node for scanning the MongoDB collection.
*
* Note that MongoDB may decide to use an underlying index for this
* scan, but that decision isn't deterministic or visible to us.
*/
static ForeignScan *
mongoGetForeignPlan(PlannerInfo *root,
RelOptInfo *foreignrel,
Oid foreigntableid,
ForeignPath *best_path,
List *targetList,
List *restrictionClauses,
Plan *outer_plan)
{
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private;
Index scan_relid = foreignrel->relid;
ForeignScan *foreignScan;
List *fdw_private;
List *columnList;
List *scan_var_list;
ListCell *lc;
List *local_exprs = NIL;
List *remote_exprs = NIL;
List *fdw_scan_tlist = NIL;
List *column_name_list = NIL;
List *is_inner_column_list = NIL;
List *quals = NIL;
MongoFdwRelType mongofdwreltype;
MongoRelQualInfo *qual_info;
MongoFdwRelationInfo *ofpinfo = NULL;
List *pathKeyList = NIL;
List *isAscSortList = NIL;
bool has_final_sort = false;
bool has_limit = false;
int64 limit_value;
int64 offset_value;
/*
* Get FDW private data created by mongoGetForeignUpperPaths(), if any.
*/
if (best_path->fdw_private)
{
has_final_sort = intVal(list_nth(best_path->fdw_private,
FdwPathPrivateHasFinalSort));
has_limit = intVal(list_nth(best_path->fdw_private,
FdwPathPrivateHasLimit));
}
/* Set scan relation id */
if (IS_SIMPLE_REL(foreignrel))
scan_relid = foreignrel->relid;
else
{
/* Join/Upper relation - set scan_relid to 0. */
scan_relid = 0;
Assert(!restrictionClauses);
/* Extract local expressions from local conditions */
foreach(lc, fpinfo->local_conds)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
local_exprs = lappend(local_exprs, rinfo->clause);
}
/* Extract remote expressions from remote conditions */
foreach(lc, fpinfo->remote_conds)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
remote_exprs = lappend(remote_exprs, rinfo->clause);
}
}
if (IS_UPPER_REL(foreignrel))
scan_var_list = pull_var_clause((Node *) fpinfo->grouped_tlist,
PVC_RECURSE_AGGREGATES);
else
scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs,
PVC_RECURSE_PLACEHOLDERS);
/* System attributes are not allowed. */
foreach(lc, scan_var_list)
{
Var *var = lfirst(lc);
const FormData_pg_attribute *attr;
Assert(IsA(var, Var));
if (var->varattno >= 0)
continue;
attr = SystemAttributeDefinition(var->varattno);
ereport(ERROR,
(errcode(ERRCODE_FDW_COLUMN_NAME_NOT_FOUND),
errmsg("system attribute \"%s\" can't be fetched from remote relation",
attr->attname.data)));
}
/*
* Separate the restrictionClauses into those that can be executed
* remotely and those that can't. baserestrictinfo clauses that were
* previously determined to be safe or unsafe are shown in
* fpinfo->remote_conds and fpinfo->local_conds. Anything else in the
* restrictionClauses list will be a join clause, which we have to check
* for remote-safety. Only the OpExpr clauses are sent to the remote
* server.
*/
foreach(lc, restrictionClauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
/* Ignore pseudoconstants, they are dealt with elsewhere */
if (rinfo->pseudoconstant)
continue;
if (list_member_ptr(fpinfo->remote_conds, rinfo))
remote_exprs = lappend(remote_exprs, rinfo->clause);
else if (list_member_ptr(fpinfo->local_conds, rinfo))
local_exprs = lappend(local_exprs, rinfo->clause);
else if (IsA(rinfo->clause, OpExpr) &&
mongo_is_foreign_expr(root, foreignrel, rinfo->clause, false))
remote_exprs = lappend(remote_exprs, rinfo->clause);
else
local_exprs = lappend(local_exprs, rinfo->clause);
}
/* Add local expression Var nodes to scan_var_list. */
scan_var_list = list_concat_unique(NIL, scan_var_list);
if (IS_UPPER_REL(foreignrel))
scan_var_list = list_concat_unique(scan_var_list,
pull_var_clause((Node *) local_exprs,
PVC_RECURSE_AGGREGATES));
else
scan_var_list = list_concat_unique(scan_var_list,
pull_var_clause((Node *) local_exprs,
PVC_RECURSE_PLACEHOLDERS));
if (IS_JOIN_REL(foreignrel))
{
/*
* For join relations, the planner needs a targetlist, which
* represents the output of the ForeignScan node.
*/
fdw_scan_tlist = add_to_flat_tlist(NIL, scan_var_list);
/*
* Ensure that the outer plan produces a tuple whose descriptor
* matches our scan tuple slot. Also, remove the local conditions
* from the outer plan's quals, lest they be evaluated twice, once by
* the local plan and once by the scan.
*/
if (outer_plan)
{
/*
* First, update the plan's qual list if possible. In some cases,
* the quals might be enforced below the topmost plan level, in
* which case we'll fail to remove them; it's not worth working
* harder than this.
*/
foreach(lc, local_exprs)
{
Node *qual = lfirst(lc);
outer_plan->qual = list_delete(outer_plan->qual, qual);
/*
* For an inner join, the local conditions of the foreign scan
* plan can be part of the joinquals as well. (They might
* also be in the mergequals or hashquals, but we can't touch
* those without breaking the plan.)
*/
if (IsA(outer_plan, NestLoop) ||
IsA(outer_plan, MergeJoin) ||
IsA(outer_plan, HashJoin))
{
Join *join_plan = (Join *) outer_plan;
if (join_plan->jointype == JOIN_INNER)
join_plan->joinqual = list_delete(join_plan->joinqual,
qual);
}
}
/*
* Now fix the subplan's tlist --- this might result in inserting
* a Result node atop the plan tree.
*/
outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
best_path->path.parallel_safe);
}
}
else if (IS_UPPER_REL(foreignrel))
{
/*
* scan_var_list should have expressions and not TargetEntry nodes.
* However, grouped_tlist created has TLEs, and thus retrieve them
* into scan_var_list.
*/
scan_var_list = list_concat_unique(NIL,
get_tlist_exprs(fpinfo->grouped_tlist,
false));
/*
* The targetlist computed while assessing push-down safety represents
* the result we expect from the foreign server.
*/
fdw_scan_tlist = fpinfo->grouped_tlist;
local_exprs = extract_actual_clauses(fpinfo->local_conds, false);
}
/* Form column list required for query execution from scan_var_list. */
columnList = mongo_get_column_list(root, foreignrel, scan_var_list,
&column_name_list,
&is_inner_column_list);
/*
* Identify the relation type. We can have a simple base rel, join rel,
* upper rel, and upper rel with join rel inside. Find out that.
*/
if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(fpinfo->outerrel))
mongofdwreltype = UPPER_JOIN_REL;
else if (IS_UPPER_REL(foreignrel))
mongofdwreltype = UPPER_REL;
else if (IS_JOIN_REL(foreignrel))
mongofdwreltype = JOIN_REL;
else
mongofdwreltype = BASE_REL;
/*
* We use MongoRelQualInfo to pass various information related to joining
* quals and grouping target to fdw_private which is used to form
* equivalent MongoDB query during the execution phase.
*/
qual_info = (MongoRelQualInfo *) palloc(sizeof(MongoRelQualInfo));
qual_info->root = root;
qual_info->foreignRel = foreignrel;
qual_info->exprColHash = NULL;
qual_info->colNameList = NIL;
qual_info->colNumList = NIL;
qual_info->rtiList = NIL;
qual_info->isOuterList = NIL;
qual_info->is_having = false;
qual_info->is_agg_column = false;
qual_info->aggTypeList = NIL;
qual_info->aggColList = NIL;
qual_info->isHavingList = NIL;
/*
* Prepare separate lists of information. This information would be
* useful at the time of execution to prepare the MongoDB query.
*/
if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
{
ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private;
/*
* Save foreign relation and relid's of an outer relation involved in
* the join depending on the relation type.
*/
if (mongofdwreltype == UPPER_JOIN_REL)
{
/* For aggregation over join relation */
qual_info->foreignRel = fpinfo->outerrel;
qual_info->outerRelids = ofpinfo->outerrel->relids;
}
else if (mongofdwreltype == UPPER_REL)
{
/* For aggregation relation */
qual_info->foreignRel = fpinfo->outerrel;
qual_info->outerRelids = fpinfo->outerrel->relids;
}
else
{
Assert(mongofdwreltype == JOIN_REL);
qual_info->foreignRel = foreignrel;
qual_info->outerRelids = fpinfo->outerrel->relids;
}
/*
* Extract required data of columns involved in join clauses and
* append it into the various lists required to pass it to the
* executor.
*
* Check and extract data for outer relation and its join clauses in
* case of aggregation on top of the join operation.
*/
if (IS_JOIN_REL(foreignrel) && fpinfo->joinclauses)
mongo_prepare_qual_info(fpinfo->joinclauses, qual_info);
else if (IS_JOIN_REL(fpinfo->outerrel) && ofpinfo->joinclauses)
mongo_prepare_qual_info(ofpinfo->joinclauses, qual_info);
/*
* Extract required data of columns involved in the WHERE clause and
* append it into the various lists required to pass it to the
* executor.
*/
if (IS_JOIN_REL(foreignrel) && fpinfo->remote_conds)
mongo_prepare_qual_info(fpinfo->remote_conds, qual_info);
/* Gather required information of an upper relation */
if (IS_UPPER_REL(foreignrel))
{
/* Extract remote expressions from the remote conditions */
foreach(lc, ofpinfo->remote_conds)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
Assert(IsA(rinfo, RestrictInfo));
quals = lappend(quals, rinfo->clause);
}
/* Extract WHERE clause column information */
mongo_prepare_qual_info(quals, qual_info);
/*
* Extract grouping target information i.e grouping operation and
* grouping clause.
*/
mongo_prepare_qual_info(scan_var_list, qual_info);
/* Extract HAVING clause information */
if (fpinfo->remote_conds)
{
qual_info->is_having = true;
mongo_prepare_qual_info(fpinfo->remote_conds, qual_info);
}
}
else
quals = remote_exprs;
}
else
{
quals = remote_exprs;
/* For baserel */
qual_info->foreignRel = foreignrel;
qual_info->outerRelids = NULL;
/*
* Extract required data of columns involved in WHERE clause of the
* simple relation.
*/
mongo_prepare_qual_info(quals, qual_info);
}
/*
* Check the ORDER BY clause, and if we found any useful pathkeys, then
* store the required information.
*/
foreach(lc, best_path->path.pathkeys)
{
EquivalenceMember *em;
PathKey *pathkey = lfirst(lc);
Expr *em_expr;
if (has_final_sort)
{
/*
* By construction, foreignrel is the input relation to the final
* sort.
*/
em = mongo_find_em_for_rel_target(root, pathkey->pk_eclass,
foreignrel);
}
else
em = mongo_find_em_for_rel(root, pathkey->pk_eclass,
qual_info->foreignRel);
/*
* We don't expect any error here; it would mean that shippability
* wasn't verified earlier. For the same reason, we don't recheck
* shippability of the sort operator.
*/
if (em == NULL)
elog(ERROR, "could not find pathkey item to sort");
/* Ignore binary-compatible relabeling */
em_expr = em->em_expr;
while (IsA(em_expr, RelabelType))
em_expr = ((RelabelType *) em_expr)->arg;
Assert(IsA(em_expr, Var));
pathKeyList = list_append_unique(pathKeyList, (Var *) em_expr);
#if PG_VERSION_NUM >= 180000
if (pathkey->pk_cmptype == COMPARE_LT)
#else
if (pathkey->pk_strategy == BTLessStrategyNumber)
#endif
isAscSortList = lappend_int(isAscSortList, 1);
else
isAscSortList = lappend_int(isAscSortList, -1);
}
/* Extract the required data of columns involved in the ORDER BY clause */
mongo_prepare_qual_info(pathKeyList, qual_info);
/* Destroy hash table used to get unique column info */
hash_destroy(qual_info->exprColHash);
/*
* Retrieve limit and offset values, which needs to be passed to the
* executor. If any of the two clauses (limit or offset) is missing from
* the query, then default value -1 is used to indicate the same.
*/
limit_value = offset_value = -1;
if (has_limit)
{
Node *node;
node = root->parse->limitCount;
if (node)
{
Assert(nodeTag(node) == T_Const &&
((Const *) node)->consttype == INT8OID);
/* Treat NULL as no limit */
if (!((Const *) node)->constisnull)
limit_value = DatumGetInt64(((Const *) node)->constvalue);
}
node = root->parse->limitOffset;
if (node)
{
Assert(nodeTag(node) == T_Const &&
((Const *) node)->consttype == INT8OID);
/* Treat NULL as no offset */
if (!((Const *) node)->constisnull)
offset_value = DatumGetInt64(((Const *) node)->constvalue);
}
}
/*
* Unlike postgres_fdw, remote query formation is done in the execution
* state. There is NO way to get the correct information required to form
* a remote query during the execution state. So, we are gathering
* information required to form a MongoDB query in the planning state and
* passing it to the execution state through fdw_private.
*/
/*
* Build the fdw_private list that will be available to the executor.
* Items in the list must match enum mongoFdwScanPrivateIndex.
*/
fdw_private = list_make2(columnList, quals);
/* Append relation type */
fdw_private = lappend(fdw_private, makeInteger(mongofdwreltype));
fdw_private = lappend(fdw_private, qual_info->colNameList);
fdw_private = lappend(fdw_private, qual_info->colNumList);
fdw_private = lappend(fdw_private, qual_info->rtiList);
fdw_private = lappend(fdw_private, qual_info->isOuterList);
fdw_private = lappend(fdw_private, pathKeyList);
fdw_private = lappend(fdw_private, isAscSortList);
fdw_private = lappend(fdw_private, makeInteger(has_limit));
fdw_private = lappend(fdw_private, makeInteger(limit_value));
fdw_private = lappend(fdw_private, makeInteger(offset_value));
if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel))
{
MongoFdwRelationInfo *tfpinfo = NULL;
fdw_private = lappend(fdw_private, qual_info->aggTypeList);
fdw_private = lappend(fdw_private, qual_info->aggColList);
fdw_private = lappend(fdw_private, ofpinfo->groupbyColList);
fdw_private = lappend(fdw_private, remote_exprs);
fdw_private = lappend(fdw_private, qual_info->isHavingList);
fdw_private = lappend(fdw_private,
makeString(fpinfo->relation_name->data));
fdw_private = lappend(fdw_private, column_name_list);
fdw_private = lappend(fdw_private, is_inner_column_list);
if (mongofdwreltype == JOIN_REL)
tfpinfo = fpinfo;
else if (mongofdwreltype == UPPER_JOIN_REL)
tfpinfo = ofpinfo;
if (tfpinfo)
{
fdw_private = lappend(fdw_private,
list_make2(makeString(tfpinfo->inner_relname),
makeString(tfpinfo->outer_relname)));
fdw_private = lappend(fdw_private, tfpinfo->joinclauses);
fdw_private = lappend(fdw_private, makeInteger(tfpinfo->jointype));
}
}
/* Create the foreign scan node */
foreignScan = make_foreignscan(targetList, local_exprs,
scan_relid,
NIL, /* No expressions to evaluate */
fdw_private
,fdw_scan_tlist
,NIL
,outer_plan
);
return foreignScan;
}
/*
* mongoExplainForeignScan
* Produces extra output for the Explain command.
*/
static void
mongoExplainForeignScan(ForeignScanState *node, ExplainState *es)
{
ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
RangeTblEntry *rte;
EState *estate = node->ss.ps.state;
List *fdw_private = fsplan->fdw_private;
int rtindex;
if (fsplan->scan.scanrelid > 0)
rtindex = fsplan->scan.scanrelid;
else
#if PG_VERSION_NUM >= 160000
rtindex = bms_next_member(fsplan->fs_base_relids, -1);
#else
rtindex = bms_next_member(fsplan->fs_relids, -1);
#endif
rte = rt_fetch(rtindex, estate->es_range_table);
if (list_length(fdw_private) > mongoFdwPrivateRelations)
{
char *relations = strVal(list_nth(fdw_private,
mongoFdwPrivateRelations));
ExplainPropertyText("Foreign Namespace", relations, es);
}
else
{
StringInfo namespaceName;
MongoFdwOptions *options;
options = mongo_get_options(rte->relid);
/* Construct fully qualified collection name */
namespaceName = makeStringInfo();
appendStringInfo(namespaceName, "%s.%s", options->svr_database,
options->collectionName);
ExplainPropertyText("Foreign Namespace", namespaceName->data, es);
mongo_free_options(options);
}
}
static void
mongoExplainForeignModify(ModifyTableState *mtstate,
ResultRelInfo *rinfo,
List *fdw_private,
int subplan_index,
ExplainState *es)
{
MongoFdwOptions *options;
StringInfo namespaceName;
Oid foreignTableId;
foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc);
options = mongo_get_options(foreignTableId);
/* Construct fully qualified collection name */
namespaceName = makeStringInfo();
appendStringInfo(namespaceName, "%s.%s", options->svr_database,
options->collectionName);
mongo_free_options(options);
ExplainPropertyText("Foreign Namespace", namespaceName->data, es);
}
/*
* mongoBeginForeignScan
* Connects to the MongoDB server, and opens a cursor that uses the
* database name, collection name, and the remote query to send to the
* server.
*
* The function also creates a hash table that maps referenced
* column names to column index and type information.
*/
static void
mongoBeginForeignScan(ForeignScanState *node, int eflags)
{
MONGO_CONN *mongoConnection;
List *columnList;
HTAB *columnMappingHash;
MongoFdwOptions *options;
MongoFdwModifyState *fmstate;
RangeTblEntry *rte;
EState *estate = node->ss.ps.state;
ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan;
List *fdw_private = fsplan->fdw_private;
Oid userid;
ForeignServer *server;
UserMapping *user;
ForeignTable *table;
int rtindex;
List *colNameList = NIL;
List *colIsInnerList = NIL;
/* If Explain with no Analyze, do nothing */
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
return;
fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState));
/*
* Identify which user to do the remote access as. This should match what
* ExecCheckRTEPerms() does. In the case of a join, use the
* lowest-numbered member RTE as a representative; we would get the same
* result from any.
*/
if (fsplan->scan.scanrelid > 0)
rtindex = fsplan->scan.scanrelid;
else
#if PG_VERSION_NUM >= 160000
rtindex = bms_next_member(fsplan->fs_base_relids, -1);
#else
rtindex = bms_next_member(fsplan->fs_relids, -1);
#endif
#if PG_VERSION_NUM >= 160000
rte = exec_rt_fetch(rtindex, estate);
userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId();
#else
rte = rt_fetch(rtindex, estate->es_range_table);
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
#endif
/* Get info about foreign table. */
fmstate->rel = node->ss.ss_currentRelation;
table = GetForeignTable(rte->relid);
server = GetForeignServer(table->serverid);
user = GetUserMapping(userid, server->serverid);
options = mongo_get_options(rte->relid);
/*
* Get connection to the foreign server. Connection manager will
* establish new connection if necessary.
*/
mongoConnection = mongo_get_connection(server, user, options);
columnList = list_nth(fdw_private, mongoFdwPrivateColumnList);
fmstate->relType = intVal(list_nth(fdw_private, mongoFdwPrivateRelType));
if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL)
{
colNameList = list_nth(fdw_private, mongoFdwPrivateColNameList);
colIsInnerList = list_nth(fdw_private, mongoFdwPrivateColIsInnerList);
}
columnMappingHash = column_mapping_hash(rte->relid, columnList,
colNameList, colIsInnerList,
fmstate->relType);
/* Create and set foreign execution state */
fmstate->columnMappingHash = columnMappingHash;
fmstate->mongoConnection = mongoConnection;
fmstate->options = options;
node->fdw_state = (void *) fmstate;
}
/*
* mongoIterateForeignScan
* Opens a Mongo cursor that uses the database name, collection name, and
* the remote query to send to the server.
*
* Reads the next document from MongoDB, converts it to a PostgreSQL tuple,
* and stores the converted tuple into the ScanTupleSlot as a virtual tuple.
*/
static TupleTableSlot *
mongoIterateForeignScan(ForeignScanState *node)
{
MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state;
TupleTableSlot *tupleSlot = node->ss.ss_ScanTupleSlot;
MONGO_CURSOR *mongoCursor = fmstate->mongoCursor;
HTAB *columnMappingHash = fmstate->columnMappingHash;
TupleDesc tupleDescriptor = tupleSlot->tts_tupleDescriptor;
Datum *columnValues = tupleSlot->tts_values;
bool *columnNulls = tupleSlot->tts_isnull;
int32 columnCount = tupleDescriptor->natts;
/* Create cursor for collection name and set query */
if (mongoCursor == NULL)
{
BSON *queryDocument;
char *collectionName;
/*
* We construct the query document to have MongoDB filter its rows. We
* could also construct a column name document here to retrieve only
* the needed columns. However, we found this optimization to degrade
* performance on the MongoDB server-side, so we instead filter out
* columns on our side.
*/
queryDocument = mongo_query_document(node);
/*
* Decide input collection to the aggregation. In case of join, outer
* relation should be given as input collection to the aggregation.
*/
if (fmstate->relType == JOIN_REL ||
fmstate->relType == UPPER_JOIN_REL)
collectionName = fmstate->outerRelName;
else
collectionName = fmstate->options->collectionName;
/*
* Logs the query pipeline (in extended JSON format) when the
* log_remote_query GUC is enabled. This represents the remote query
* intended to run on the MongoDB server.
*/
if (log_remote_query)
{
char *ext_json = NULL;
size_t length = 0;
/* Get readable query pipeline in the extended json format. */
ext_json = bson_as_relaxed_extended_json(queryDocument, &length);
if (ext_json && length > 0)
{
/*
* Constructs a remote query compatible with MongoDB by
* transforming the relaxed extended JSON. This is done by
* prepending "db..aggregate(" and removing
* the '{ "pipeline":' wrapper from the JSON string.
*/
ext_json[length - 1] = '\0'; /* Remove last '}' */
ereport(LOG,
errmsg("remote query: db.%s.aggregate(%s)",
collectionName,
ext_json + 14)); /* 14 = length of '{ "pipeline" :' */
bson_free(ext_json);
}
}
mongoCursor = mongoCursorCreate(fmstate->mongoConnection,
fmstate->options->svr_database,
collectionName,
queryDocument);
/* Save mongoCursor */
fmstate->mongoCursor = mongoCursor;
}
/*
* We execute the protocol to load a virtual tuple into a slot. We first
* call ExecClearTuple, then fill in values / isnull arrays, and last call
* ExecStoreVirtualTuple. If we are done fetching documents from Mongo,
* we just return an empty slot as required.
*/
ExecClearTuple(tupleSlot);
/* Initialize all values for this row to null */
memset(columnValues, 0, columnCount * sizeof(Datum));
memset(columnNulls, true, columnCount * sizeof(bool));
if (mongoCursorNext(mongoCursor, NULL))
{
const BSON *bsonDocument = mongoCursorBson(mongoCursor);
const char *bsonDocumentKey = NULL; /* Top level document */
fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash,
columnValues, columnNulls, fmstate->relType);
ExecStoreVirtualTuple(tupleSlot);
}
return tupleSlot;
}
/*
* mongoEndForeignScan
* Finishes scanning the foreign table, closes the cursor and the
* connection to MongoDB, and reclaims scan related resources.
*/
static void
mongoEndForeignScan(ForeignScanState *node)
{
MongoFdwModifyState *fmstate;
fmstate = (MongoFdwModifyState *) node->fdw_state;
/* If we executed a query, reclaim mongo related resources */
if (fmstate != NULL)
{
if (fmstate->options)
{
mongo_free_options(fmstate->options);
fmstate->options = NULL;
}
mongo_free_scan_state(fmstate);
}
}
/*
* mongoReScanForeignScan
* Rescans the foreign table.
*
* Note that rescans in Mongo end up being notably more expensive than what the
* planner expects them to be, since MongoDB cursors don't provide reset/rewind
* functionality.
*/
static void
mongoReScanForeignScan(ForeignScanState *node)
{
MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state;
/* Close down the old cursor */
if (fmstate->mongoCursor)
{
mongoCursorDestroy(fmstate->mongoCursor);
fmstate->mongoCursor = NULL;
}
}
static List *
mongoPlanForeignModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index)
{
CmdType operation = plan->operation;
RangeTblEntry *rte = planner_rt_fetch(resultRelation, root);
Relation rel;
List *targetAttrs = NIL;
/*
* Core code already has some lock on each rel being planned, so we can
* use NoLock here.
*/
rel = table_open(rte->relid, NoLock);
if (operation == CMD_INSERT)
{
TupleDesc tupdesc = RelationGetDescr(rel);
int attnum;
for (attnum = 1; attnum <= tupdesc->natts; attnum++)
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1);
if (!attr->attisdropped)
targetAttrs = lappend_int(targetAttrs, attnum);
}
}
else if (operation == CMD_UPDATE)
{
Bitmapset *tmpset;
#if PG_VERSION_NUM >= 160000
RTEPermissionInfo *perminfo;
int attidx;
#endif
AttrNumber col;
#if PG_VERSION_NUM >= 160000
perminfo = getRTEPermissionInfo(root->parse->rteperminfos, rte);
tmpset = bms_copy(perminfo->updatedCols);
attidx = -1;
#else
tmpset = bms_copy(rte->updatedCols);
#endif
#if PG_VERSION_NUM >= 160000
while ((attidx = bms_next_member(tmpset, attidx)) >= 0)
#else
while ((col = bms_first_member(tmpset)) >= 0)
#endif
{
#if PG_VERSION_NUM >= 160000
col = attidx + FirstLowInvalidHeapAttributeNumber;
#else
col += FirstLowInvalidHeapAttributeNumber;
#endif
if (col <= InvalidAttrNumber) /* Shouldn't happen */
elog(ERROR, "system-column update is not supported");
/*
* We also disallow updates to the first column which happens to
* be the row identifier in MongoDb (_id)
*/
if (col == 1) /* Shouldn't happen */
elog(ERROR, "row identifier column update is not supported");
targetAttrs = lappend_int(targetAttrs, col);
}
/* We also want the rowid column to be available for the update */
targetAttrs = lcons_int(1, targetAttrs);
}
else
targetAttrs = lcons_int(1, targetAttrs);
/*
* RETURNING list not supported
*/
if (plan->returningLists)
elog(ERROR, "RETURNING is not supported by this FDW");
table_close(rel, NoLock);
return list_make1(targetAttrs);
}
/*
* mongoBeginForeignModify
* Begin an insert/update/delete operation on a foreign table.
*/
static void
mongoBeginForeignModify(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
List *fdw_private,
int subplan_index,
int eflags)
{
MongoFdwModifyState *fmstate;
Relation rel = resultRelInfo->ri_RelationDesc;
AttrNumber n_params;
Oid typefnoid = InvalidOid;
bool isvarlena = false;
ListCell *lc;
Oid foreignTableId;
Oid userid;
ForeignServer *server;
UserMapping *user;
ForeignTable *table;
#if PG_VERSION_NUM >= 160000
ForeignScan *fsplan = (ForeignScan *) mtstate->ps.plan;
#else
EState *estate = mtstate->ps.state;
RangeTblEntry *rte;
#endif
/*
* Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState
* stays NULL.
*/
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
return;
#if PG_VERSION_NUM >= 160000
userid = fsplan->checkAsUser ? fsplan->checkAsUser : GetUserId();
#else
rte = rt_fetch(resultRelInfo->ri_RangeTableIndex, estate->es_range_table);
userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
#endif
foreignTableId = RelationGetRelid(rel);
/* Get info about foreign table. */
table = GetForeignTable(foreignTableId);
server = GetForeignServer(table->serverid);
user = GetUserMapping(userid, server->serverid);
/* Begin constructing MongoFdwModifyState. */
fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState));
fmstate->rel = rel;
fmstate->options = mongo_get_options(foreignTableId);
/*
* Get connection to the foreign server. Connection manager will
* establish new connection if necessary.
*/
fmstate->mongoConnection = mongo_get_connection(server, user,
fmstate->options);
fmstate->target_attrs = (List *) list_nth(fdw_private, 0);
n_params = list_length(fmstate->target_attrs) + 1;
fmstate->p_flinfo = (FmgrInfo *) palloc(sizeof(FmgrInfo) * n_params);
fmstate->p_nums = 0;
if (mtstate->operation == CMD_UPDATE)
{
Form_pg_attribute attr;
#if PG_VERSION_NUM >= 140000
Plan *subplan = outerPlanState(mtstate)->plan;
#else
Plan *subplan = mtstate->mt_plans[subplan_index]->plan;
#endif
Assert(subplan != NULL);
attr = TupleDescAttr(RelationGetDescr(rel), 0);
/* Find the rowid resjunk column in the subplan's result */
fmstate->rowidAttno = ExecFindJunkAttributeInTlist(subplan->targetlist,
NameStr(attr->attname));
if (!AttributeNumberIsValid(fmstate->rowidAttno))
elog(ERROR, "could not find junk row identifier column");
}
/* Set up for remaining transmittable parameters */
foreach(lc, fmstate->target_attrs)
{
int attnum = lfirst_int(lc);
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(rel),
attnum - 1);
Assert(!attr->attisdropped);
getTypeOutputInfo(attr->atttypid, &typefnoid, &isvarlena);
fmgr_info(typefnoid, &fmstate->p_flinfo[fmstate->p_nums]);
fmstate->p_nums++;
}
Assert(fmstate->p_nums <= n_params);
resultRelInfo->ri_FdwState = fmstate;
}
/*
* mongoExecForeignInsert
* Insert one row into a foreign table.
*/
static TupleTableSlot *
mongoExecForeignInsert(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot)
{
BSON *bsonDoc;
Oid typoid;
Datum value;
bool isnull = false;
MongoFdwModifyState *fmstate;
fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState;
bsonDoc = bsonCreate();
typoid = get_atttype(RelationGetRelid(resultRelInfo->ri_RelationDesc), 1);
/* Get following parameters from slot */
if (slot != NULL && fmstate->target_attrs != NIL)
{
ListCell *lc;
foreach(lc, fmstate->target_attrs)
{
int attnum = lfirst_int(lc);
value = slot_getattr(slot, attnum, &isnull);
/* First column of MongoDB's foreign table must be _id */
if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "_id") != 0)
elog(ERROR, "first column of MongoDB's foreign table must be \"_id\"");
if (typoid != NAMEOID)
elog(ERROR, "type of first column of MongoDB's foreign table must be \"NAME\"");
if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "__doc") == 0)
continue;
/*
* Ignore the value of first column which is row identifier in
* MongoDb (_id) and let MongoDB to insert the unique value for
* that column.
*/
if (attnum == 1)
{
ereport(DEBUG1,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot insert given data for \"_id\" column, skipping"),
errhint("Let MongoDB insert the unique value for \"_id\" column.")));
continue;
}
append_mongo_value(bsonDoc,
TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data,
value,
isnull,
TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid);
}
}
/* Now we are ready to insert tuple/document into MongoDB */
mongoInsert(fmstate->mongoConnection, fmstate->options->svr_database,
fmstate->options->collectionName, bsonDoc);
bsonDestroy(bsonDoc);
return slot;
}
/*
* mongoAddForeignUpdateTargets
* Add column(s) needed for update/delete on a foreign table, we are using
* first column as row identification column, so we are adding that into
* target list.
*/
#if PG_VERSION_NUM >= 140000
static void
mongoAddForeignUpdateTargets(PlannerInfo *root,
Index rtindex,
RangeTblEntry *target_rte,
Relation target_relation)
#else
static void
mongoAddForeignUpdateTargets(Query *parsetree,
RangeTblEntry *target_rte,
Relation target_relation)
#endif
{
Var *var;
const char *attrname;
#if PG_VERSION_NUM < 140000
TargetEntry *tle;
#endif
/*
* What we need is the rowid which is the first column
*/
Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(target_relation),
0);
/* Make a Var representing the desired value */
#if PG_VERSION_NUM >= 140000
var = makeVar(rtindex,
#else
var = makeVar(parsetree->resultRelation,
#endif
1,
attr->atttypid,
attr->atttypmod,
InvalidOid,
0);
/* Get name of the row identifier column */
attrname = NameStr(attr->attname);
#if PG_VERSION_NUM >= 140000
/* Register it as a row-identity column needed by this target rel */
add_row_identity_var(root, var, rtindex, attrname);
#else
/* Wrap it in a TLE with the right name ... */
tle = makeTargetEntry((Expr *) var,
list_length(parsetree->targetList) + 1,
pstrdup(attrname),
true);
/* ... And add it to the query's targetlist */
parsetree->targetList = lappend(parsetree->targetList, tle);
#endif
}
static TupleTableSlot *
mongoExecForeignUpdate(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot)
{
Datum datum;
bool isNull = false;
Oid foreignTableId;
char *columnName;
Oid typoid;
BSON *document;
BSON *op = NULL;
BSON set;
MongoFdwModifyState *fmstate;
fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState;
foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc);
/* Get the id that was passed up as a resjunk column */
datum = ExecGetJunkAttribute(planSlot, fmstate->rowidAttno, &isNull);
columnName = get_attname(foreignTableId, 1, false);
/* First column of MongoDB's foreign table must be _id */
if (strcmp(columnName, "_id") != 0)
elog(ERROR, "first column of MongoDB's foreign table must be \"_id\"");
typoid = get_atttype(foreignTableId, 1);
/* The type of first column of MongoDB's foreign table must be NAME */
if (typoid != NAMEOID)
elog(ERROR, "type of first column of MongoDB's foreign table must be \"NAME\"");
document = bsonCreate();
bsonAppendStartObject(document, "$set", &set);
/* Get following parameters from slot */
if (slot != NULL && fmstate->target_attrs != NIL)
{
ListCell *lc;
foreach(lc, fmstate->target_attrs)
{
int attnum = lfirst_int(lc);
Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor,
attnum - 1);
Datum value;
bool isnull;
if (strcmp("_id", attr->attname.data) == 0)
continue;
if (strcmp("__doc", attr->attname.data) == 0)
elog(ERROR, "system column '__doc' update is not supported");
value = slot_getattr(slot, attnum, &isnull);
append_mongo_value(&set, attr->attname.data, value,
isnull ? true : false, attr->atttypid);
}
}
bsonAppendFinishObject(document, &set);
op = bsonCreate();
if (!append_mongo_value(op, columnName, datum, isNull, typoid))
{
bsonDestroy(document);
return NULL;
}
/* We are ready to update the row into MongoDB */
mongoUpdate(fmstate->mongoConnection, fmstate->options->svr_database,
fmstate->options->collectionName, op, document);
bsonDestroy(op);
bsonDestroy(document);
/* Return NULL if nothing was updated on the remote end */
return slot;
}
/*
* mongoExecForeignDelete
* Delete one row from a foreign table
*/
static TupleTableSlot *
mongoExecForeignDelete(EState *estate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot)
{
Datum datum;
bool isNull = false;
Oid foreignTableId;
char *columnName = NULL;
Oid typoid;
BSON *document;
MongoFdwModifyState *fmstate;
fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState;
foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc);
/* Get the id that was passed up as a resjunk column */
datum = ExecGetJunkAttribute(planSlot, 1, &isNull);
columnName = get_attname(foreignTableId, 1, false);
/* First column of MongoDB's foreign table must be _id */
if (strcmp(columnName, "_id") != 0)
elog(ERROR, "first column of MongoDB's foreign table must be \"_id\"");
typoid = get_atttype(foreignTableId, 1);
/* The type of first column of MongoDB's foreign table must be NAME */
if (typoid != NAMEOID)
elog(ERROR, "type of first column of MongoDB's foreign table must be \"NAME\"");
document = bsonCreate();
if (!append_mongo_value(document, columnName, datum, isNull, typoid))
{
bsonDestroy(document);
return NULL;
}
/* Now we are ready to delete a single document from MongoDB */
mongoDelete(fmstate->mongoConnection, fmstate->options->svr_database,
fmstate->options->collectionName, document);
bsonDestroy(document);
/* Return NULL if nothing was updated on the remote end */
return slot;
}
/*
* mongoEndForeignModify
* Finish an insert/update/delete operation on a foreign table
*/
static void
mongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo)
{
MongoFdwModifyState *fmstate;
fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState;
if (fmstate)
{
if (fmstate->options)
{
mongo_free_options(fmstate->options);
fmstate->options = NULL;
}
mongo_free_scan_state(fmstate);
pfree(fmstate);
}
}
/*
* foreign_table_document_count
* Connects to the MongoDB server, and queries it for the number of
* documents in the foreign collection. On success, the function returns
* the document count. On failure, the function returns -1.0.
*/
static double
foreign_table_document_count(Oid foreignTableId)
{
MongoFdwOptions *options;
MONGO_CONN *mongoConnection;
const BSON *emptyQuery = NULL;
double documentCount;
Oid userid = GetUserId();
ForeignServer *server;
UserMapping *user;
ForeignTable *table;
/* Get info about foreign table. */
table = GetForeignTable(foreignTableId);
server = GetForeignServer(table->serverid);
user = GetUserMapping(userid, server->serverid);
/* Resolve foreign table options; and connect to mongo server */
options = mongo_get_options(foreignTableId);
/*
* Get connection to the foreign server. Connection manager will
* establish new connection if necessary.
*/
mongoConnection = mongo_get_connection(server, user, options);
documentCount = mongoAggregateCount(mongoConnection, options->svr_database,
options->collectionName, emptyQuery);
mongo_free_options(options);
return documentCount;
}
/*
* column_mapping_hash
* Creates a hash table that maps column names to column index and types.
*
* This table helps us quickly translate BSON document key/values to the
* corresponding PostgreSQL columns.
*/
static HTAB *
column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList,
List *colIsInnerList, uint32 relType)
{
ListCell *columnCell;
HTAB *columnMappingHash;
HASHCTL hashInfo;
uint32 attnum = 0;
Index listIndex = 0;
Index aggIndex = 0;
memset(&hashInfo, 0, sizeof(hashInfo));
hashInfo.keysize = NAMEDATALEN;
hashInfo.entrysize = sizeof(ColumnMapping);
hashInfo.hash = string_hash;
hashInfo.hcxt = CurrentMemoryContext;
columnMappingHash = hash_create("Column Mapping Hash", MaxHashTableSize,
&hashInfo,
(HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT));
Assert(columnMappingHash != NULL);
foreach(columnCell, columnList)
{
Var *column = (Var *) lfirst(columnCell);
ColumnMapping *columnMapping;
char *columnName;
bool handleFound = false;
void *hashKey;
if (relType == JOIN_REL)
{
int is_innerrel = list_nth_int(colIsInnerList, listIndex);
columnName = strVal(list_nth(colNameList, listIndex++));
/*
* In MongoDB, columns involved in join result-set from inner
* table prefixed with Join result name. Uses hard-coded string
* "Join Result" to be prefixed to form the hash key to read the
* joined result set. This same prefix needs to be given as
* joined result set name in the $lookup stage when building the
* remote query.
*
* For a simple relation scan, the column name would be the hash
* key.
*/
if (is_innerrel)
{
const char *resultName = "Join_Result";
StringInfo KeyString = makeStringInfo();
appendStringInfo(KeyString, "%s.%s", resultName, columnName);
hashKey = (void *) KeyString->data;
}
else
hashKey = (void *) columnName;
}
/*
* In MongoDB, columns involved in upper result-set named as
* "_id.column_name_variable" not the actual column names. Use this
* as hashKey to match the bson key we get at the time of fetching the
* column values.
*
* Use the hard-coded string v_agg* to get the aggregation result.
* This same name needs to be given as an aggregation result name
* while building the remote query.
*/
else if (relType == UPPER_REL || relType == UPPER_JOIN_REL)
{
if (IsA(column, Var))
{
if (relType == UPPER_REL)
columnName = get_attname(foreignTableId, column->varattno,
false);
else
columnName = strVal(list_nth(colNameList, listIndex++));
/*
* Keep variable name same as a column name. Use the same
* name while building the MongoDB query in the
* mongo_query_document function.
*/
hashKey = psprintf("_id.%s", columnName);
}
else
hashKey = psprintf("AGG_RESULT_KEY%d", aggIndex++);
}
else
{
columnName = get_attname(foreignTableId, column->varattno, false);
hashKey = (void *) columnName;
}
columnMapping = (ColumnMapping *) hash_search(columnMappingHash,
hashKey,
HASH_ENTER,
&handleFound);
Assert(columnMapping != NULL);
/*
* Save attribute number of the current column in the resulting tuple.
* For join/upper relation, it is continuously increasing integers
* starting from 0, and for simple relation, it's varattno.
*/
if (relType != BASE_REL)
{
columnMapping->columnIndex = attnum;
attnum++;
}
else
columnMapping->columnIndex = column->varattno - 1;
/* Save other information */
if ((relType == UPPER_REL || relType == UPPER_JOIN_REL) &&
!strncmp(hashKey, "AGG_RESULT_KEY", 5))
{
Aggref *agg = (Aggref *) lfirst(columnCell);
columnMapping->columnTypeId = agg->aggtype;
columnMapping->columnTypeMod = agg->aggcollid;
columnMapping->columnArrayTypeId = InvalidOid;
}
else
{
columnMapping->columnTypeId = column->vartype;
columnMapping->columnTypeMod = column->vartypmod;
columnMapping->columnArrayTypeId = get_element_type(column->vartype);
}
}
return columnMappingHash;
}
/*
* fill_tuple_slot
* Walks over all key/value pairs in the given document.
*
* For each pair, the function checks if the key appears in the column mapping
* hash, and if the value type is compatible with the one specified for the
* column. If so, the function converts the value and fills the corresponding
* tuple position. The bsonDocumentKey parameter is used for recursion, and
* should always be passed as NULL.
*/
static void
fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey,
HTAB *columnMappingHash, Datum *columnValues,
bool *columnNulls, uint32 relType)
{
ColumnMapping *columnMapping;
bool handleFound = false;
void *hashKey;
BSON_ITERATOR bsonIterator = {NULL, 0};
if (bsonIterInit(&bsonIterator, (BSON *) bsonDocument) == false)
elog(ERROR, "failed to initialize BSON iterator");
hashKey = "__doc";
columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey,
HASH_FIND, &handleFound);
if (columnMapping != NULL && handleFound == true &&
columnValues[columnMapping->columnIndex] == 0)
{
JsonLexContext *lex;
text *result;
Datum columnValue;
char *str;
str = bsonAsJson(bsonDocument);
result = cstring_to_text_with_len(str, strlen(str));
#if PG_VERSION_NUM >= 170000
lex = makeJsonLexContext(NULL, result, false);
#else
lex = makeJsonLexContext(result, false);
#endif
pg_parse_json(lex, &nullSemAction);
columnValue = PointerGetDatum(result);
switch (columnMapping->columnTypeId)
{
case BOOLOID:
case INT2OID:
case INT4OID:
case INT8OID:
case BOXOID:
case BYTEAOID:
case CHAROID:
case VARCHAROID:
case NAMEOID:
case JSONOID:
case XMLOID:
case POINTOID:
case LSEGOID:
case LINEOID:
case UUIDOID:
case LSNOID:
case TEXTOID:
case CASHOID:
case DATEOID:
case MACADDROID:
case TIMESTAMPOID:
case TIMESTAMPTZOID:
case BPCHAROID:
columnValue = PointerGetDatum(result);
break;
case JSONBOID:
columnValue = DirectFunctionCall1(jsonb_in,
PointerGetDatum(str));
break;
default:
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("unsupported type for column __doc"),
errhint("Column type: %u",
(uint32) columnMapping->columnTypeId)));
break;
}
columnValues[columnMapping->columnIndex] = columnValue;
columnNulls[columnMapping->columnIndex] = false;
return;
}
while (bsonIterNext(&bsonIterator))
{
const char *bsonKey = bsonIterKey(&bsonIterator);
BSON_TYPE bsonType = bsonIterType(&bsonIterator);
Oid columnTypeId = InvalidOid;
Oid columnArrayTypeId = InvalidOid;
bool compatibleTypes = false;
const char *bsonFullKey;
int32 attnum = 0;
bool is_agg = false;
if (!strncmp(bsonKey, "AGG_RESULT_KEY", 5) && bsonType == BSON_TYPE_INT32)
is_agg = true;
columnMapping = NULL;
if (bsonDocumentKey != NULL)
{
/*
* For fields in nested BSON objects, we use fully qualified field
* name to check the column mapping.
*/
StringInfo bsonFullKeyString = makeStringInfo();
appendStringInfo(bsonFullKeyString, "%s.%s", bsonDocumentKey,
bsonKey);
bsonFullKey = bsonFullKeyString->data;
}
else
bsonFullKey = bsonKey;
/* Look up the corresponding column for this bson key */
hashKey = (void *) bsonFullKey;
columnMapping = (ColumnMapping *) hash_search(columnMappingHash,
hashKey,
HASH_FIND,
&handleFound);
if (columnMapping != NULL)
{
columnTypeId = columnMapping->columnTypeId;
columnArrayTypeId = columnMapping->columnArrayTypeId;
}
/* Recurse into nested objects */
if (bsonType == BSON_TYPE_DOCUMENT)
{
if (columnTypeId != JSONOID)
{
BSON subObject;
bsonIterSubObject(&bsonIterator, &subObject);
fill_tuple_slot(&subObject, bsonFullKey, columnMappingHash,
columnValues, columnNulls, relType);
continue;
}
}
/* If no corresponding column or null BSON value, continue */
if (!is_agg && (columnMapping == NULL || bsonType == BSON_TYPE_NULL))
continue;
/* Check if columns have compatible types */
if ((OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY))
compatibleTypes = true;
else
compatibleTypes = column_types_compatible(bsonType, columnTypeId);
/* If types are incompatible, leave this column null */
if (!compatibleTypes)
continue;
if (columnMapping != NULL)
attnum = columnMapping->columnIndex;
/* Fill in corresponding column value and null flag */
if (OidIsValid(columnArrayTypeId))
columnValues[attnum] = column_value_array(&bsonIterator,
columnArrayTypeId);
else
columnValues[attnum] = column_value(&bsonIterator, columnTypeId,
columnMapping->columnTypeMod);
columnNulls[attnum] = false;
}
}
/*
* column_types_compatible
* Checks if the given BSON type can be converted to the given PostgreSQL
* type.
*
* In this check, the function also uses its knowledge of internal conversions
* applied by BSON APIs.
*/
static bool
column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId)
{
bool compatibleTypes = false;
/* We consider the PostgreSQL column type as authoritative */
switch (columnTypeId)
{
case INT2OID:
case INT4OID:
case INT8OID:
case FLOAT4OID:
case FLOAT8OID:
case NUMERICOID:
if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 ||
bsonType == BSON_TYPE_DOUBLE)
compatibleTypes = true;
if (bsonType == BSON_TYPE_BOOL)
compatibleTypes = true;
break;
case BOOLOID:
if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 ||
bsonType == BSON_TYPE_DOUBLE || bsonType == BSON_TYPE_BOOL)
compatibleTypes = true;
break;
case BPCHAROID:
case VARCHAROID:
case TEXTOID:
if (bsonType == BSON_TYPE_UTF8)
compatibleTypes = true;
break;
case BYTEAOID:
if (bsonType == BSON_TYPE_BINDATA)
compatibleTypes = true;
if (bsonType == BSON_TYPE_OID)
compatibleTypes = true;
break;
case NAMEOID:
/*
* We currently error out on data types other than object
* identifier. MongoDB supports more data types for the _id field
* but those are not yet handled in mongo_fdw.
*/
if (bsonType != BSON_TYPE_OID)
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("cannot convert BSON type to column type"),
errhint("Column type \"NAME\" is compatible only with BSON type \"ObjectId\".")));
/*
* We currently overload the NAMEOID type to represent the BSON
* object identifier. We can safely overload this 64-byte data
* type since it's reserved for internal use in PostgreSQL.
*/
compatibleTypes = true;
break;
case DATEOID:
case TIMESTAMPOID:
case TIMESTAMPTZOID:
if (bsonType == BSON_TYPE_DATE_TIME)
compatibleTypes = true;
break;
case NUMERICARRAY_OID:
if (bsonType == BSON_TYPE_ARRAY)
compatibleTypes = true;
break;
case JSONOID:
if (bsonType == BSON_TYPE_DOCUMENT ||
bsonType == BSON_TYPE_ARRAY)
compatibleTypes = true;
break;
default:
/*
* We currently error out on other data types. Some types such as
* byte arrays are easy to add, but they need testing.
*
* Other types such as money or inet, do not have equivalents in
* MongoDB.
*/
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("cannot convert BSON type to column type"),
errhint("Column type: %u", (uint32) columnTypeId)));
break;
}
return compatibleTypes;
}
/*
* column_value_array
* Uses array element type id to read the current array pointed to by the
* BSON iterator, and converts each array element (with matching type) to
* the corresponding PostgreSQL datum.
*
* Then, the function constructs an array datum from element datums, and
* returns the array datum.
*/
static Datum
column_value_array(BSON_ITERATOR *bsonIterator, Oid valueTypeId)
{
Datum *columnValueArray = palloc(INITIAL_ARRAY_CAPACITY * sizeof(Datum));
uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY;
uint32 arrayIndex = 0;
ArrayType *columnValueObject;
Datum columnValueDatum;
bool typeByValue;
char typeAlignment;
int16 typeLength;
BSON_ITERATOR bsonSubIterator = {NULL, 0};
bsonIterSubIter(bsonIterator, &bsonSubIterator);
while (bsonIterNext(&bsonSubIterator))
{
BSON_TYPE bsonType = bsonIterType(&bsonSubIterator);
bool compatibleTypes = false;
compatibleTypes = column_types_compatible(bsonType, valueTypeId);
if (bsonType == BSON_TYPE_NULL || !compatibleTypes)
continue;
if (arrayIndex >= arrayCapacity)
{
/* Double the array capacity. */
arrayCapacity *= 2;
columnValueArray = repalloc(columnValueArray,
arrayCapacity * sizeof(Datum));
}
/* Use default type modifier (0) to convert column value */
columnValueArray[arrayIndex] = column_value(&bsonSubIterator,
valueTypeId, 0);
arrayIndex++;
}
get_typlenbyvalalign(valueTypeId, &typeLength, &typeByValue,
&typeAlignment);
columnValueObject = construct_array(columnValueArray,
arrayIndex,
valueTypeId,
typeLength,
typeByValue,
typeAlignment);
columnValueDatum = PointerGetDatum(columnValueObject);
pfree(columnValueArray);
return columnValueDatum;
}
/*
* column_value
* Uses column type information to read the current value pointed to by
* the BSON iterator, and converts this value to the corresponding
* PostgreSQL datum. The function then returns this datum.
*/
static Datum
column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId,
int32 columnTypeMod)
{
Datum columnValue;
switch (columnTypeId)
{
case INT2OID:
{
int16 value = (int16) bsonIterInt32(bsonIterator);
columnValue = Int16GetDatum(value);
}
break;
case INT4OID:
{
int32 value = bsonIterInt32(bsonIterator);
columnValue = Int32GetDatum(value);
}
break;
case INT8OID:
{
int64 value = bsonIterInt64(bsonIterator);
columnValue = Int64GetDatum(value);
}
break;
case FLOAT4OID:
{
float4 value = (float4) bsonIterDouble(bsonIterator);
columnValue = Float4GetDatum(value);
}
break;
case FLOAT8OID:
{
float8 value = bsonIterDouble(bsonIterator);
columnValue = Float8GetDatum(value);
}
break;
case NUMERICOID:
{
float8 value = bsonIterDouble(bsonIterator);
Datum valueDatum = DirectFunctionCall1(float8_numeric,
Float8GetDatum(value));
/*
* Since we have a Numeric value, using numeric() here instead
* of numeric_in() input function for typmod conversion.
*/
columnValue = DirectFunctionCall2(numeric, valueDatum,
Int32GetDatum(columnTypeMod));
}
break;
case BOOLOID:
{
bool value = bsonIterBool(bsonIterator);
columnValue = BoolGetDatum(value);
}
break;
case BPCHAROID:
{
const char *value = bsonIterString(bsonIterator);
Datum valueDatum = CStringGetDatum(value);
columnValue = DirectFunctionCall3(bpcharin, valueDatum,
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(columnTypeMod));
}
break;
case VARCHAROID:
{
const char *value = bsonIterString(bsonIterator);
Datum valueDatum = CStringGetDatum(value);
columnValue = DirectFunctionCall3(varcharin, valueDatum,
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(columnTypeMod));
}
break;
case TEXTOID:
{
const char *value = bsonIterString(bsonIterator);
columnValue = CStringGetTextDatum(value);
}
break;
case NAMEOID:
{
char value[NAMEDATALEN];
Datum valueDatum = 0;
bson_oid_t *bsonObjectId = (bson_oid_t *) bsonIterOid(bsonIterator);
bson_oid_to_string(bsonObjectId, value);
valueDatum = CStringGetDatum(value);
columnValue = DirectFunctionCall3(namein, valueDatum,
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(columnTypeMod));
}
break;
case BYTEAOID:
{
int value_len;
char *value;
bytea *result;
switch (bsonIterType(bsonIterator))
{
case BSON_TYPE_OID:
value = (char *) bsonIterOid(bsonIterator);
value_len = 12;
break;
default:
value = (char *) bsonIterBinData(bsonIterator,
(uint32_t *) &value_len);
break;
}
result = (bytea *) palloc(value_len + VARHDRSZ);
memcpy(VARDATA(result), value, value_len);
SET_VARSIZE(result, value_len + VARHDRSZ);
columnValue = PointerGetDatum(result);
}
break;
case DATEOID:
{
int64 valueMillis = bsonIterDate(bsonIterator);
int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS;
Datum timestampDatum = TimestampGetDatum(timestamp);
columnValue = DirectFunctionCall1(timestamp_date,
timestampDatum);
}
break;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
{
int64 valueMillis = bsonIterDate(bsonIterator);
int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS;
/* Overlook type modifiers for timestamp */
columnValue = TimestampGetDatum(timestamp);
}
break;
case JSONOID:
{
JsonLexContext *lex;
text *result;
StringInfo buffer = makeStringInfo();
BSON_TYPE type = BSON_ITER_TYPE(bsonIterator);
if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT)
ereport(ERROR,
(errmsg("cannot convert to json")));
/* Convert BSON to JSON value */
bsonToJsonStringValue(buffer, bsonIterator,
BSON_TYPE_ARRAY == type);
result = cstring_to_text_with_len(buffer->data, buffer->len);
#if PG_VERSION_NUM >= 170000
lex = makeJsonLexContext(NULL, result, false);
#else
lex = makeJsonLexContext(result, false);
#endif
pg_parse_json(lex, &nullSemAction);
columnValue = PointerGetDatum(result);
}
break;
default:
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("cannot convert BSON type to column type"),
errhint("Column type: %u", (uint32) columnTypeId)));
break;
}
return columnValue;
}
/*
* mongo_free_scan_state
* Closes the cursor and connection to MongoDB, and reclaims all Mongo
* related resources allocated for the foreign scan.
*/
static void
mongo_free_scan_state(MongoFdwModifyState *fmstate)
{
if (fmstate == NULL)
return;
if (fmstate->queryDocument)
{
bsonDestroy(fmstate->queryDocument);
fmstate->queryDocument = NULL;
}
if (fmstate->mongoCursor)
{
mongoCursorDestroy(fmstate->mongoCursor);
fmstate->mongoCursor = NULL;
}
/* Release remote connection */
mongo_release_connection(fmstate->mongoConnection);
}
/*
* mongoAnalyzeForeignTable
* Collects statistics for the given foreign table.
*/
static bool
mongoAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages)
{
BlockNumber pageCount = 0;
int attributeCount;
int32 *attributeWidths;
Oid foreignTableId;
int32 documentWidth;
double documentCount;
double foreignTableSize;
foreignTableId = RelationGetRelid(relation);
documentCount = foreign_table_document_count(foreignTableId);
if (documentCount > 0.0)
{
attributeCount = RelationGetNumberOfAttributes(relation);
attributeWidths = (int32 *) palloc0((attributeCount + 1) * sizeof(int32));
/*
* We estimate disk costs assuming a sequential scan over the data.
* This is an inaccurate assumption as Mongo scatters the data over
* disk pages, and may rely on an index to retrieve the data. Still,
* this should at least give us a relative cost.
*/
documentWidth = get_relation_data_width(foreignTableId,
attributeWidths);
foreignTableSize = documentCount * documentWidth;
pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ);
}
else
ereport(DEBUG1,
(errmsg("could not retrieve document count for collection"),
errhint("Could not collect statistics about foreign table.")));
(*totalpages) = pageCount;
(*func) = mongo_acquire_sample_rows;
return true;
}
/*
* mongo_acquire_sample_rows
* Acquires a random sample of rows from the foreign table.
*
* Selected rows are returned in the caller allocated sampleRows array,
* which must have at least target row count entries. The actual number of
* rows selected is returned as the function result. We also count the number
* of rows in the collection and return it in total row count. We also always
* set dead row count to zero.
*
* Note that the returned list of rows is not always in order by physical
* position in the MongoDB collection. Therefore, correlation estimates
* derived later may be meaningless, but it's OK because we don't use the
* estimates currently (the planner only pays attention to correlation for
* index scans).
*/
static int
mongo_acquire_sample_rows(Relation relation,
int errorLevel,
HeapTuple *sampleRows,
int targetRowCount,
double *totalRowCount,
double *totalDeadRowCount)
{
MONGO_CONN *mongoConnection;
int sampleRowCount = 0;
double rowCount = 0;
double rowCountToSkip = -1; /* -1 means not set yet */
double randomState;
Datum *columnValues;
bool *columnNulls;
Oid foreignTableId;
TupleDesc tupleDescriptor;
AttrNumber columnCount;
AttrNumber columnId;
HTAB *columnMappingHash;
MONGO_CURSOR *mongoCursor;
BSON *queryDocument = bsonCreate();
List *columnList = NIL;
char *relationName;
MemoryContext oldContext = CurrentMemoryContext;
MemoryContext tupleContext;
MongoFdwOptions *options;
ForeignServer *server;
UserMapping *user;
ForeignTable *table;
/* Create list of columns in the relation */
tupleDescriptor = RelationGetDescr(relation);
columnCount = tupleDescriptor->natts;
for (columnId = 1; columnId <= columnCount; columnId++)
{
Var *column = (Var *) palloc0(sizeof(Var));
Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId - 1);
column->varattno = columnId;
column->vartype = attr->atttypid;
column->vartypmod = attr->atttypmod;
columnList = lappend(columnList, column);
}
foreignTableId = RelationGetRelid(relation);
table = GetForeignTable(foreignTableId);
server = GetForeignServer(table->serverid);
user = GetUserMapping(GetUserId(), server->serverid);
options = mongo_get_options(foreignTableId);
/*
* Get connection to the foreign server. Connection manager will
* establish new connection if necessary.
*/
mongoConnection = mongo_get_connection(server, user, options);
/* Create cursor for collection name and set query */
mongoCursor = mongoCursorCreate(mongoConnection, options->svr_database,
options->collectionName, queryDocument);
columnMappingHash = column_mapping_hash(foreignTableId, columnList, NIL,
NIL, BASE_REL);
/*
* Use per-tuple memory context to prevent leak of memory used to read
* rows from the file with copy routines.
*/
tupleContext = AllocSetContextCreate(CurrentMemoryContext,
"mongo_fdw temporary context",
ALLOCSET_DEFAULT_SIZES);
/* Prepare for sampling rows */
randomState = anl_init_selection_state(targetRowCount);
columnValues = (Datum *) palloc(columnCount * sizeof(Datum));
columnNulls = (bool *) palloc(columnCount * sizeof(bool));
for (;;)
{
/* Check for user-requested abort or sleep */
#if PG_VERSION_NUM >= 180000
vacuum_delay_point(true);
#else
vacuum_delay_point();
#endif
/* Initialize all values for this row to null */
memset(columnValues, 0, columnCount * sizeof(Datum));
memset(columnNulls, true, columnCount * sizeof(bool));
if (mongoCursorNext(mongoCursor, NULL))
{
const BSON *bsonDocument = mongoCursorBson(mongoCursor);
const char *bsonDocumentKey = NULL; /* Top level document */
/* Fetch next tuple */
MemoryContextReset(tupleContext);
MemoryContextSwitchTo(tupleContext);
fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash,
columnValues, columnNulls, BASE_REL);
MemoryContextSwitchTo(oldContext);
}
else
{
bson_error_t error;
if (mongoc_cursor_error(mongoCursor, &error))
ereport(ERROR,
(errmsg("could not iterate over mongo collection"),
errhint("Mongo driver error: %s", error.message)));
break;
}
/*
* The first targetRowCount sample rows are simply copied into the
* reservoir. Then we start replacing tuples in the sample until we
* reach the end of the relation. This algorithm is from Jeff
* Vitter's paper (see more info in commands/analyze.c).
*/
if (sampleRowCount < targetRowCount)
sampleRows[sampleRowCount++] = heap_form_tuple(tupleDescriptor,
columnValues,
columnNulls);
else
{
/*
* t in Vitter's paper is the number of records already processed.
* If we need to compute a new S value, we must use the "not yet
* incremented" value of rowCount as t.
*/
if (rowCountToSkip < 0)
rowCountToSkip = anl_get_next_S(rowCount, targetRowCount,
&randomState);
if (rowCountToSkip <= 0)
{
/*
* Found a suitable tuple, so save it, replacing one old tuple
* at random.
*/
int rowIndex = (int) (targetRowCount * anl_random_fract());
Assert(rowIndex >= 0);
Assert(rowIndex < targetRowCount);
heap_freetuple(sampleRows[rowIndex]);
sampleRows[rowIndex] = heap_form_tuple(tupleDescriptor,
columnValues,
columnNulls);
}
rowCountToSkip -= 1;
}
rowCount += 1;
}
/* Only clean up the query struct, but not its data */
bsonDestroy(queryDocument);
/* Clean up */
MemoryContextDelete(tupleContext);
pfree(columnValues);
pfree(columnNulls);
/* Emit some interesting relation info */
relationName = RelationGetRelationName(relation);
ereport(errorLevel,
(errmsg("\"%s\": collection contains %.0f rows; %d rows in sample",
relationName, rowCount, sampleRowCount)));
(*totalRowCount) = rowCount;
(*totalDeadRowCount) = 0;
return sampleRowCount;
}
Datum
mongo_fdw_version(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(CODE_VERSION);
}
/*
* mongoBeginForeignInsert
* Prepare for an insert operation triggered by partition routing
* or COPY FROM.
*
* This is not yet supported, so raise an error.
*/
static void
mongoBeginForeignInsert(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo)
{
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION),
errmsg("COPY and foreign partition routing not supported in mongo_fdw")));
}
/*
* mongoEndForeignInsert
* BeginForeignInsert() is not yet implemented, hence we do not
* have anything to cleanup as of now. We throw an error here just
* to make sure when we do that we do not forget to cleanup
* resources.
*/
static void
mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo)
{
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION),
errmsg("COPY and foreign partition routing not supported in mongo_fdw")));
}
/*
* mongoGetForeignJoinPaths
* Add possible ForeignPath to joinrel, if the join is safe to push down.
*/
static void
mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
JoinType jointype, JoinPathExtraData *extra)
{
MongoFdwRelationInfo *fpinfo;
ForeignPath *joinpath;
Cost startup_cost;
Cost total_cost;
Path *epq_path = NULL; /* Path to create plan to be executed when
* EvalPlanQual gets triggered. */
MongoFdwRelationInfo *fpinfo_o;
MongoFdwRelationInfo *fpinfo_i;
/*
* Skip if this join combination has been considered already.
*/
if (joinrel->fdw_private)
return;
fpinfo_o = (MongoFdwRelationInfo *) outerrel->fdw_private;
fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private;
/* If join pushdown is not enabled, honor it. */
if ((!IS_JOIN_REL(outerrel) && !fpinfo_o->options->enable_join_pushdown) ||
(!IS_JOIN_REL(innerrel) && !fpinfo_i->options->enable_join_pushdown) ||
!enable_join_pushdown)
return;
/*
* Create unfinished MongoFdwRelationInfo entry which is used to indicate
* that the join relation is already considered, so that we won't waste
* time in judging the safety of join pushdown and adding the same paths
* again if found safe. Once we know that this join can be pushed down,
* we fill the entry.
*/
fpinfo = (MongoFdwRelationInfo *) palloc0(sizeof(MongoFdwRelationInfo));
fpinfo->pushdown_safe = false;
joinrel->fdw_private = fpinfo;
/*
* In case there is a possibility that EvalPlanQual will be executed, we
* should be able to reconstruct the row, from base relations applying all
* the conditions. We create a local plan from a suitable local path
* available in the path list. In case such a path doesn't exist, we can
* not push the join to the foreign server since we won't be able to
* reconstruct the row for EvalPlanQual(). Find an alternative local path
* before we add ForeignPath, lest the new path would kick possibly the
* only local path. Do this before calling mongo_foreign_join_ok(), since
* that function updates fpinfo and marks it as pushable if the join is
* found to be pushable.
*/
if (root->parse->commandType == CMD_DELETE ||
root->parse->commandType == CMD_UPDATE ||
root->rowMarks)
{
epq_path = GetExistingLocalJoinPath(joinrel);
if (!epq_path)
{
elog(DEBUG3, "could not push down foreign join because a local path suitable for EPQ checks was not found");
return;
}
}
else
epq_path = NULL;
if (!mongo_foreign_join_ok(root, joinrel, jointype, outerrel, innerrel,
extra))
{
/* Free path required for EPQ if we copied one; we don't need it now */
if (epq_path)
pfree(epq_path);
return;
}
/* TODO: Put accurate estimates here */
startup_cost = 15.0;
total_cost = 20 + startup_cost;
/*
* Create a new join path and add it to the joinrel which represents a
* join between foreign tables.
*/
#if PG_VERSION_NUM >= 180000
joinpath = create_foreign_join_path(root,
joinrel,
NULL,
joinrel->rows,
0,
startup_cost,
total_cost,
NIL, /* no pathkeys */
joinrel->lateral_relids,
epq_path,
extra->restrictlist,
NIL); /* no fdw_private */
#elif PG_VERSION_NUM >= 170000
joinpath = create_foreign_join_path(root,
joinrel,
NULL,
joinrel->rows,
startup_cost,
total_cost,
NIL, /* no pathkeys */
joinrel->lateral_relids,
epq_path,
extra->restrictlist,
NIL); /* no fdw_private */
#else
joinpath = create_foreign_join_path(root,
joinrel,
NULL,
joinrel->rows,
startup_cost,
total_cost,
NIL, /* no pathkeys */
joinrel->lateral_relids,
epq_path,
NIL); /* no fdw_private */
#endif
/* Add generated path into joinrel by add_path(). */
add_path(joinrel, (Path *) joinpath);
/* Add paths with pathkeys */
#if PG_VERSION_NUM >= 170000
mongo_add_paths_with_pathkeys(root, joinrel, epq_path, startup_cost,
total_cost, extra->restrictlist);
#else
mongo_add_paths_with_pathkeys(root, joinrel, epq_path, startup_cost,
total_cost);
#endif
/* XXX Consider parameterized paths for the join relation */
}
/*
* mongo_foreign_join_ok
* Assess whether the join between inner and outer relations can be
* pushed down to the foreign server.
*/
static bool
mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel,
JoinType jointype, RelOptInfo *outerrel,
RelOptInfo *innerrel, JoinPathExtraData *extra)
{
MongoFdwRelationInfo *fpinfo;
MongoFdwRelationInfo *fpinfo_o;
MongoFdwRelationInfo *fpinfo_i;
ListCell *lc;
List *joinclauses = NIL;
List *scan_var_list;
RangeTblEntry *rte;
char *colname;
/* We support pushing down only INNER, LEFT, RIGHT OUTER join */
if (jointype != JOIN_INNER && jointype != JOIN_LEFT &&
jointype != JOIN_RIGHT)
return false;
fpinfo = (MongoFdwRelationInfo *) joinrel->fdw_private;
fpinfo_o = (MongoFdwRelationInfo *) outerrel->fdw_private;
fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private;
/* Recursive joins can't be pushed down */
if (IS_JOIN_REL(outerrel) || IS_JOIN_REL(innerrel))
return false;
/*
* If either of the joining relations is marked as unsafe to pushdown, the
* join can not be pushed down.
*/
if (!fpinfo_o || !fpinfo_o->pushdown_safe ||
!fpinfo_i || !fpinfo_i->pushdown_safe)
return false;
/*
* If joining relations have local conditions, those conditions are
* required to be applied before joining the relations. Hence the join
* can not be pushed down.
*/
if (fpinfo_o->local_conds || fpinfo_i->local_conds)
return false;
scan_var_list = pull_var_clause((Node *) joinrel->reltarget->exprs,
PVC_RECURSE_PLACEHOLDERS);
/*
* Don't push-down join when whole row reference and/or full document
* retrieval is involved in the target list.
*/
foreach(lc, scan_var_list)
{
Var *var = lfirst(lc);
Assert(IsA(var, Var));
/* Don't support whole row reference. */
if (var->varattno == 0)
return false;
rte = planner_rt_fetch(var->varno, root);
colname = get_attname(rte->relid, var->varattno, false);
/* Don't support full document retrieval */
if (strcmp("__doc", colname) == 0)
return false;
}
/*
* Separate restrict list into join quals and pushed-down (other) quals.
*
* Join quals belonging to an outer join must all be shippable, else we
* cannot execute the join remotely. Add such quals to 'joinclauses'.
*
* Add other quals to fpinfo->remote_conds if they are shippable, else to
* fpinfo->local_conds. In an inner join it's okay to execute conditions
* either locally or remotely; the same is true for pushed-down conditions
* at an outer join.
*
* Note we might return failure after having already scribbled on
* fpinfo->remote_conds and fpinfo->local_conds. That's okay because we
* won't consult those lists again if we deem the join unshippable.
*/
joinclauses = NIL;
foreach(lc, extra->restrictlist)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
bool is_remote_clause = mongo_is_foreign_expr(root,
joinrel,
rinfo->clause,
false);
if (IS_OUTER_JOIN(jointype) &&
!RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids))
{
if (!is_remote_clause)
return false;
joinclauses = lappend(joinclauses, rinfo);
}
else
{
if (is_remote_clause && jointype == JOIN_INNER)
{
/*
* Unlike postgres_fdw, for inner join, don't append the join
* clauses to remote_conds, instead keep the join clauses
* separate. Currently, we are providing limited operator
* push-ability support for join pushdown, hence we keep those
* clauses separate to avoid INNER JOIN not getting pushdown
* if any of the WHERE clauses are not shippable as per join
* pushdown shippability.
*/
joinclauses = lappend(joinclauses, rinfo);
}
else
{
ListCell *cell;
List *local_var_list = pull_var_clause((Node *) rinfo->clause,
PVC_RECURSE_PLACEHOLDERS);
/*
* Don't push-down join when whole row reference and/or full
* document retrieval is involved in the join clause.
*/
foreach(cell, local_var_list)
{
Var *var = lfirst(cell);
Assert(IsA(var, Var));
/* Don't support whole row reference. */
if (var->varattno == 0)
return false;
rte = planner_rt_fetch(var->varno, root);
colname = get_attname(rte->relid, var->varattno, false);
/* Don't support full document retrieval */
if (strcmp("__doc", colname) == 0)
return false;
}
fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
}
}
}
/*
* If there's some PlaceHolderVar that would need to be evaluated within
* this join tree (because there's an upper reference to a quantity that
* may go to NULL as a result of an outer join), then we can't try to push
* the join down.
*/
foreach(lc, root->placeholder_list)
{
PlaceHolderInfo *phinfo = lfirst(lc);
Relids relids;
/* PlaceHolderInfo refers to parent relids, not child relids. */
relids = IS_OTHER_REL(joinrel) ?
joinrel->top_parent_relids : joinrel->relids;
if (bms_is_subset(phinfo->ph_eval_at, relids) &&
bms_nonempty_difference(relids, phinfo->ph_eval_at))
return false;
}
/* Save the join clauses, for later use. */
fpinfo->joinclauses = joinclauses;
fpinfo->outerrel = outerrel;
fpinfo->innerrel = innerrel;
fpinfo->jointype = jointype;
/*
* Pull the other remote conditions from the joining relations into join
* clauses or other remote clauses (remote_conds) of this relation. This
* avoids building sub-queries at every join step.
*
* For an INNER and OUTER join, the clauses from the outer side are added
* to remote_conds since those can be evaluated after the join is
* evaluated. The clauses from the inner side are added to the
* joinclauses, since they need to be evaluated while constructing the
* join.
*
* The joining sides cannot have local conditions, thus no need to test
* the shippability of the clauses being pulled up.
*/
switch (jointype)
{
case JOIN_INNER:
case JOIN_LEFT:
fpinfo->joinclauses = mongo_list_concat(fpinfo->joinclauses,
fpinfo_i->remote_conds);
fpinfo->remote_conds = mongo_list_concat(fpinfo->remote_conds,
fpinfo_o->remote_conds);
break;
case JOIN_RIGHT:
fpinfo->joinclauses = mongo_list_concat(fpinfo->joinclauses,
fpinfo_o->remote_conds);
fpinfo->remote_conds = mongo_list_concat(fpinfo->remote_conds,
fpinfo_i->remote_conds);
break;
default:
/* Should not happen, we have just checked this above */
elog(ERROR, "unsupported join type %d", jointype);
}
fpinfo->outer_relname = fpinfo_o->base_relname;
fpinfo->inner_relname = fpinfo_i->base_relname;
/* Mark that this join can be pushed down safely */
fpinfo->pushdown_safe = true;
/* Joinrel's aggregation flag depends on each joining relation's flag. */
fpinfo->is_agg_scanrel_pushable = fpinfo_o->is_agg_scanrel_pushable &&
fpinfo_i->is_agg_scanrel_pushable;
/* Set the flag is_order_by_pushable of the join relation */
fpinfo->is_order_by_pushable = fpinfo_o->is_order_by_pushable &&
fpinfo_i->is_order_by_pushable;
/*
* Set the string describing this join relation to be used in EXPLAIN
* output of the corresponding ForeignScan.
*/
fpinfo->relation_name = makeStringInfo();
appendStringInfo(fpinfo->relation_name, "(%s) %s JOIN (%s)",
fpinfo_o->relation_name->data,
mongo_get_jointype_name(fpinfo->jointype),
fpinfo_i->relation_name->data);
return true;
}
/*
* mongo_prepare_qual_info
* Gather information of columns involved in the quals by extracting
* clause from each qual and process it further using mongo_check_qual().
*/
static void
mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info)
{
ListCell *lc;
foreach(lc, quals)
{
Expr *expr = (Expr *) lfirst(lc);
/* Extract clause from RestrictInfo */
if (IsA(expr, RestrictInfo))
{
RestrictInfo *ri = (RestrictInfo *) expr;
expr = ri->clause;
}
mongo_check_qual(expr, qual_info);
}
}
/*
* mongo_foreign_grouping_ok
* Assess whether the aggregation, grouping and having operations can
* be pushed down to the foreign server. As a side effect, save
* information we obtain in this function to MongoFdwRelationInfo of
* the input relation.
*/
static bool
mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel,
Node *havingQual)
{
Query *query = root->parse;
PathTarget *grouping_target = grouped_rel->reltarget;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) grouped_rel->fdw_private;
MongoFdwRelationInfo *ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private;
ListCell *lc;
int i;
List *tlist = NIL;
/* Grouping Sets are not pushable */
if (query->groupingSets)
return false;
/*
* If underneath input relation has any local conditions, those conditions
* are required to be applied before performing aggregation. Hence the
* aggregate cannot be pushed down.
*/
if (ofpinfo->local_conds)
return false;
/*
* Evaluate grouping targets and check whether they are safe to push down
* to the foreign side. All GROUP BY expressions will be part of the
* grouping target and thus there is no need to evaluate them separately.
* While doing so, add required expressions into the target list which can
* then be used to pass to a foreign server.
*/
i = 0;
foreach(lc, grouping_target->exprs)
{
Expr *expr = (Expr *) lfirst(lc);
Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
ListCell *l;
/* Check whether this expression is part of GROUP BY clause */
if (sgref && get_sortgroupref_clause_noerr(sgref, query->groupClause))
{
TargetEntry *tle;
/*
* If any of the GROUP BY expression is not shippable we can not
* push down aggregation to the foreign server.
*/
if (!mongo_is_foreign_expr(root, grouped_rel, expr, false))
return false;
/* Add column in group by column list */
ofpinfo->groupbyColList = lappend(ofpinfo->groupbyColList, expr);
/*
* If it would be a foreign param, we can't put it into the tlist,
* so we have to fail.
*/
if (mongo_is_foreign_param(root, grouped_rel, expr))
return false;
/*
* Pushable, so add to tlist. We need to create a TLE for this
* expression and apply the sortgroupref to it. We cannot use
* add_to_flat_tlist() here because that avoids making duplicate
* entries in the tlist. If there are duplicate entries with
* distinct sortgrouprefs, we have to duplicate that situation in
* the output tlist.
*/
tle = makeTargetEntry(expr, list_length(tlist) + 1, NULL, false);
tle->ressortgroupref = sgref;
tlist = lappend(tlist, tle);
}
else
{
/* Check entire expression whether it is pushable or not */
if (mongo_is_foreign_expr(root, grouped_rel, expr, false) &&
!mongo_is_foreign_param(root, grouped_rel, expr))
{
/* Pushable, add to tlist */
tlist = add_to_flat_tlist(tlist, list_make1(expr));
}
else
{
List *aggvars;
/* Not matched exactly, pull the var with aggregates then */
aggvars = pull_var_clause((Node *) expr,
PVC_INCLUDE_AGGREGATES);
/*
* If any aggregate expression is not shippable, then we
* cannot push down aggregation to the foreign server.
*/
if (!mongo_is_foreign_expr(root, grouped_rel, (Expr *) aggvars,
false))
return false;
/*
* Add aggregates, if any, into the targetlist. Plain var
* nodes should be either same as some GROUP BY expression or
* part of some GROUP BY expression. In later case, the query
* cannot refer plain var nodes without the surrounding
* expression. In both the cases, they are already part of
* the targetlist and thus no need to add them again. In fact
* adding pulled plain var nodes in SELECT clause will cause
* an error on the foreign server if they are not same as some
* GROUP BY expression.
*/
foreach(l, aggvars)
{
expr = (Expr *) lfirst(l);
if (IsA(expr, Aggref))
tlist = add_to_flat_tlist(tlist, list_make1(expr));
}
}
}
i++;
}
/*
* Classify the pushable and non-pushable having clauses and save them in
* remote_conds and local_conds of the grouped rel's fpinfo.
*/
if (havingQual)
{
foreach(lc, (List *) havingQual)
{
Expr *expr = (Expr *) lfirst(lc);
RestrictInfo *rinfo;
/*
* Currently, the core code doesn't wrap havingQuals in
* RestrictInfos, so we must make our own.
*/
Assert(!IsA(expr, RestrictInfo));
#if PG_VERSION_NUM >= 160000
rinfo = make_restrictinfo(root,
expr,
true,
false,
false,
false,
root->qual_security_level,
grouped_rel->relids,
NULL,
NULL);
#elif PG_VERSION_NUM >= 140000
rinfo = make_restrictinfo(root,
expr,
true,
false,
false,
root->qual_security_level,
grouped_rel->relids,
NULL,
NULL);
#else
rinfo = make_restrictinfo(expr,
true,
false,
false,
root->qual_security_level,
grouped_rel->relids,
NULL,
NULL);
#endif
if (!mongo_is_foreign_expr(root, grouped_rel, expr, true))
fpinfo->local_conds = lappend(fpinfo->local_conds, rinfo);
else
fpinfo->remote_conds = lappend(fpinfo->remote_conds, rinfo);
}
}
/*
* If there are any local conditions, pull Vars and aggregates from it and
* check whether they are safe to pushdown or not.
*/
if (fpinfo->local_conds)
{
List *aggvars = NIL;
foreach(lc, fpinfo->local_conds)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
aggvars = list_concat(aggvars,
pull_var_clause((Node *) rinfo->clause,
PVC_INCLUDE_AGGREGATES));
}
foreach(lc, aggvars)
{
Expr *expr = (Expr *) lfirst(lc);
/*
* If aggregates within local conditions are not safe to push
* down, then we cannot push down the query. Vars are already
* part of GROUP BY clause which are checked above, so no need to
* access them again here.
*/
if (IsA(expr, Aggref))
{
if (!mongo_is_foreign_expr(root, grouped_rel, expr, false))
return false;
tlist = add_to_flat_tlist(tlist, list_make1(expr));
}
}
}
/* Store generated targetlist */
fpinfo->grouped_tlist = tlist;
/* Safe to pushdown */
fpinfo->pushdown_safe = true;
/*
* Set the string describing this grouped relation to be used in EXPLAIN
* output of corresponding ForeignScan.
*/
fpinfo->relation_name = makeStringInfo();
appendStringInfo(fpinfo->relation_name, "Aggregate on (%s)",
ofpinfo->relation_name->data);
return true;
}
/*
* mongoGetForeignUpperPaths
* Add paths for post-join operations like aggregation, grouping etc. if
* corresponding operations are safe to push down.
*/
static void
mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage,
RelOptInfo *input_rel, RelOptInfo *output_rel,
void *extra)
{
MongoFdwRelationInfo *fpinfo;
/*
* If input rel is not safe to pushdown, then simply return as we cannot
* perform any post-join operations on the foreign server.
*/
if (!input_rel->fdw_private ||
!((MongoFdwRelationInfo *) input_rel->fdw_private)->pushdown_safe)
return;
/* Ignore stages we don't support; and skip any duplicate calls. */
if ((stage != UPPERREL_GROUP_AGG && stage != UPPERREL_ORDERED &&
stage != UPPERREL_FINAL) ||
output_rel->fdw_private)
return;
fpinfo = (MongoFdwRelationInfo *) palloc0(sizeof(MongoFdwRelationInfo));
fpinfo->pushdown_safe = false;
fpinfo->stage = stage;
output_rel->fdw_private = fpinfo;
switch (stage)
{
case UPPERREL_GROUP_AGG:
mongo_add_foreign_grouping_paths(root, input_rel, output_rel,
(GroupPathExtraData *) extra);
break;
case UPPERREL_ORDERED:
mongo_add_foreign_ordered_paths(root, input_rel, output_rel);
break;
case UPPERREL_FINAL:
mongo_add_foreign_final_paths(root, input_rel, output_rel,
(FinalPathExtraData *) extra);
break;
default:
elog(ERROR, "unexpected upper relation: %d", (int) stage);
break;
}
}
/*
* mongo_add_foreign_grouping_paths
* Add foreign path for grouping and/or aggregation.
*
* Given input_rel represents the underlying scan. The paths are added to the
* given grouped_rel.
*/
static void
mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel,
RelOptInfo *grouped_rel,
GroupPathExtraData *extra)
{
Query *parse = root->parse;
MongoFdwRelationInfo *fpinfo = grouped_rel->fdw_private;
ForeignPath *grouppath;
Cost startup_cost;
Cost total_cost;
double num_groups;
/* Nothing to be done, if there is no grouping or aggregation required. */
if (!parse->groupClause && !parse->groupingSets && !parse->hasAggs &&
!root->hasHavingQual)
return;
/* Save the input_rel as outerrel in fpinfo */
fpinfo->outerrel = input_rel;
/* Set aggregation flag of aggregate relation */
fpinfo->is_agg_scanrel_pushable =
((MongoFdwRelationInfo *) input_rel->fdw_private)->is_agg_scanrel_pushable;
/* If aggregate pushdown is not enabled, honor it. */
if (!enable_aggregate_pushdown || !fpinfo->is_agg_scanrel_pushable)
return;
/* Assess if it is safe to push down aggregation and grouping. */
if (!mongo_foreign_grouping_ok(root, grouped_rel, extra->havingQual))
return;
fpinfo->is_order_by_pushable =
((MongoFdwRelationInfo *) input_rel->fdw_private)->is_order_by_pushable;
/*
* TODO: Put accurate estimates here.
*
* Cost used here is minimum of the cost estimated for base and join
* relation.
*/
startup_cost = 15;
total_cost = 10 + startup_cost;
/* Estimate output tuples which should be same as number of groups */
#if PG_VERSION_NUM >= 140000
num_groups = estimate_num_groups(root,
get_sortgrouplist_exprs(root->parse->groupClause,
fpinfo->grouped_tlist),
input_rel->rows, NULL, NULL);
#else
num_groups = estimate_num_groups(root,
get_sortgrouplist_exprs(root->parse->groupClause,
fpinfo->grouped_tlist),
input_rel->rows, NULL);
#endif
/* Create and add foreign path to the grouping relation. */
#if PG_VERSION_NUM >= 180000
grouppath = create_foreign_upper_path(root,
grouped_rel,
grouped_rel->reltarget,
num_groups,
0,
startup_cost,
total_cost,
NIL, /* no pathkeys */
NULL,
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private */
#elif PG_VERSION_NUM >= 170000
grouppath = create_foreign_upper_path(root,
grouped_rel,
grouped_rel->reltarget,
num_groups,
startup_cost,
total_cost,
NIL, /* no pathkeys */
NULL,
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private */
#else
grouppath = create_foreign_upper_path(root,
grouped_rel,
grouped_rel->reltarget,
num_groups,
startup_cost,
total_cost,
NIL, /* no pathkeys */
NULL,
NIL); /* no fdw_private */
#endif
/* Add generated path into grouped_rel by add_path(). */
add_path(grouped_rel, (Path *) grouppath);
}
/*
* mongoEstimateCosts
* Estimate the remote query cost
*/
static void
mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost,
Oid foreigntableid)
{
MongoFdwOptions *options;
/* Fetch options */
options = mongo_get_options(foreigntableid);
/* Local databases are probably faster */
if (strcmp(options->svr_address, "127.0.0.1") == 0 ||
strcmp(options->svr_address, "localhost") == 0)
*startup_cost = 10;
else
*startup_cost = 25;
*total_cost = baserel->rows + *startup_cost;
}
/*
* mongo_get_useful_ecs_for_relation
* Determine which EquivalenceClasses might be involved in useful
* orderings of this relation.
*
* This function is in some respects a mirror image of the core function
* pathkeys_useful_for_merging: for a regular table, we know what indexes
* we have and want to test whether any of them are useful. For a foreign
* table, we don't know what indexes are present on the remote side but
* want to speculate about which ones we'd like to use if they existed.
*
* This function returns a list of potentially-useful equivalence classes,
* but it does not guarantee that an EquivalenceMember exists which contains
* Vars only from the given relation. For example, given ft1 JOIN t1 ON
* ft1.x + t1.x = 0, this function will say that the equivalence class
* containing ft1.x + t1.x is potentially useful. Supposing ft1 is remote and
* t1 is local (or on a different server), it will turn out that no useful
* ORDER BY clause can be generated. It's not our job to figure that out
* here; we're only interested in identifying relevant ECs.
*/
static List *
mongo_get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel)
{
List *useful_eclass_list = NIL;
ListCell *lc;
Relids relids;
/*
* First, consider whether any active EC is potentially useful for a merge
* join against this relation.
*/
if (rel->has_eclass_joins)
{
foreach(lc, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc);
if (eclass_useful_for_merging(root, cur_ec, rel))
useful_eclass_list = lappend(useful_eclass_list, cur_ec);
}
}
/*
* Next, consider whether there are any non-EC derivable join clauses that
* are merge-joinable. If the joininfo list is empty, we can exit
* quickly.
*/
if (rel->joininfo == NIL)
return useful_eclass_list;
/* If this is a child rel, we must use the topmost parent rel to search. */
if (IS_OTHER_REL(rel))
{
Assert(!bms_is_empty(rel->top_parent_relids));
relids = rel->top_parent_relids;
}
else
relids = rel->relids;
/* Check each join clause in turn. */
foreach(lc, rel->joininfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(lc);
/* Consider only mergejoinable clauses */
if (restrictinfo->mergeopfamilies == NIL)
continue;
/* Make sure we've got canonical ECs. */
update_mergeclause_eclasses(root, restrictinfo);
/*
* restrictinfo->mergeopfamilies != NIL is sufficient to guarantee
* that left_ec and right_ec will be initialized, per comments in
* distribute_qual_to_rels.
*
* We want to identify which side of this merge-joinable clause
* contains columns from the relation produced by this RelOptInfo. We
* test for overlap, not containment, because there could be extra
* relations on either side. For example, suppose we've got something
* like ((A JOIN B ON A.x = B.x) JOIN C ON A.y = C.y) LEFT JOIN D ON
* A.y = D.y. The input rel might be the joinrel between A and B, and
* we'll consider the join clause A.y = D.y. relids contains a
* relation not involved in the join class (B) and the equivalence
* class for the left-hand side of the clause contains a relation not
* involved in the input rel (C). Despite the fact that we have only
* overlap and not containment in either direction, A.y is potentially
* useful as a sort column.
*
* Note that it's even possible that relids overlaps neither side of
* the join clause. For example, consider A LEFT JOIN B ON A.x = B.x
* AND A.x = 1. The clause A.x = 1 will appear in B's joininfo list,
* but overlaps neither side of B. In that case, we just skip this
* join clause, since it doesn't suggest a useful sort order for this
* relation.
*/
if (bms_overlap(relids, restrictinfo->right_ec->ec_relids))
useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
restrictinfo->right_ec);
else if (bms_overlap(relids, restrictinfo->left_ec->ec_relids))
useful_eclass_list = list_append_unique_ptr(useful_eclass_list,
restrictinfo->left_ec);
}
return useful_eclass_list;
}
/*
* mongo_get_useful_pathkeys_for_relation
* Determine which orderings of a relation might be useful.
*
* Getting data in sorted order can be useful either because the requested
* order matches the final output ordering for the overall query we're
* planning, or because it enables an efficient merge join. Here, we try
* to figure out which pathkeys to consider.
*
* MongoDB considers null values as the "smallest" ones, so they appear first
* when sorting in ascending order, and appear last when sorting in descending
* order. MongoDB doesn't have provision for "NULLS FIRST" and "NULLS LAST"
* like syntaxes. So, by considering all these restrictions from MongoDB, we
* can support push-down of only below two cases of the ORDER BY clause:
*
* 1. ORDER BY ASC NULLS FIRST
* 2. ORDER BY DESC NULLS LAST
*
* Where, expr can only be a column and not any expression because MongoDB
* sorts only on fields. Multiple columns can be provided.
*/
static List *
mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel)
{
List *useful_pathkeys_list = NIL;
List *useful_eclass_list;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) rel->fdw_private;
EquivalenceClass *query_ec = NULL;
ListCell *lc;
/*
* Pushing the query_pathkeys to the remote server is always worth
* considering, because it might let us avoid a local sort.
*/
fpinfo->qp_is_pushdown_safe = false;
if (root->query_pathkeys)
{
bool query_pathkeys_ok = true;
foreach(lc, root->query_pathkeys)
{
PathKey *pathkey = (PathKey *) lfirst(lc);
/* Only ASC NULLS FIRST and DESC NULLS LAST can be pushed down */
if (!IS_PATHKEY_PUSHABLE(pathkey))
{
query_pathkeys_ok = false;
break;
}
/*
* The planner and executor don't have any clever strategy for
* taking data sorted by a prefix of the query's pathkeys and
* getting it to be sorted by all of those pathkeys. We'll just
* end up resorting the entire data set. So, unless we can push
* down all of the query pathkeys, forget it.
*/
if (!mongo_is_foreign_pathkey(root, rel, pathkey))
{
query_pathkeys_ok = false;
break;
}
}
if (query_pathkeys_ok)
{
useful_pathkeys_list = list_make1(list_copy(root->query_pathkeys));
fpinfo->qp_is_pushdown_safe = true;
}
}
/* Get the list of interesting EquivalenceClasses. */
useful_eclass_list = mongo_get_useful_ecs_for_relation(root, rel);
/* Extract unique EC for query, if any, so we don't consider it again. */
if (list_length(root->query_pathkeys) == 1)
{
PathKey *query_pathkey = linitial(root->query_pathkeys);
query_ec = query_pathkey->pk_eclass;
}
/*
* As a heuristic, the only pathkeys we consider here are those of length
* one. It's surely possible to consider more, but since each one we
* choose to consider will generate a round-trip to the remote side, we
* need to be a bit cautious here. It would sure be nice to have a local
* cache of information about remote index definitions...
*/
foreach(lc, useful_eclass_list)
{
EquivalenceClass *cur_ec = lfirst(lc);
EquivalenceMember *em;
Expr *em_expr;
PathKey *pathkey;
/* If redundant with what we did above, skip it. */
if (cur_ec == query_ec)
continue;
/* Can't push down the sort if the EC's opfamily is not shippable. */
if (!mongo_is_builtin(linitial_oid(cur_ec->ec_opfamilies)))
continue;
/* If no pushable expression for this rel, skip it. */
if (!(em = mongo_find_em_for_rel(root, cur_ec, rel)))
continue;
/* Ignore binary-compatible relabeling */
em_expr = em->em_expr;
while (em_expr && IsA(em_expr, RelabelType))
em_expr = ((RelabelType *) em_expr)->arg;
/* Only Vars are allowed per MongoDB. */
if (!IsA(em_expr, Var))
continue;
/* Looks like we can generate a pathkey, so let's do it. */
pathkey = make_canonical_pathkey(root, cur_ec,
linitial_oid(cur_ec->ec_opfamilies),
BTLessStrategyNumber,
false);
if (!IS_PATHKEY_PUSHABLE(pathkey))
continue;
/* Check for sort operator pushability. */
if (!mongo_is_default_sort_operator(em, pathkey))
continue;
useful_pathkeys_list = lappend(useful_pathkeys_list,
list_make1(pathkey));
}
return useful_pathkeys_list;
}
/*
* mongo_add_paths_with_pathkeys
* Add path with root->query_pathkeys if that's pushable.
*
* Pushing down query_pathkeys to the foreign server might let us avoid a
* local sort.
*/
#if PG_VERSION_NUM >= 170000
static void
mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel,
Path *epq_path, Cost base_startup_cost,
Cost base_total_cost, List *restrictlist)
#else
static void
mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel,
Path *epq_path, Cost base_startup_cost,
Cost base_total_cost)
#endif
{
ListCell *lc;
List *useful_pathkeys_list = NIL; /* List of all pathkeys */
/* If orderby pushdown is not enabled, honor it. */
if (!enable_order_by_pushdown ||
!((MongoFdwRelationInfo *) rel->fdw_private)->is_order_by_pushable)
return;
/*
* Check the query pathkeys length. Don't push when exceeding the limit
* set by MongoDB.
*/
if (list_length(root->query_pathkeys) > MAX_PATHKEYS)
return;
useful_pathkeys_list = mongo_get_useful_pathkeys_for_relation(root, rel);
/* Create one path for each set of pathkeys we found above. */
foreach(lc, useful_pathkeys_list)
{
Cost startup_cost;
Cost total_cost;
List *useful_pathkeys = lfirst(lc);
Path *sorted_epq_path;
/* TODO put accurate estimates. */
startup_cost = base_startup_cost * DEFAULT_MONGO_SORT_MULTIPLIER;
total_cost = base_total_cost * DEFAULT_MONGO_SORT_MULTIPLIER;
/*
* The EPQ path must be at least as well sorted as the path itself, in
* case it gets used as input to a mergejoin.
*/
sorted_epq_path = epq_path;
if (sorted_epq_path != NULL &&
!pathkeys_contained_in(useful_pathkeys,
sorted_epq_path->pathkeys))
sorted_epq_path = (Path *)
create_sort_path(root,
rel,
sorted_epq_path,
useful_pathkeys,
-1.0);
if (IS_SIMPLE_REL(rel))
#if PG_VERSION_NUM >= 180000
add_path(rel, (Path *)
create_foreignscan_path(root, rel,
NULL,
rel->rows,
0,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
NIL, /* no fdw_restrictinfo list */
NIL)); /* no fdw_private list */
#elif PG_VERSION_NUM >= 170000
add_path(rel, (Path *)
create_foreignscan_path(root, rel,
NULL,
rel->rows,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
NIL, /* no fdw_restrictinfo list */
NIL)); /* no fdw_private list */
#else
add_path(rel, (Path *)
create_foreignscan_path(root, rel,
NULL,
rel->rows,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
NIL)); /* no fdw_private list */
#endif
else
#if PG_VERSION_NUM >= 180000
add_path(rel, (Path *)
create_foreign_join_path(root, rel,
NULL,
rel->rows,
0,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
restrictlist,
NIL)); /* no fdw_private */
#elif PG_VERSION_NUM >= 170000
add_path(rel, (Path *)
create_foreign_join_path(root, rel,
NULL,
rel->rows,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
restrictlist,
NIL)); /* no fdw_private */
#else
add_path(rel, (Path *)
create_foreign_join_path(root, rel,
NULL,
rel->rows,
startup_cost,
total_cost,
useful_pathkeys,
rel->lateral_relids,
sorted_epq_path,
NIL)); /* no fdw_private */
#endif
}
}
/*
* mongo_find_em_for_rel
* Find an equivalence class member expression, all of whose Vars, come
* from the indicated relation.
*/
EquivalenceMember *
mongo_find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel)
{
ListCell *lc_em;
foreach(lc_em, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc_em);
/*
* Note we require !bms_is_empty, else we'd accept constant
* expressions which are not suitable for the purpose.
*/
if (bms_is_subset(em->em_relids, rel->relids) &&
!bms_is_empty(em->em_relids) &&
mongo_is_foreign_expr(root, rel, em->em_expr, false))
{
/*
* If there is more than one equivalence member whose Vars are
* taken entirely from this relation, we'll be content to choose
* any one of those.
*/
return em;
}
}
/* We didn't find any suitable equivalence class expression */
return NULL;
}
/*
* mongo_add_foreign_ordered_paths
* Add foreign paths for performing the final sort remotely.
*
* Given input_rel contains the source-data Paths. The paths are added to the
* given ordered_rel.
*/
static void
mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel,
RelOptInfo *ordered_rel)
{
Query *parse = root->parse;
MongoFdwRelationInfo *ifpinfo = input_rel->fdw_private;
MongoFdwRelationInfo *fpinfo = ordered_rel->fdw_private;
double rows;
Cost startup_cost;
Cost total_cost;
List *fdw_private;
ForeignPath *ordered_path;
ListCell *lc;
/* Set the flag is_order_by_pushable of the ordered relation */
fpinfo->is_order_by_pushable =
((MongoFdwRelationInfo *) input_rel->fdw_private)->is_order_by_pushable;
/* If orderby pushdown is not enabled, honor it. */
if (!enable_order_by_pushdown || !fpinfo->is_order_by_pushable)
return;
/* Shouldn't get here unless the query has ORDER BY */
Assert(parse->sortClause);
/* We don't support cases where there are any SRFs in the targetlist */
if (parse->hasTargetSRFs)
return;
/*
* Check the query pathkeys length. Don't push when exceeding the limit
* set by MongoDB.
*/
if (list_length(root->query_pathkeys) > MAX_PATHKEYS)
return;
/* Save the input_rel as outerrel in fpinfo */
fpinfo->outerrel = input_rel;
/*
* If the input_rel is a base or join relation, we would already have
* considered pushing down the final sort to the remote server when
* creating pre-sorted foreign paths for that relation, because the
* query_pathkeys is set to the root->sort_pathkeys in that case (see
* standard_qp_callback()).
*/
if (input_rel->reloptkind == RELOPT_BASEREL ||
input_rel->reloptkind == RELOPT_JOINREL)
{
Assert(root->query_pathkeys == root->sort_pathkeys);
/* Safe to push down */
fpinfo->pushdown_safe = ifpinfo->qp_is_pushdown_safe;
return;
}
/* The input_rel should be a grouping relation */
Assert(input_rel->reloptkind == RELOPT_UPPER_REL &&
ifpinfo->stage == UPPERREL_GROUP_AGG);
/*
* We try to create a path below by extending a simple foreign path for
* the underlying grouping relation to perform the final sort remotely,
* which is stored into the fdw_private list of the resulting path.
*/
/* Assess if it is safe to push down the final sort */
foreach(lc, root->sort_pathkeys)
{
PathKey *pathkey = (PathKey *) lfirst(lc);
EquivalenceClass *pathkey_ec = pathkey->pk_eclass;
EquivalenceMember *em = NULL;
Expr *sort_expr;
/*
* mongo_is_foreign_expr would detect volatile expressions as well,
* but checking ec_has_volatile here saves some cycles.
*/
if (pathkey_ec->ec_has_volatile)
return;
if (!IS_PATHKEY_PUSHABLE(pathkey))
return;
/*
* Get the sort expression for the pathkey_ec. The EC must contain a
* shippable EM that is computed in input_rel's reltarget, else we
* can't push down the sort.
*/
em = mongo_find_em_for_rel_target(root, pathkey_ec, input_rel);
/* Check for sort operator pushability. */
if (!mongo_is_default_sort_operator(em, pathkey))
return;
/* Ignore binary-compatible relabeling */
sort_expr = em->em_expr;
while (sort_expr && IsA(sort_expr, RelabelType))
sort_expr = ((RelabelType *) sort_expr)->arg;
/* Only Vars are allowed per MongoDB. */
if (!IsA(sort_expr, Var))
return;
}
/* Safe to push down */
fpinfo->pushdown_safe = true;
/* TODO: Put accurate estimates */
startup_cost = 10;
total_cost = 10 + startup_cost;
rows = 10;
/*
* Build the fdw_private list that will be used by mongoGetForeignPlan.
* Items in the list must match the order in the enum FdwPathPrivateIndex.
*/
fdw_private = list_make2(makeInteger(true), makeInteger(false));
/* Create foreign ordering path */
#if PG_VERSION_NUM >= 180000
ordered_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_ORDERED],
rows,
0,
startup_cost,
total_cost,
root->sort_pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
fdw_private);
#elif PG_VERSION_NUM >= 170000
ordered_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_ORDERED],
rows,
startup_cost,
total_cost,
root->sort_pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
fdw_private);
#else
ordered_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_ORDERED],
rows,
startup_cost,
total_cost,
root->sort_pathkeys,
NULL, /* no extra plan */
fdw_private);
#endif
/* and add it to the ordered_rel */
add_path(ordered_rel, (Path *) ordered_path);
}
/*
* mongo_add_foreign_final_paths
* Add foreign paths for performing the final processing remotely.
*
* Given input_rel contains the source-data Paths. The paths are added to the
* given final_rel.
*/
static void
mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel,
RelOptInfo *final_rel, FinalPathExtraData *extra)
{
Query *parse = root->parse;
MongoFdwRelationInfo *ifpinfo = (MongoFdwRelationInfo *) input_rel->fdw_private;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) final_rel->fdw_private;
bool has_final_sort = false;
List *pathkeys = NIL;
double rows;
Cost startup_cost;
Cost total_cost;
List *fdw_private;
ForeignPath *final_path;
/*
* Currently, we only support this for SELECT commands
*/
if (parse->commandType != CMD_SELECT)
return;
/*
* We do not support LIMIT with FOR UPDATE/SHARE. Also, if there is no
* FOR UPDATE/SHARE clause and there is no LIMIT, don't need to add
* Foreign final path.
*/
if (parse->rowMarks || !extra->limit_needed)
return;
/* We don't support cases where there are any SRFs in the targetlist */
if (parse->hasTargetSRFs)
return;
/* Save the input_rel as outerrel in fpinfo */
fpinfo->outerrel = input_rel;
/*
* If there is no need to add a LIMIT node, there might be a ForeignPath
* in the input_rel's pathlist that implements all behavior of the query.
* Note: we would already have accounted for the query's FOR UPDATE/SHARE
* (if any) before we get here.
*/
if (!extra->limit_needed)
{
ListCell *lc;
Assert(parse->rowMarks);
/*
* Grouping and aggregation are not supported with FOR UPDATE/SHARE,
* so the input_rel should be a base, join, or ordered relation; and
* if it's an ordered relation, its input relation should be a base or
* join relation.
*/
Assert(input_rel->reloptkind == RELOPT_BASEREL ||
input_rel->reloptkind == RELOPT_JOINREL ||
(input_rel->reloptkind == RELOPT_UPPER_REL &&
ifpinfo->stage == UPPERREL_ORDERED &&
(ifpinfo->outerrel->reloptkind == RELOPT_BASEREL ||
ifpinfo->outerrel->reloptkind == RELOPT_JOINREL)));
foreach(lc, input_rel->pathlist)
{
Path *path = (Path *) lfirst(lc);
/*
* apply_scanjoin_target_to_paths() uses create_projection_path()
* to adjust each of its input paths if needed, whereas
* create_ordered_paths() uses apply_projection_to_path() to do
* that. So the former might have put a ProjectionPath on top of
* the ForeignPath; look through ProjectionPath and see if the
* path underneath it is ForeignPath.
*/
if (IsA(path, ForeignPath) ||
(IsA(path, ProjectionPath) &&
IsA(((ProjectionPath *) path)->subpath, ForeignPath)))
{
/*
* Create foreign final path; this gets rid of a
* no-longer-needed outer plan (if any), which makes the
* EXPLAIN output look cleaner
*/
#if PG_VERSION_NUM >= 180000
final_path = create_foreign_upper_path(root,
path->parent,
path->pathtarget,
path->rows,
0,
path->startup_cost,
path->total_cost,
path->pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private */
#elif PG_VERSION_NUM >= 170000
final_path = create_foreign_upper_path(root,
path->parent,
path->pathtarget,
path->rows,
path->startup_cost,
path->total_cost,
path->pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
NIL); /* no fdw_private */
#else
final_path = create_foreign_upper_path(root,
path->parent,
path->pathtarget,
path->rows,
path->startup_cost,
path->total_cost,
path->pathkeys,
NULL, /* no extra plan */
NIL); /* no fdw_private */
#endif
/* and add it to the final_rel */
add_path(final_rel, (Path *) final_path);
/* Safe to push down */
fpinfo->pushdown_safe = true;
return;
}
}
/*
* If we get here it means no ForeignPaths; since we would already
* have considered pushing down all operations for the query to the
* remote server, give up on it.
*/
return;
}
Assert(extra->limit_needed);
/*
* If the input_rel is an ordered relation, replace the input_rel with its
* input relation
*/
if (input_rel->reloptkind == RELOPT_UPPER_REL &&
ifpinfo->stage == UPPERREL_ORDERED)
{
/* Do not push down LIMIT if ORDER BY push down is disabled */
if (!enable_order_by_pushdown)
return;
input_rel = ifpinfo->outerrel;
ifpinfo = (MongoFdwRelationInfo *) input_rel->fdw_private;
has_final_sort = true;
pathkeys = root->sort_pathkeys;
}
/* The input_rel should be a base, join, or grouping relation */
Assert(input_rel->reloptkind == RELOPT_BASEREL ||
input_rel->reloptkind == RELOPT_JOINREL ||
(input_rel->reloptkind == RELOPT_UPPER_REL &&
ifpinfo->stage == UPPERREL_GROUP_AGG));
/*
* We try to create a path below by extending a simple foreign path for
* the underlying base, join, or grouping relation to perform the final
* sort (if has_final_sort) and the LIMIT restriction remotely, which is
* stored into the fdw_private list of the resulting path. (We
* re-estimate the costs of sorting the underlying relation, if
* has_final_sort.)
*/
/*
* Assess if it is safe to push down the LIMIT and OFFSET to the remote
* server
*/
/*
* If the underlying relation has any local conditions, the LIMIT/OFFSET
* cannot be pushed down.
*/
if (ifpinfo->local_conds)
return;
/*
* Support only Const nodes as expressions are NOT supported on MongoDB.
* Also, MongoDB supports only positive 64-bit integer values, so don't
* pushdown in case of -ve values given for LIMIT/OFFSET clauses.
*/
if (parse->limitCount)
{
Node *node = parse->limitCount;
if (nodeTag(node) != T_Const ||
(((Const *) node)->consttype != INT8OID))
return;
if (!((Const *) node)->constisnull &&
(DatumGetInt64(((Const *) node)->constvalue) < 0))
return;
}
if (parse->limitOffset)
{
Node *node = parse->limitOffset;
if (nodeTag(node) != T_Const ||
(((Const *) node)->consttype != INT8OID))
return;
if (!((Const *) node)->constisnull &&
(DatumGetInt64(((Const *) node)->constvalue) < 0))
return;
}
/* Safe to push down */
fpinfo->pushdown_safe = true;
/* TODO: Put accurate estimates */
startup_cost = 1;
total_cost = 1 + startup_cost;
rows = 1;
/*
* Build the fdw_private list that will be used by mongoGetForeignPlan.
* Items in the list must match order in enum FdwPathPrivateIndex.
*/
fdw_private = list_make2(makeInteger(has_final_sort),
makeInteger(extra->limit_needed));
/*
* Create foreign final path; this gets rid of a no-longer-needed outer
* plan (if any), which makes the EXPLAIN output look cleaner
*/
#if PG_VERSION_NUM >= 180000
final_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_FINAL],
rows,
0,
startup_cost,
total_cost,
pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
fdw_private);
#elif PG_VERSION_NUM >= 170000
final_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_FINAL],
rows,
startup_cost,
total_cost,
pathkeys,
NULL, /* no extra plan */
NIL, /* no fdw_restrictinfo list */
fdw_private);
#else
final_path = create_foreign_upper_path(root,
input_rel,
root->upper_targets[UPPERREL_FINAL],
rows,
startup_cost,
total_cost,
pathkeys,
NULL, /* no extra plan */
fdw_private);
#endif
/* and add it to the final_rel */
add_path(final_rel, (Path *) final_path);
}
/*
* mongo_find_em_for_rel_target
* Find an equivalence class member expression to be computed as a sort
* column in the given target.
*/
static EquivalenceMember *
mongo_find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec,
RelOptInfo *rel)
{
PathTarget *target = rel->reltarget;
ListCell *lc1;
int i;
i = 0;
foreach(lc1, target->exprs)
{
Expr *expr = (Expr *) lfirst(lc1);
Index sgref = get_pathtarget_sortgroupref(target, i);
ListCell *lc2;
/* Ignore non-sort expressions */
if (sgref == 0 ||
get_sortgroupref_clause_noerr(sgref,
root->parse->sortClause) == NULL)
{
i++;
continue;
}
/* We ignore binary-compatible relabeling */
while (expr && IsA(expr, RelabelType))
expr = ((RelabelType *) expr)->arg;
/* Locate an EquivalenceClass member matching this expr, if any */
foreach(lc2, ec->ec_members)
{
EquivalenceMember *em = (EquivalenceMember *) lfirst(lc2);
Expr *em_expr;
/* Don't match constants */
if (em->em_is_const)
continue;
/* Ignore child members */
if (em->em_is_child)
continue;
/* Match if same expression (after stripping relabel) */
em_expr = em->em_expr;
while (em_expr && IsA(em_expr, RelabelType))
em_expr = ((RelabelType *) em_expr)->arg;
if (!equal(em_expr, expr))
continue;
/*
* Check that expression (including relabels!) is shippable. If
* it's unsafe to remote, we cannot push down the final sort.
*/
if (mongo_is_foreign_expr(root, rel, em->em_expr, false))
return em;
}
i++;
}
return NULL; /* keep compiler quiet */
}
/*
* mongo_is_default_sort_operator
* Returns true if default sort operator is provided.
*/
bool
mongo_is_default_sort_operator(EquivalenceMember *em, PathKey *pathkey)
{
Oid oprid;
char *oprname;
TypeCacheEntry *typentry;
if (em == NULL)
return false;
/* Can't push down the sort if pathkey's opfamily is not shippable. */
if (!mongo_is_builtin(pathkey->pk_opfamily))
return NULL;
#if PG_VERSION_NUM >= 180000
oprid = get_opfamily_member_for_cmptype(pathkey->pk_opfamily,
em->em_datatype,
em->em_datatype,
pathkey->pk_cmptype);
#else
oprid = get_opfamily_member(pathkey->pk_opfamily,
em->em_datatype,
em->em_datatype,
pathkey->pk_strategy);
#endif
if (!OidIsValid(oprid))
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
#if PG_VERSION_NUM >= 180000
pathkey->pk_cmptype, em->em_datatype, em->em_datatype,
#else
pathkey->pk_strategy, em->em_datatype, em->em_datatype,
#endif
pathkey->pk_opfamily);
/* Can't push down the sort if the operator is not shippable. */
oprname = get_opname(oprid);
if (!((strncmp(oprname, "<", NAMEDATALEN) == 0) ||
(strncmp(oprname, ">", NAMEDATALEN) == 0)))
return false;
/*
* See whether the operator is default < or > for sort expr's datatype.
* Here we need to use the expression's actual type to discover whether
* the desired operator will be the default or not.
*/
typentry = lookup_type_cache(exprType((Node *) em->em_expr),
TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
if (oprid == typentry->lt_opr || oprid == typentry->gt_opr)
return true;
return false;
}
mongo_fdw-REL-5_5_3/mongo_fdw.control 0000664 0000000 0000000 00000000422 15066665201 0017653 0 ustar 00root root 0000000 0000000 # mongo_fdw extension
#
# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
# Portions Copyright © 2012–2014 Citus Data, Inc.
#
comment = 'foreign data wrapper for MongoDB access'
default_version = '1.1'
module_pathname = '$libdir/mongo_fdw'
relocatable = true
mongo_fdw-REL-5_5_3/mongo_fdw.h 0000664 0000000 0000000 00000037561 15066665201 0016440 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_fdw.h
* Foreign-data wrapper for remote MongoDB servers
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_fdw.h
*
*-------------------------------------------------------------------------
*/
#ifndef MONGO_FDW_H
#define MONGO_FDW_H
#include "mongo_wrapper.h"
#include "mongoc.h"
#include "access/reloptions.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_user_mapping.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/explain.h"
#include "commands/vacuum.h"
#include "fmgr.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "nodes/makefuncs.h"
#include "nodes/pg_list.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h"
#include "utils/hsearch.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#define BSON bson_t
#define BSON_TYPE bson_type_t
#define BSON_ITERATOR bson_iter_t
#define MONGO_CONN mongoc_client_t
#define MONGO_CURSOR mongoc_cursor_t
#define BSON_TYPE_DOCUMENT BSON_TYPE_DOCUMENT
#define BSON_TYPE_NULL BSON_TYPE_NULL
#define BSON_TYPE_ARRAY BSON_TYPE_ARRAY
#define BSON_TYPE_INT32 BSON_TYPE_INT32
#define BSON_TYPE_INT64 BSON_TYPE_INT64
#define BSON_TYPE_DOUBLE BSON_TYPE_DOUBLE
#define BSON_TYPE_BINDATA BSON_TYPE_BINARY
#define BSON_TYPE_BOOL BSON_TYPE_BOOL
#define BSON_TYPE_UTF8 BSON_TYPE_UTF8
#define BSON_TYPE_OID BSON_TYPE_OID
#define BSON_TYPE_DATE_TIME BSON_TYPE_DATE_TIME
#define BSON_TYPE_SYMBOL BSON_TYPE_SYMBOL
#define BSON_TYPE_UNDEFINED BSON_TYPE_UNDEFINED
#define BSON_TYPE_REGEX BSON_TYPE_REGEX
#define BSON_TYPE_CODE BSON_TYPE_CODE
#define BSON_TYPE_CODEWSCOPE BSON_TYPE_CODEWSCOPE
#define BSON_TYPE_TIMESTAMP BSON_TYPE_TIMESTAMP
#define PREF_READ_PRIMARY_NAME "readPrimary"
#define PREF_READ_SECONDARY_NAME "readSecondary"
#define PREF_READ_PRIMARY_PREFERRED_NAME "readPrimaryPreferred"
#define PREF_READ_SECONDARY_PREFERRED_NAME "readSecondaryPreferred"
#define PREF_READ_NEAREST_NAME "readNearest"
#define BSON_ITER_BOOL bson_iter_bool
#define BSON_ITER_DOUBLE bson_iter_double
#define BSON_ITER_INT32 bson_iter_int32
#define BSON_ITER_INT64 bson_iter_int64
#define BSON_ITER_OID bson_iter_oid
#define BSON_ITER_UTF8 bson_iter_utf8
#define BSON_ITER_REGEX bson_iter_regex
#define BSON_ITER_DATE_TIME bson_iter_date_time
#define BSON_ITER_CODE bson_iter_code
#define BSON_ITER_VALUE bson_iter_value
#define BSON_ITER_KEY bson_iter_key
#define BSON_ITER_NEXT bson_iter_next
#define BSON_ITER_TYPE bson_iter_type
#define BSON_ITER_BINARY bson_iter_binary
/* Defines for valid option names */
#define OPTION_NAME_ADDRESS "address"
#define OPTION_NAME_PORT "port"
#define OPTION_NAME_DATABASE "database"
#define OPTION_NAME_COLLECTION "collection"
#define OPTION_NAME_USERNAME "username"
#define OPTION_NAME_PASSWORD "password"
#define OPTION_NAME_USE_REMOTE_ESTIMATE "use_remote_estimate"
#define OPTION_NAME_READ_PREFERENCE "read_preference"
#define OPTION_NAME_AUTHENTICATION_DATABASE "authentication_database"
#define OPTION_NAME_REPLICA_SET "replica_set"
#define OPTION_NAME_SSL "ssl"
#define OPTION_NAME_PEM_FILE "pem_file"
#define OPTION_NAME_PEM_PWD "pem_pwd"
#define OPTION_NAME_CA_FILE "ca_file"
#define OPTION_NAME_CA_DIR "ca_dir"
#define OPTION_NAME_CRL_FILE "crl_file"
#define OPTION_NAME_WEAK_CERT "weak_cert_validation"
#define OPTION_NAME_ENABLE_JOIN_PUSHDOWN "enable_join_pushdown"
#define OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN "enable_aggregate_pushdown"
#define OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN "enable_order_by_pushdown"
/* Default values for option parameters */
#define DEFAULT_IP_ADDRESS "127.0.0.1"
#define DEFAULT_PORT_NUMBER 27017
#define DEFAULT_DATABASE_NAME "test"
/* Defines for sending queries and converting types */
#define EQUALITY_OPERATOR_NAME "="
#define INITIAL_ARRAY_CAPACITY 8
#define MONGO_TUPLE_COST_MULTIPLIER 5
#define MONGO_CONNECTION_COST_MULTIPLIER 5
#define POSTGRES_TO_UNIX_EPOCH_DAYS (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE)
#define POSTGRES_TO_UNIX_EPOCH_USECS (POSTGRES_TO_UNIX_EPOCH_DAYS * USECS_PER_DAY)
/* Macro for list API backporting. */
#define mongo_list_concat(l1, l2) list_concat((l1), (l2))
/* Macro for hard-coded aggregation result key */
#define AGG_RESULT_KEY "v_agg"
/*
* We build a hash table that stores the column details. However, a table can
* have maximum MaxHeapAttributeNumber columns. And since we allow join only
* on two tables, we set the max hash table size to twice that limit.
*/
#define MaxHashTableSize (MaxHeapAttributeNumber * 2)
/*
* MongoValidOption keeps an option name and a context. When an option is
* passed into mongo_fdw objects (server and foreign table), we compare this
* option's name and context against those of valid options.
*/
typedef struct MongoValidOption
{
const char *optionName;
Oid optionContextId;
} MongoValidOption;
/* Array of options that are valid for mongo_fdw */
static const uint32 ValidOptionCount = 23;
static const MongoValidOption ValidOptionArray[] =
{
/* Foreign server options */
{OPTION_NAME_ADDRESS, ForeignServerRelationId},
{OPTION_NAME_PORT, ForeignServerRelationId},
{OPTION_NAME_USE_REMOTE_ESTIMATE, ForeignServerRelationId},
{OPTION_NAME_READ_PREFERENCE, ForeignServerRelationId},
{OPTION_NAME_AUTHENTICATION_DATABASE, ForeignServerRelationId},
{OPTION_NAME_REPLICA_SET, ForeignServerRelationId},
{OPTION_NAME_SSL, ForeignServerRelationId},
{OPTION_NAME_PEM_FILE, ForeignServerRelationId},
{OPTION_NAME_PEM_PWD, ForeignServerRelationId},
{OPTION_NAME_CA_FILE, ForeignServerRelationId},
{OPTION_NAME_CA_DIR, ForeignServerRelationId},
{OPTION_NAME_CRL_FILE, ForeignServerRelationId},
{OPTION_NAME_WEAK_CERT, ForeignServerRelationId},
{OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignServerRelationId},
{OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignServerRelationId},
{OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignServerRelationId},
/* Foreign table options */
{OPTION_NAME_DATABASE, ForeignTableRelationId},
{OPTION_NAME_COLLECTION, ForeignTableRelationId},
{OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignTableRelationId},
{OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignTableRelationId},
{OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignTableRelationId},
/* User mapping options */
{OPTION_NAME_USERNAME, UserMappingRelationId},
{OPTION_NAME_PASSWORD, UserMappingRelationId}
};
/*
* MongoFdwOptions holds the option values to be used when connecting to Mongo.
* To resolve these values, we first check foreign table's options, and if not
* present, we then fall back to the default values specified above.
*/
typedef struct MongoFdwOptions
{
char *svr_address;
uint16 svr_port;
char *svr_database;
char *collectionName;
char *svr_username;
char *svr_password;
bool use_remote_estimate; /* use remote estimate for rows */
char *readPreference;
char *authenticationDatabase;
char *replicaSet;
bool ssl;
char *pem_file;
char *pem_pwd;
char *ca_file;
char *ca_dir;
char *crl_file;
bool weak_cert_validation;
bool enable_join_pushdown;
bool enable_aggregate_pushdown;
bool enable_order_by_pushdown;
} MongoFdwOptions;
/*
* MongoFdwExecState keeps foreign data wrapper specific execution state that
* we create and hold onto when executing the query.
*
* Execution state of a foreign insert/update/delete operation.
*/
typedef struct MongoFdwModifyState
{
Relation rel; /* relcache entry for the foreign table */
List *target_attrs; /* list of target attribute numbers */
/* Info about parameters for prepared statement */
int p_nums; /* number of parameters to transmit */
FmgrInfo *p_flinfo; /* output conversion functions for them */
struct HTAB *columnMappingHash;
MONGO_CONN *mongoConnection; /* MongoDB connection */
MONGO_CURSOR *mongoCursor; /* MongoDB cursor */
BSON *queryDocument; /* Bson Document */
MongoFdwOptions *options;
AttrNumber rowidAttno; /* attnum of resjunk rowid column */
/* Join/Upper relation information */
uint32 relType; /* relation type. Base, Join, Upper, or Upper
* on join */
char *outerRelName; /* Outer relation name */
} MongoFdwModifyState;
/*
* ColumnMapping represents a hash table entry that maps a column name to
* column-related information. We construct these hash table entries to speed
* up the conversion from BSON documents to PostgreSQL tuples, and each hash
* entry maps the column name to the column's tuple index and its type-related
* information.
*/
typedef struct ColumnMapping
{
char columnName[NAMEDATALEN];
uint32 columnIndex;
Oid columnTypeId;
int32 columnTypeMod;
Oid columnArrayTypeId;
/* Column serial number in target list (set only for join rel) */
uint32 columnSerialNo;
} ColumnMapping;
/*
* FDW-specific planner information kept in RelOptInfo.fdw_private for a
* mongo_fdw foreign table. For a baserel, this struct is created by
* MongoGetForeignRelSize.
*/
typedef struct MongoFdwRelationInfo
{
/*
* True means that the relation can be pushed down. Always true for simple
* foreign scan.
*/
bool pushdown_safe;
/* baserestrictinfo clauses, broken down into safe and unsafe subsets. */
List *local_conds;
List *remote_conds;
/* Name of the base rel (not set for join rels!) */
char *base_relname;
/*
* Name of the relation while EXPLAINing ForeignScan. It is used for join
* relations but is set for all relations. For join relation, the name
* indicates which foreign tables are being joined and the join type used.
*/
StringInfo relation_name;
/* True means that the query_pathkeys is safe to push down */
bool qp_is_pushdown_safe;
/* Join information */
RelOptInfo *outerrel;
RelOptInfo *innerrel;
JoinType jointype;
List *joinclauses;
char *inner_relname;
char *outer_relname;
MongoFdwOptions *options; /* Options applicable for this relation */
/* Grouping information */
List *grouped_tlist;
List *groupbyColList;
/* Upper relation information */
UpperRelationKind stage;
/*
* True if the underlying scan relation involved in aggregation is
* pushable.
*/
bool is_agg_scanrel_pushable;
/* Inherit required flags from MongoFdwOptions */
bool is_order_by_pushable;
} MongoFdwRelationInfo;
/*
* MongoRelQualInfo contains column name, varno, varattno, and its relation
* name of columns involved in the join quals which is passed to the execution
* state through fdw_private. For upper relation, it also includes aggregate
* type, aggregate column name, and whether the aggregate is in target or in
* having clause details.
*
* Unlike postgres_fdw, remote query formation is done in the execution state.
* The information, mainly the varno i.e. range table index, we get at the
* execution time is different than the planning state. That may result in
* fetching incorrect data. So, to avoid this, we are gathering information
* required to form a MongoDB query in the planning state and passing it to the
* executor.
*
* For join relation:
* Assume, we have the following two tables with RTI 1 and 2 respectively:
* T1(a int, b int)
* T2(x int, y int)
*
* and if the join clause is like below with T1 as inner relation and T2 outer
* relation:
* (T1.a = T2.x AND T1.b > T2.y)
*
* then as columns a, b, x, and y are involved in the join clause, we need to
* form the following 4 lists as part of MongoRelQualInfo:
*
* 1. colNameList: List of column names
* a->x->b->y
* 2. colNumList: List of column attribute number
* 1->1->2->2
* 3. rtiList: Range table index of the column
* 1->2->1->2
* 4. isOuterList: Is it a column of an outer relation?
* 1->0->1->0
*
* If we want information related to column 'a', then look for information
* available at the zeroth index of all four lists.
*
* To avoid duplicate entry of columns, we use a hash table having a unique
* hash key as a set of varno and varattno.
*
* For upper relation:
* Assume, we have to calculate the sum of column 'a' and the average of column
* 'b' of the above table 'T1' where the minimum of a is greater than 1. This
* can be done by the following SQL query:
*
* SELECT SUM(a), AVG(b) FROM T1 HAVING MIN(a) > 1;
*
* Here, there are two aggregation types SUM and MIN, and two aggregation
* columns i.e. 'a' and 'b'. To differentiate between two aggregation
* operations, we need to save information about whether the aggregation
* operation is part of a target list or having clause. So, we need to form
* the following three lists as a part of MongoRelQualInfo:
*
* 1. aggTypeList: List of aggregation operations
* SUM->AVG->MIN
* 2. aggColList: List of aggregated columns
* a->b->a
* 3. isHavingList: Is aggregation operation part of HAVING clause or not?
* 0->0->1
*/
typedef struct MongoRelQualInfo
{
PlannerInfo *root; /* global planner state */
RelOptInfo *foreignRel; /* the foreign relation we are planning for */
Relids outerRelids; /* set of base relids of outer relation */
List *colNameList;
List *colNumList;
List *rtiList;
List *isOuterList;
struct HTAB *exprColHash;
/* For upper-relation */
bool is_agg_column; /* is column aggregated or not? */
bool is_having; /* is it part of HAVING clause or not? */
List *aggTypeList;
List *aggColList;
List *isHavingList;
} MongoRelQualInfo;
typedef struct ColumnHashKey
{
int varno;
int varattno;
} ColumnHashKey;
/*
* Indexes for relation type. The RelOptKind could be used but there is no
* kind called UPPER_JOIN_REL. The UPPER_JOIN_REL is nothing but UPPER_REL but
* for our use case, we are differentiating these two types.
*/
typedef enum MongoFdwRelType
{
BASE_REL,
JOIN_REL,
UPPER_REL,
UPPER_JOIN_REL
} MongoFdwRelType;
/* options.c */
extern MongoFdwOptions *mongo_get_options(Oid foreignTableId);
extern void mongo_free_options(MongoFdwOptions *options);
extern StringInfo mongo_option_names_string(Oid currentContextId);
/* connection.c */
MONGO_CONN *mongo_get_connection(ForeignServer *server,
UserMapping *user,
MongoFdwOptions *opt);
extern void mongo_cleanup_connection(void);
extern void mongo_release_connection(MONGO_CONN *conn);
/* Function declarations related to creating the mongo query */
extern BSON *mongo_query_document(ForeignScanState *scanStateNode);
extern List *mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel,
List *scan_var_list, List **colNameList,
List **colIsInnerList);
extern bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel,
Expr *expression, bool is_having_cond);
extern bool mongo_is_foreign_param(PlannerInfo *root, RelOptInfo *baserel,
Expr *expr);
/* Function declarations for foreign data wrapper */
extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS);
extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS);
/* deparse.c headers */
extern void mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info);
extern const char *mongo_get_jointype_name(JoinType jointype);
extern EquivalenceMember *mongo_find_em_for_rel(PlannerInfo *root,
EquivalenceClass *ec,
RelOptInfo *rel);
extern bool mongo_is_builtin(Oid oid);
extern bool mongo_is_default_sort_operator(EquivalenceMember *em,
PathKey *pathkey);
extern bool mongo_is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel,
PathKey *pathkey);
#endif /* MONGO_FDW_H */
mongo_fdw-REL-5_5_3/mongo_query.c 0000664 0000000 0000000 00000142740 15066665201 0017014 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_query.c
* FDW query handling for mongo_fdw
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_query.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "mongo_wrapper.h"
#include
#include
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/heap.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_operator.h"
#include "common/hashfn.h"
#include "mongoc.h"
#include "mongo_query.h"
#include "optimizer/optimizer.h"
#include "parser/parsetree.h"
#include "utils/rel.h"
#include "utils/syscache.h"
/*
* Global context for foreign_expr_walker's search of an expression tree.
*/
typedef struct foreign_glob_cxt
{
PlannerInfo *root; /* global planner state */
RelOptInfo *foreignrel; /* the foreign relation we are planning for */
Relids relids; /* relids of base relations in the underlying
* scan */
bool is_having_cond; /* "true" for HAVING clause condition */
} foreign_glob_cxt;
/*
* Local (per-tree-level) context for foreign_expr_walker's search.
* This is concerned with identifying collations used in the expression.
*/
typedef enum
{
FDW_COLLATE_NONE, /* expression is of a noncollatable type */
FDW_COLLATE_SAFE, /* collation derives from a foreign Var */
FDW_COLLATE_UNSAFE /* collation derives from something else */
} FDWCollateState;
typedef struct foreign_loc_cxt
{
Oid collation; /* OID of current collation, if any */
FDWCollateState state; /* state of current collation choice */
} foreign_loc_cxt;
/* Local functions forward declarations */
static bool foreign_expr_walker(Node *node,
foreign_glob_cxt *glob_cxt,
foreign_loc_cxt *outer_cxt);
static List *prepare_var_list_for_baserel(Oid relid, Index varno,
Bitmapset *attrs_used);
static HTAB *column_info_hash(List *colname_list, List *colnum_list,
List *rti_list, List *isouter_list);
static void mongo_prepare_pipeline(List *clause, BSON *inner_pipeline,
pipeline_cxt *context);
static void mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc,
pipeline_cxt *context);
#if PG_VERSION_NUM >= 160000
static List *mongo_append_unique_var(List *varlist, Var *var);
#endif
/*
* mongo_query_document
* Takes in the applicable operator expressions for relation, the join
* clauses for join relation, and grouping targets for upper relation and
* converts these expressions, join clauses, and grouping targets into
* equivalent queries in MongoDB.
*
* For join clauses, transforms simple comparison expressions along with a
* comparison between two vars and nested operator expressions as well.
*
* Example: Consider the following two foreign tables:
* t1(_id NAME, age INT, name VARCHAR)
* t2(_id NAME, old INT, alias VARCHAR)
*
* SQL query:
* SELECT * FROM t1 LEFT JOIN t2 ON (t1.age = t2.old)
* WHERE (t1.age % 2) = 1
* ORDER BY t1.age ASC NULLS FIRST;
* Equivalent MongoDB query:
*
* db.t1.aggregate([
* {
* "$lookup":
* {
* "from": "t2",
* "let": { "v_age": "$age" },
* "pipeline": [
* {
* "$match":
* {
* "$expr":
* {
* "$and": [
* { "$eq": [ "$$v_age", "$old" ] }
* { "$ne": [ "$$v_age", null ] },
* { "$ne": [ "$old", null ] },
* ]
* }
* }
* }
* ],
* "as": "Join_Result"
* }
* },
* { "$match" :
* {
* "$expr" :
* { "$and" : [
* { "$eq" : [ { "$mod" : [ "$age", 2] }, 1]},
* { "$ne" : [ "$age", null ] }
* ]
* }
* }
* }
* {
* "$unwind":
* {
* "path": "$Join_Result",
* "preserveNullAndEmptyArrays": true
* }
* },
* { "$sort": { "age" : 1 } }
* ])
*
* Any MongoDB query would have the following three main arrays:
* 1. Root pipeline array (first square bracket):
* This has three elements called $lookup, $unwind, and $match stages.
* 2. Inner pipeline array (starting with "pipeline" keyword above):
* It has one element that is $match.
* 3. "$and" expression inside inner pipeline:
* These elements depend on the join clauses available.
*
* The outer $match stage (2nd element of root pipeline array) represents
* remote_exprs, and $match inside $lookup stage represents the join clauses.
*
* For grouping target, add $group stage on the base relation or join relation.
* The HAVING clause is nothing but a post $match stage.
*
* Example: Consider above table t1:
*
* SQL query:
* SELECT name, SUM(age) FROM t1 GROUP BY name HAVING MIN(name) = 'xyz'
* ORDER BY name DESC NULLS LAST;
*
* Equivalent MongoDB query:
*
* db.t1.aggregate([
* {
* "$group":
* {
* "_id": {"name": "$name"},
* "v_agg0": {"$sum": "$age"},
* "v_having": {"$min": "$name"}
* }
* },
* {
* "$match": {"v_having": "xyz"}
* }
* { "$sort": { "name" : -1 } }
* ])
*
* For ORDER BY, add $sort stage on the base relation or join or grouping
* relation as shown in the above examples of join and grouping relations.
*/
BSON *
mongo_query_document(ForeignScanState *scanStateNode)
{
ForeignScan *fsplan = (ForeignScan *) scanStateNode->ss.ps.plan;
BSON *queryDocument = bsonCreate();
List *PrivateList = fsplan->fdw_private;
List *opExpressionList = list_nth(PrivateList,
mongoFdwPrivateRemoteExprList);
MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state;
BSON root_pipeline;
BSON match_stage;
int root_index = 0;
List *joinclauses = NIL;
List *colnum_list;
List *colname_list = NIL;
List *isouter_list = NIL;
List *rti_list;
List *pathkey_list;
List *is_ascsort_list;
char *inner_relname = NULL;
char *outer_relname = NULL;
HTAB *columnInfoHash;
int jointype = 0;
bool has_limit;
/* Retrieve data passed by planning phase */
colname_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseColNameList);
colnum_list = list_nth(PrivateList, mongoFdwPrivareJoinClauseColNumList);
rti_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseRtiList);
isouter_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseIsOuterList);
#ifdef USE_ASSERT_CHECKING
{
/* Length should be same for all lists of column information */
int natts = list_length(colname_list);
Assert(natts == list_length(colnum_list) &&
natts == list_length(rti_list) &&
natts == list_length(isouter_list));
}
#endif
/* Store information in the hash-table */
columnInfoHash = column_info_hash(colname_list, colnum_list, rti_list,
isouter_list);
/* Retrieve information related to ORDER BY clause */
pathkey_list = list_nth(PrivateList, mongoFdwPrivatePathKeyList);
is_ascsort_list = list_nth(PrivateList, mongoFdwPrivateIsAscSortList);
/* Retrieve information related to LIMIT/OFFSET clause */
has_limit = intVal(list_nth(PrivateList, mongoFdwPrivateHasLimitClause));
if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL)
{
List *innerouter_relname;
joinclauses = list_nth(PrivateList, mongoFdwPrivateJoinClauseList);
if (joinclauses)
jointype = intVal(list_nth(PrivateList, mongoFdwPrivateJoinType));
innerouter_relname = list_nth(PrivateList,
mongoFdwPrivateJoinInnerOuterRelName);
inner_relname = strVal(list_nth(innerouter_relname, 0));
outer_relname = strVal(list_nth(innerouter_relname, 1));
}
/* Prepare array of stages */
bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline);
if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL)
{
BSON inner_pipeline;
BSON lookup_object;
BSON lookup;
BSON let_exprs;
BSON unwind_stage;
BSON unwind;
BSON *inner_pipeline_doc = bsonCreate();
ListCell *cell1;
ListCell *cell2;
/* $lookup stage. This is to perform JOIN */
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&lookup_object);
bsonAppendStartObject(&lookup_object, "$lookup", &lookup);
bsonAppendUTF8(&lookup, "from", inner_relname);
/*
* Start "let" operator: Specifies variables to use in the pipeline
* stages. To access columns of outer relation, those need to be
* defined in terms of a variable using "let".
*/
bsonAppendStartObject(&lookup, "let", &let_exprs);
forboth(cell1, colname_list, cell2, isouter_list)
{
char *colname = strVal(lfirst(cell1));
bool is_outer = lfirst_int(cell2);
/*
* Ignore column name with "*" because this is not the name of any
* particular column and is not allowed in the let operator. While
* deparsing the COUNT(*) aggregation operation, this column name
* is added to lists to maintain the length of column information.
*/
if (is_outer && strcmp(colname, "*") != 0)
{
char *varname = psprintf("%s",
get_varname_for_outer_col(colname));
char *field = psprintf("$%s", colname);
bsonAppendUTF8(&let_exprs, varname, field);
}
}
bsonAppendFinishObject(&lookup, &let_exprs); /* End "let" */
/* Form inner pipeline required in $lookup stage to execute $match */
bsonAppendStartArray(inner_pipeline_doc, "pipeline", &inner_pipeline);
if (joinclauses)
{
pipeline_cxt context;
context.colInfoHash = columnInfoHash;
context.isBoolExpr = false;
context.isJoinClause = true;
context.scanStateNode = scanStateNode;
/* Form equivalent join qual clauses in MongoDB */
mongo_prepare_pipeline(joinclauses, &inner_pipeline, &context);
bsonAppendFinishArray(inner_pipeline_doc, &inner_pipeline);
}
/* Append inner pipeline to $lookup stage */
bson_append_array(&lookup, "pipeline", (int) strlen("pipeline"),
&inner_pipeline);
bsonAppendUTF8(&lookup, "as", "Join_Result");
bsonAppendFinishObject(&lookup_object, &lookup);
bsonAppendFinishObject(&root_pipeline, &lookup_object);
/*
* $unwind stage. This deconstructs an array field from the input
* documents to output a document for each element.
*/
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&unwind_stage);
bsonAppendStartObject(&unwind_stage, "$unwind", &unwind);
bsonAppendUTF8(&unwind, "path", "$Join_Result");
if (jointype == JOIN_INNER)
bsonAppendBool(&unwind, "preserveNullAndEmptyArrays", false);
else
bsonAppendBool(&unwind, "preserveNullAndEmptyArrays", true);
bsonAppendFinishObject(&unwind_stage, &unwind);
bsonAppendFinishObject(&root_pipeline, &unwind_stage);
fmstate->outerRelName = outer_relname;
}
/*
* Add filter into query pipeline if available. These are remote_exprs
* i.e. clauses available in WHERE and those are push-able to the remote
* side.
*/
if (opExpressionList)
{
pipeline_cxt context;
context.colInfoHash = columnInfoHash;
context.isBoolExpr = false;
context.isJoinClause = false;
context.scanStateNode = scanStateNode;
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&match_stage);
/* Form equivalent WHERE clauses in MongoDB */
mongo_prepare_pipeline(opExpressionList, &match_stage, &context);
bsonAppendFinishObject(&root_pipeline, &match_stage);
}
/* Add $group stage for upper relation */
if (fmstate->relType == UPPER_JOIN_REL || fmstate->relType == UPPER_REL)
{
List *func_list;
List *agg_col_list;
List *groupby_col_list;
List *having_expr;
BSON groupby_expr;
BSON group_stage;
BSON group_expr;
BSON group;
ListCell *cell1;
ListCell *cell2;
ListCell *cell3;
List *is_having_list;
Index aggIndex = 0;
func_list = list_nth(PrivateList, mongoFdwPrivateAggType);
agg_col_list = list_nth(PrivateList, mongoFdwPrivateAggColList);
groupby_col_list = list_nth(PrivateList, mongoFdwPrivateGroupByColList);
having_expr = list_nth(PrivateList, mongoFdwPrivateHavingExpr);
is_having_list = list_nth(PrivateList, mongoFdwPrivateIsHavingList);
/* $group stage. */
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&group_stage);
bsonAppendStartObject(&group_stage, "$group", &group);
/*
* Add columns from the GROUP BY clause in the "_id" field of $group
* stage. In case of aggregation on join result, a column of the
* inner table needs to be accessed by prefixing it using
* "Join_Result", which is been hardcoded.
*/
if (groupby_col_list)
{
ListCell *columnCell;
bsonAppendStartObject(&group, "_id", &groupby_expr);
foreach(columnCell, groupby_col_list)
{
Var *column = (Var *) lfirst(columnCell);
bool found = false;
ColInfoHashKey key;
ColInfoHashEntry *columnInfo;
key.varNo = column->varno;
key.varAttno = column->varattno;
columnInfo = (ColInfoHashEntry *) hash_search(columnInfoHash,
(void *) &key,
HASH_FIND,
&found);
if (found)
{
if (columnInfo->isOuter)
bsonAppendUTF8(&groupby_expr, columnInfo->colName,
psprintf("$%s", columnInfo->colName));
else
bsonAppendUTF8(&groupby_expr, columnInfo->colName,
psprintf("$Join_Result.%s",
columnInfo->colName));
}
}
bsonAppendFinishObject(&group, &groupby_expr); /* End "_id" */
}
else
{
/* If no GROUP BY clause then append null to the _id. */
bsonAppendNull(&group, "_id");
}
/* Add grouping operation */
forthree(cell1, func_list, cell2, agg_col_list, cell3, is_having_list)
{
ColInfoHashKey key;
ColInfoHashEntry *columnInfo;
bool found = false;
char *func_name = strVal(lfirst(cell1));
Var *column = (Var *) lfirst(cell2);
bool is_having_agg = lfirst_int(cell3);
if (is_having_agg)
bsonAppendStartObject(&group, "v_having", &group_expr);
else
bsonAppendStartObject(&group,
psprintf("AGG_RESULT_KEY%d",
aggIndex++),
&group_expr);
key.varNo = column->varno;
key.varAttno = column->varattno;
columnInfo = (ColInfoHashEntry *) hash_search(columnInfoHash,
(void *) &key,
HASH_FIND,
&found);
/*
* The aggregation operation in MongoDB other than COUNT has the
* same name as PostgreSQL but COUNT needs to be performed using
* the $sum operator because MongoDB doesn't have a direct $count
* operator for the currently supported version (i.e. v4.4).
*
* There is no syntax in MongoDB to provide column names for COUNT
* operation but for other supported operations, we can do so.
*
* In case of aggregation over the join, the resulted columns of
* inner relation need to be accessed by prefixing it with
* "Join_Result".
*/
if (found && strcmp(func_name, "count") != 0)
{
if (columnInfo->isOuter)
bsonAppendUTF8(&group_expr, psprintf("$%s", func_name),
psprintf("$%s", columnInfo->colName));
else
bsonAppendUTF8(&group_expr, psprintf("$%s", func_name),
psprintf("$Join_Result.%s",
columnInfo->colName));
}
else
{
/*
* The COUNT(*) in PostgreSQL is equivalent to {$sum: 1} in
* the MongoDB.
*/
bsonAppendInt32(&group_expr, psprintf("$%s", "sum"), 1);
}
bsonAppendFinishObject(&group, &group_expr);
}
bsonAppendFinishObject(&group_stage, &group);
bsonAppendFinishObject(&root_pipeline, &group_stage);
/* Add HAVING operation */
if (having_expr)
{
pipeline_cxt context;
context.colInfoHash = columnInfoHash;
context.isBoolExpr = false;
context.isJoinClause = false;
context.scanStateNode = scanStateNode;
/* $match stage. Add a filter for the HAVING clause */
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&match_stage);
/* Form equivalent HAVING clauses in MongoDB */
mongo_prepare_pipeline(having_expr, &match_stage, &context);
bsonAppendFinishObject(&root_pipeline, &match_stage);
}
}
/* Add sort stage */
if (pathkey_list)
{
BSON sort_stage;
BSON sort;
ListCell *cell1;
ListCell *cell2;
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&sort_stage);
bsonAppendStartObject(&sort_stage, "$sort", &sort);
forboth(cell1, pathkey_list, cell2, is_ascsort_list)
{
Var *column = (Var *) lfirst(cell1);
int is_asc_sort = lfirst_int(cell2);
bool found = false;
ColInfoHashKey key;
ColInfoHashEntry *columnInfo;
/* Find column name */
key.varNo = column->varno;
key.varAttno = column->varattno;
columnInfo = (ColInfoHashEntry *) hash_search(columnInfoHash,
(void *) &key,
HASH_FIND,
&found);
if (found)
{
/*
* In the case of upper rel, access the column by prefixing it
* with "_id". To access the column of the inner relation in
* the join operation, use the prefix "Join_Result" because
* direct access is not possible. However, columns of the
* simple relation and outer relation of the join can be
* accessed directly.
*/
if (fmstate->relType == UPPER_JOIN_REL ||
fmstate->relType == UPPER_REL)
bsonAppendInt32(&sort,
psprintf("_id.%s", columnInfo->colName),
is_asc_sort);
else if (!columnInfo->isOuter && fmstate->relType != BASE_REL)
bsonAppendInt32(&sort,
psprintf("Join_Result.%s",
columnInfo->colName),
is_asc_sort);
else
bsonAppendInt32(&sort, columnInfo->colName, is_asc_sort);
}
}
bsonAppendFinishObject(&sort_stage, &sort);
bsonAppendFinishObject(&root_pipeline, &sort_stage); /* End sort */
}
/* Add LIMIT/SKIP stage */
if (has_limit)
{
int64 limit_value;
int64 offset_value;
/*
* Add skip stage for OFFSET clause. However, don't add the same if
* either offset is not provided or the offset value is zero.
*/
offset_value = (int64) intVal(list_nth(PrivateList,
mongoFdwPrivateLimitOffsetList));
if (offset_value != -1 && offset_value != 0)
{
BSON skip_stage;
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&skip_stage);
bsonAppendInt64(&skip_stage, "$skip", offset_value);
bsonAppendFinishObject(&root_pipeline, &skip_stage);
}
/*
* Add limit stage for LIMIT clause. However, don't add the same if
* the limit is not provided.
*/
limit_value = (int64) intVal(list_nth(PrivateList,
mongoFdwPrivateLimitCountList));
if (limit_value != -1)
{
BSON limit_stage;
bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++),
&limit_stage);
bsonAppendInt64(&limit_stage, "$limit", limit_value);
bsonAppendFinishObject(&root_pipeline, &limit_stage);
}
}
bsonAppendFinishArray(queryDocument, &root_pipeline);
return queryDocument;
}
/*
* mongo_operator_name
* Takes in the given PostgreSQL comparison operator name, and returns its
* equivalent in MongoDB.
*/
char *
mongo_operator_name(const char *operatorName)
{
const char *mongoOperatorName = NULL;
const int32 nameCount = 14;
static const char *nameMappings[][2] = {{"<", "$lt"},
{">", "$gt"},
{"<=", "$lte"},
{">=", "$gte"},
{"<>", "$ne"},
{"=", "$eq"},
{"+", "$add"},
{"-", "$subtract"},
{"*", "$multiply"},
{"/", "$divide"},
{"%", "$mod"},
{"^", "$pow"},
{"|/", "$sqrt"},
{"@", "$abs"}};
int32 nameIndex;
for (nameIndex = 0; nameIndex < nameCount; nameIndex++)
{
const char *pgOperatorName = nameMappings[nameIndex][0];
if (strncmp(pgOperatorName, operatorName, NAMEDATALEN) == 0)
{
mongoOperatorName = nameMappings[nameIndex][1];
break;
}
}
return (char *) mongoOperatorName;
}
void
append_param_value(BSON *queryDocument, const char *keyName, Param *paramNode,
ForeignScanState *scanStateNode)
{
ExprState *param_expr;
Datum param_value;
bool isNull;
ExprContext *econtext;
if (scanStateNode == NULL)
return;
econtext = scanStateNode->ss.ps.ps_ExprContext;
/* Prepare for parameter expression evaluation */
param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *) scanStateNode);
/* Evaluate the parameter expression */
param_value = ExecEvalExpr(param_expr, econtext, &isNull);
append_mongo_value(queryDocument, keyName, param_value, isNull,
paramNode->paramtype);
}
/*
* append_constant_value
* Appends to the query document the key name and constant value.
*
* The function translates the constant value from its PostgreSQL type
* to its MongoDB equivalent.
*/
void
append_constant_value(BSON *queryDocument, const char *keyName, Const *constant)
{
if (constant->constisnull)
{
bsonAppendNull(queryDocument, keyName);
return;
}
append_mongo_value(queryDocument, keyName, constant->constvalue, false,
constant->consttype);
}
bool
append_mongo_value(BSON *queryDocument, const char *keyName, Datum value,
bool isnull, Oid id)
{
bool status = false;
if (isnull)
{
status = bsonAppendNull(queryDocument, keyName);
return status;
}
switch (id)
{
case INT2OID:
{
int16 valueInt = DatumGetInt16(value);
status = bsonAppendInt32(queryDocument, keyName,
(int) valueInt);
}
break;
case INT4OID:
{
int32 valueInt = DatumGetInt32(value);
status = bsonAppendInt32(queryDocument, keyName, valueInt);
}
break;
case INT8OID:
{
int64 valueLong = DatumGetInt64(value);
status = bsonAppendInt64(queryDocument, keyName, valueLong);
}
break;
case FLOAT4OID:
{
float4 valueFloat = DatumGetFloat4(value);
status = bsonAppendDouble(queryDocument, keyName,
(double) valueFloat);
}
break;
case FLOAT8OID:
{
float8 valueFloat = DatumGetFloat8(value);
status = bsonAppendDouble(queryDocument, keyName, valueFloat);
}
break;
case NUMERICOID:
{
Datum valueDatum = DirectFunctionCall1(numeric_float8,
value);
float8 valueFloat = DatumGetFloat8(valueDatum);
status = bsonAppendDouble(queryDocument, keyName, valueFloat);
}
break;
case BOOLOID:
{
bool valueBool = DatumGetBool(value);
status = bsonAppendBool(queryDocument, keyName,
(int) valueBool);
}
break;
case BPCHAROID:
case VARCHAROID:
case TEXTOID:
{
char *outputString;
Oid outputFunctionId;
bool typeVarLength;
getTypeOutputInfo(id, &outputFunctionId, &typeVarLength);
outputString = OidOutputFunctionCall(outputFunctionId, value);
status = bsonAppendUTF8(queryDocument, keyName, outputString);
}
break;
case BYTEAOID:
{
int len;
char *data;
char *result = DatumGetPointer(value);
if (VARATT_IS_1B(result))
{
len = VARSIZE_1B(result) - VARHDRSZ_SHORT;
data = VARDATA_1B(result);
}
else
{
len = VARSIZE_4B(result) - VARHDRSZ;
data = VARDATA_4B(result);
}
if (strcmp(keyName, "_id") == 0)
{
bson_oid_t oid;
bson_oid_init_from_data(&oid, (const uint8_t *) data);
status = bsonAppendOid(queryDocument, keyName, &oid);
}
else
status = bsonAppendBinary(queryDocument, keyName, data,
len);
}
break;
case NAMEOID:
{
char *outputString;
Oid outputFunctionId;
bool typeVarLength;
bson_oid_t bsonObjectId;
memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes));
getTypeOutputInfo(id, &outputFunctionId, &typeVarLength);
outputString = OidOutputFunctionCall(outputFunctionId, value);
bsonOidFromString(&bsonObjectId, outputString);
status = bsonAppendOid(queryDocument, keyName, &bsonObjectId);
}
break;
case DATEOID:
{
Datum valueDatum = DirectFunctionCall1(date_timestamp,
value);
Timestamp valueTimestamp = DatumGetTimestamp(valueDatum);
int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS;
int64 valueMilliSecs = valueMicroSecs / 1000;
status = bsonAppendDate(queryDocument, keyName,
valueMilliSecs);
}
break;
case TIMESTAMPOID:
case TIMESTAMPTZOID:
{
Timestamp valueTimestamp = DatumGetTimestamp(value);
int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS;
int64 valueMilliSecs = valueMicroSecs / 1000;
status = bsonAppendDate(queryDocument, keyName,
valueMilliSecs);
}
break;
case NUMERICARRAY_OID:
{
ArrayType *array;
Oid elmtype;
int16 elmlen;
bool elmbyval;
char elmalign;
int num_elems;
Datum *elem_values;
bool *elem_nulls;
int i;
BSON childDocument;
array = DatumGetArrayTypeP(value);
elmtype = ARR_ELEMTYPE(array);
get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign,
&elem_values, &elem_nulls, &num_elems);
bsonAppendStartArray(queryDocument, keyName, &childDocument);
for (i = 0; i < num_elems; i++)
{
Datum valueDatum;
float8 valueFloat;
if (elem_nulls[i])
continue;
valueDatum = DirectFunctionCall1(numeric_float8,
elem_values[i]);
valueFloat = DatumGetFloat8(valueDatum);
status = bsonAppendDouble(&childDocument, keyName,
valueFloat);
}
bsonAppendFinishArray(queryDocument, &childDocument);
pfree(elem_values);
pfree(elem_nulls);
}
break;
case TEXTARRAYOID:
{
ArrayType *array;
Oid elmtype;
int16 elmlen;
bool elmbyval;
char elmalign;
int num_elems;
Datum *elem_values;
bool *elem_nulls;
int i;
BSON childDocument;
array = DatumGetArrayTypeP(value);
elmtype = ARR_ELEMTYPE(array);
get_typlenbyvalalign(elmtype, &elmlen, &elmbyval, &elmalign);
deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign,
&elem_values, &elem_nulls, &num_elems);
bsonAppendStartArray(queryDocument, keyName, &childDocument);
for (i = 0; i < num_elems; i++)
{
char *valueString;
Oid outputFunctionId;
bool typeVarLength;
if (elem_nulls[i])
continue;
getTypeOutputInfo(TEXTOID, &outputFunctionId,
&typeVarLength);
valueString = OidOutputFunctionCall(outputFunctionId,
elem_values[i]);
status = bsonAppendUTF8(&childDocument, keyName,
valueString);
}
bsonAppendFinishArray(queryDocument, &childDocument);
pfree(elem_values);
pfree(elem_nulls);
}
break;
case JSONOID:
{
char *outputString;
Oid outputFunctionId;
struct json_object *o;
bool typeVarLength;
getTypeOutputInfo(id, &outputFunctionId, &typeVarLength);
outputString = OidOutputFunctionCall(outputFunctionId, value);
o = jsonTokenerPrase(outputString);
if (o == NULL)
{
elog(WARNING, "cannot parse the document");
status = 0;
break;
}
status = jsonToBsonAppendElement(queryDocument, keyName, o);
}
break;
default:
/*
* We currently error out on other data types. Some types such as
* byte arrays are easy to add, but they need testing.
*
* Other types such as money or inet, do not have equivalents in
* MongoDB.
*/
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("cannot convert constant value to BSON value"),
errhint("Constant value data type: %u", id)));
break;
}
return status;
}
/*
* mongo_get_column_list
* Process scan_var_list to find all columns needed for query execution
* and return them.
*
* Also, form two separate lists:
* 1. column_name_list: column names of needed columns.
* 2. is_inner_column_list: column is of inner relation or not.
*/
List *
mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel,
List *scan_var_list, List **column_name_list,
List **is_inner_column_list)
{
List *columnList = NIL;
ListCell *lc;
RelOptInfo *scanrel;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private;
MongoFdwRelationInfo *ofpinfo = NULL;
scanrel = IS_UPPER_REL(foreignrel) ? fpinfo->outerrel : foreignrel;
if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel))
ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private;
foreach(lc, scan_var_list)
{
Var *var = (Var *) lfirst(lc);
RangeTblEntry *rte = planner_rt_fetch(var->varno, root);
int is_innerrel = false;
/*
* Add aggregation target also in the needed column list. This would
* be handled in the function column_mapping_hash.
*/
if (IsA(var, Aggref))
{
#if PG_VERSION_NUM >= 160000
columnList = mongo_append_unique_var(columnList, var);
#else
columnList = list_append_unique(columnList, var);
#endif
continue;
}
if (!IsA(var, Var))
continue;
/* Var belongs to foreign table? */
if (!bms_is_member(var->varno, scanrel->relids))
continue;
/* Is whole-row reference requested? */
if (var->varattno == 0)
{
List *wr_var_list;
Bitmapset *attrs_used;
#if PG_VERSION_NUM >= 160000
ListCell *cell;
#endif
Assert(OidIsValid(rte->relid));
/*
* Get list of Var nodes for all undropped attributes of the base
* relation.
*/
attrs_used = bms_make_singleton(0 -
FirstLowInvalidHeapAttributeNumber);
wr_var_list = prepare_var_list_for_baserel(rte->relid, var->varno,
attrs_used);
#if PG_VERSION_NUM >= 160000
foreach(cell, wr_var_list)
{
Var *tlvar = (Var *) lfirst(cell);
columnList = mongo_append_unique_var(columnList, tlvar);
}
#else
columnList = list_concat_unique(columnList, wr_var_list);
#endif
bms_free(attrs_used);
}
else
#if PG_VERSION_NUM >= 160000
columnList = mongo_append_unique_var(columnList, var);
#else
columnList = list_append_unique(columnList, var);
#endif
if (IS_JOIN_REL(foreignrel) ||
(IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel)))
{
char *columnName;
columnName = get_attname(rte->relid, var->varattno, false);
*column_name_list = lappend(*column_name_list,
makeString(columnName));
if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel) &&
bms_is_member(var->varno, ofpinfo->innerrel->relids))
is_innerrel = true;
else if (IS_JOIN_REL(foreignrel) &&
bms_is_member(var->varno, fpinfo->innerrel->relids))
is_innerrel = true;
*is_inner_column_list = lappend_int(*is_inner_column_list,
is_innerrel);
}
}
return columnList;
}
/*
* Check if expression is safe to execute remotely, and return true if so.
*
* In addition, *outer_cxt is updated with collation information.
*
* We must check that the expression contains only node types we can deparse,
* that all types/operators are safe to send (which we approximate
* as being built-in), and that all collations used in the expression derive
* from Vars of the foreign table.
*
* For WHERE as well as JOIN clauses, in the case of operator expression, we do
* support arithmetic (=, <, >, <=, >=, <>, +, -, *, /, %, ^, @ and |/)
* operators. Also, both operands of the binary operator can be a column. If
* the expression is a tree, we do recurse into it. Supports Boolean
* expression as well.
*/
static bool
foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt,
foreign_loc_cxt *outer_cxt)
{
foreign_loc_cxt inner_cxt;
Oid collation;
FDWCollateState state;
/* Need do nothing for empty subexpressions */
if (node == NULL)
return true;
/* Set up inner_cxt for possible recursion to child nodes */
inner_cxt.collation = InvalidOid;
inner_cxt.state = FDW_COLLATE_NONE;
switch (nodeTag(node))
{
case T_Var:
{
Var *var = (Var *) node;
/*
* If the Var is from the foreign table, we consider its
* collation (if any) safe to use. If it is from another
* table, don't push it down.
*/
if (bms_is_member(var->varno, glob_cxt->relids) &&
var->varlevelsup == 0)
{
/* Var belongs to foreign table */
collation = var->varcollid;
state = OidIsValid(collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
}
else
{
/*
* Var belongs to some other table. Unlike postgres_fdw,
* can't be treated like Param because MongoDB doesn't
* have corresponding syntax to represent it in the query
* pipeline.
*/
return false;
}
}
break;
case T_Const:
{
Const *c = (Const *) node;
/*
* We don't push down operators where the constant is an
* array, since conditional operators for arrays in MongoDB
* aren't properly defined.
*/
if (OidIsValid(get_element_type(c->consttype)))
return false;
/*
* If the constant has nondefault collation, either it's of a
* non-builtin type, or it reflects folding of a CollateExpr.
* It's unsafe to send to the remote unless it's used in a
* non-collation-sensitive context.
*/
collation = c->constcollid;
if (collation == InvalidOid ||
collation == DEFAULT_COLLATION_OID)
state = FDW_COLLATE_NONE;
else
state = FDW_COLLATE_UNSAFE;
}
break;
case T_Param:
{
Param *p = (Param *) node;
/*
* Bail out on planner internal params. We could perhaps pass
* them to the remote server as regular params, but we don't
* have the machinery to do that at the moment.
*/
if (p->paramkind != PARAM_EXTERN)
return false;
/*
* Collation rule is same as for Consts and non-foreign Vars.
*/
collation = p->paramcollid;
if (collation == InvalidOid ||
collation == DEFAULT_COLLATION_OID)
state = FDW_COLLATE_NONE;
else
state = FDW_COLLATE_UNSAFE;
}
break;
case T_OpExpr:
{
OpExpr *oe = (OpExpr *) node;
char *oname = get_opname(oe->opno);
/* Don't support operator expression in grouping targets */
if (IS_UPPER_REL(glob_cxt->foreignrel) &&
!glob_cxt->is_having_cond)
return false;
/*
* We support =, <, >, <=, >=, <>, +, -, *, /, %, ^, |/, and @
* operators for joinclause of join relation.
*/
if (!(strncmp(oname, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) &&
(mongo_operator_name(oname) == NULL))
return false;
/*
* Recurse to input subexpressions.
*
* We support same operators as joinclause for WHERE
* conditions of simple as well as join relation.
*/
if (!foreign_expr_walker((Node *) oe->args, glob_cxt,
&inner_cxt))
return false;
/*
* If operator's input collation is not derived from a foreign
* Var, it can't be sent to remote.
*/
if (oe->inputcollid == InvalidOid)
/* OK, inputs are all noncollatable */ ;
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
oe->inputcollid != inner_cxt.collation)
return false;
/* Result-collation handling */
collation = oe->opcollid;
if (collation == InvalidOid)
state = FDW_COLLATE_NONE;
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
collation == inner_cxt.collation)
state = FDW_COLLATE_SAFE;
else if (collation == DEFAULT_COLLATION_OID)
state = FDW_COLLATE_NONE;
else
state = FDW_COLLATE_UNSAFE;
}
break;
case T_RelabelType:
{
RelabelType *r = (RelabelType *) node;
/*
* Recurse to input subexpression.
*/
if (!foreign_expr_walker((Node *) r->arg,
glob_cxt, &inner_cxt))
return false;
/*
* RelabelType must not introduce a collation not derived from
* an input foreign Var.
*/
collation = r->resultcollid;
if (collation == InvalidOid)
state = FDW_COLLATE_NONE;
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
collation == inner_cxt.collation)
state = FDW_COLLATE_SAFE;
else if (collation == DEFAULT_COLLATION_OID)
state = FDW_COLLATE_NONE;
else
state = FDW_COLLATE_UNSAFE;
}
break;
case T_List:
{
List *l = (List *) node;
ListCell *lc;
/*
* Recurse to component subexpressions.
*
* For simple relation, if the comparison is between two
* columns of the same table, then we don't push down because
* building corresponding MongoDB query is not possible with
* the cirrent MongoC driver.
*/
foreach(lc, l)
{
if ((!foreign_expr_walker((Node *) lfirst(lc),
glob_cxt, &inner_cxt)))
return false;
}
/*
* When processing a list, collation state just bubbles up
* from the list elements.
*/
collation = inner_cxt.collation;
state = inner_cxt.state;
}
break;
case T_BoolExpr:
{
BoolExpr *b = (BoolExpr *) node;
/*
* Recurse to input sub-expressions.
*/
if (!foreign_expr_walker((Node *) b->args,
glob_cxt, &inner_cxt))
return false;
/* Output is always boolean and so noncollatable. */
collation = InvalidOid;
state = FDW_COLLATE_NONE;
}
break;
case T_Aggref:
{
Aggref *agg = (Aggref *) node;
ListCell *lc;
const char *func_name = get_func_name(agg->aggfnoid);
/* Not safe to pushdown when not in a grouping context */
if (!IS_UPPER_REL(glob_cxt->foreignrel))
return false;
/* Only non-split aggregates are pushable. */
if (agg->aggsplit != AGGSPLIT_SIMPLE)
return false;
/*
* Aggregates with the order, FILTER, VARIADIC, and DISTINCT
* are not supported on MongoDB.
*/
if (agg->aggorder || agg->aggfilter || agg->aggvariadic ||
agg->aggdistinct)
return false;
if (!(strcmp(func_name, "min") == 0 ||
strcmp(func_name, "max") == 0 ||
strcmp(func_name, "sum") == 0 ||
strcmp(func_name, "avg") == 0 ||
strcmp(func_name, "count") == 0))
return false;
/*
* Don't push down when the count is on the column. This
* restriction is due to the unavailability of syntax in the
* MongoDB to provide a count of the particular column.
*/
if (!strcmp(func_name, "count") && agg->args)
return false;
/*
* Recurse to input args. aggdirectargs, aggorder, and
* aggdistinct are all present in args, so no need to check
* their shippability explicitly.
*/
foreach(lc, agg->args)
{
Node *n = (Node *) lfirst(lc);
/* If TargetEntry, extract the expression from it. */
if (IsA(n, TargetEntry))
{
TargetEntry *tle = (TargetEntry *) n;
n = (Node *) tle->expr;
}
if (!IsA(n, Var) || !foreign_expr_walker(n, glob_cxt,
&inner_cxt))
return false;
}
/*
* If aggregate's input collation is not derived from a
* foreign Var, it can't be sent to remote.
*/
if (agg->inputcollid == InvalidOid)
/* OK, inputs are all noncollatable */ ;
else if (inner_cxt.state != FDW_COLLATE_SAFE ||
agg->inputcollid != inner_cxt.collation)
return false;
/*
* Detect whether the node is introducing a collation not
* derived from a foreign Var. (If so, we just mark it unsafe
* for now rather than immediately returning false, since th e
* parent node might not care.)
*/
collation = agg->aggcollid;
if (collation == InvalidOid)
state = FDW_COLLATE_NONE;
else if (inner_cxt.state == FDW_COLLATE_SAFE &&
collation == inner_cxt.collation)
state = FDW_COLLATE_SAFE;
else if (collation == DEFAULT_COLLATION_OID)
state = FDW_COLLATE_NONE;
else
state = FDW_COLLATE_UNSAFE;
}
break;
default:
/*
* If it's anything else, assume it's unsafe. This list can be
* expanded later, but don't forget to add deparse support.
*/
return false;
}
/*
* Now, merge my collation information into my parent's state.
*/
if (state > outer_cxt->state)
{
/* Override previous parent state */
outer_cxt->collation = collation;
outer_cxt->state = state;
}
else if (state == outer_cxt->state)
{
/* Merge, or detect error if there's a collation conflict */
switch (state)
{
case FDW_COLLATE_NONE:
/* Nothing + nothing is still nothing */
break;
case FDW_COLLATE_SAFE:
if (collation != outer_cxt->collation)
{
/*
* Non-default collation always beats default.
*/
if (outer_cxt->collation == DEFAULT_COLLATION_OID)
{
/* Override previous parent state */
outer_cxt->collation = collation;
}
else if (collation != DEFAULT_COLLATION_OID)
{
/*
* Conflict; show state as indeterminate. We don't
* want to "return false" right away, since parent
* node might not care about collation.
*/
outer_cxt->state = FDW_COLLATE_UNSAFE;
}
}
break;
case FDW_COLLATE_UNSAFE:
/* We're still conflicted ... */
break;
}
}
/* It looks OK */
return true;
}
/*
* mongo_is_foreign_expr
* Returns true if given expr is safe to evaluate on the foreign server.
*/
bool
mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression,
bool is_having_cond)
{
foreign_glob_cxt glob_cxt;
foreign_loc_cxt loc_cxt;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) baserel->fdw_private;
/*
* Check that the expression consists of nodes that are safe to execute
* remotely.
*/
glob_cxt.root = root;
glob_cxt.foreignrel = baserel;
/*
* For an upper relation, use relids from its underneath scan relation,
* because the upperrel's own relids currently aren't set to anything
* meaningful by the core code. For other relations, use their own
* relids.
*/
if (IS_UPPER_REL(baserel))
glob_cxt.relids = fpinfo->outerrel->relids;
else
glob_cxt.relids = baserel->relids;
glob_cxt.is_having_cond = is_having_cond;
loc_cxt.collation = InvalidOid;
loc_cxt.state = FDW_COLLATE_NONE;
if (!foreign_expr_walker((Node *) expression, &glob_cxt, &loc_cxt))
return false;
/*
* If the expression has a valid collation that does not arise from a
* foreign var, the expression can not be sent over.
*/
if (loc_cxt.state == FDW_COLLATE_UNSAFE)
return false;
/* OK to evaluate on the remote server */
return true;
}
/*
* prepare_var_list_for_baserel
* Build list of nodes corresponding to the attributes requested for given
* base relation.
*
* The list contains Var nodes corresponding to the attributes specified in
* attrs_used. If whole-row reference is required, add Var nodes corresponding
* to all the attributes in the relation.
*/
static List *
prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used)
{
int attno;
List *tlist = NIL;
Node *node;
bool wholerow_requested = false;
Relation relation;
TupleDesc tupdesc;
Assert(OidIsValid(relid));
/* Planner must have taken a lock, so request no lock here */
relation = table_open(relid, NoLock);
tupdesc = RelationGetDescr(relation);
/* Is whole-row reference requested? */
wholerow_requested = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
attrs_used);
/* Handle user defined attributes first. */
for (attno = 1; attno <= tupdesc->natts; attno++)
{
Form_pg_attribute attr = TupleDescAttr(tupdesc, attno - 1);
/* Ignore dropped attributes. */
if (attr->attisdropped)
continue;
/* For a required attribute create a Var node */
if (wholerow_requested ||
bms_is_member(attno - FirstLowInvalidHeapAttributeNumber,
attrs_used))
{
node = (Node *) makeVar(varno, attno, attr->atttypid,
attr->atttypmod, attr->attcollation, 0);
tlist = lappend(tlist, node);
}
}
table_close(relation, NoLock);
return tlist;
}
/*
* column_info_hash
* Creates a hash table that maps varno and varattno to the column names,
* and also stores whether the column is part of outer relation or not.
*
* This table helps us to form the pipeline quickly.
*/
static HTAB *
column_info_hash(List *colname_list, List *colnum_list, List *rti_list,
List *isouter_list)
{
HTAB *columnInfoHash;
ColInfoHashKey key;
HASHCTL hashInfo;
ListCell *l1;
ListCell *l2;
ListCell *l3;
ListCell *l4;
memset(&hashInfo, 0, sizeof(hashInfo));
hashInfo.keysize = sizeof(ColInfoHashKey);
hashInfo.entrysize = sizeof(ColInfoHashEntry);
hashInfo.hcxt = CurrentMemoryContext;
columnInfoHash = hash_create("Column Information Hash", MaxHashTableSize,
&hashInfo,
(HASH_ELEM | HASH_BLOBS | HASH_CONTEXT));
Assert(columnInfoHash != NULL);
/*
* There's no forfour() in version 11 and below, so need to traverse one
* list the hard way.
*/
l4 = list_head(isouter_list);
forthree(l1, colname_list, l2, colnum_list, l3, rti_list)
{
ColInfoHashEntry *columnInfo;
char *columnName = strVal(lfirst(l1));
int columnNum = lfirst_int(l2);
int varNo = lfirst_int(l3);
bool isOuter = lfirst_int(l4);
key.varNo = varNo;
key.varAttno = columnNum;
columnInfo = (ColInfoHashEntry *) hash_search(columnInfoHash,
(void *) &key,
HASH_ENTER,
NULL);
Assert(columnInfo != NULL);
columnInfo->colName = columnName;
columnInfo->isOuter = isOuter;
l4 = lnext(isouter_list, l4);
}
return columnInfoHash;
}
/*
* mongo_prepare_pipeline
* Form query pipeline syntax equivalent to postgresql.
*
* From the example given on mongo_query_document, the following part of
* MongoDB query formed by this function:
*
* "pipeline": [
* {
* "$match":
* {
* "$expr":
* {
* "$and": [
* { "$eq": [ "$$v_age", "$old" ] }
* { "$ne": [ "$$v_age", null ] },
* { "$ne": [ "$old", null ] },
* ]
* }
* }
* }
* ]
*/
static void
mongo_prepare_pipeline(List *clause, BSON *inner_pipeline,
pipeline_cxt *context)
{
BSON *and_query_doc = bsonCreate();
BSON match_object;
BSON match_stage;
BSON expr;
BSON and_op;
if (context->isJoinClause)
{
int inner_pipeline_index = 0;
bsonAppendStartObject(inner_pipeline,
psprintf("%d", inner_pipeline_index++),
&match_object);
bsonAppendStartObject(&match_object, "$match", &match_stage);
}
else
bsonAppendStartObject(inner_pipeline, "$match", &match_stage);
bsonAppendStartObject(&match_stage, "$expr", &expr);
bsonAppendStartArray(and_query_doc, "$and", &and_op);
context->arrayIndex = 0;
context->opExprCount = 0;
/* Append JOIN/WHERE/HAVING clause expression */
mongo_append_clauses_to_pipeline(clause, &and_op, context);
/* Append $and array to $expr */
bson_append_array(&expr, "$and", (int) strlen("$and"), &and_op);
bsonAppendFinishArray(and_query_doc, &and_op);
bsonAppendFinishObject(&match_stage, &expr);
if (context->isJoinClause)
{
bsonAppendFinishObject(&match_object, &match_stage);
bsonAppendFinishObject(inner_pipeline, &match_object);
}
else
bsonAppendFinishObject(inner_pipeline, &match_stage);
}
/*
* mongo_append_clauses_to_pipeline
* Append all JOIN/WHERE/HAVING clauses to mongoDB's $and array.
*/
static void
mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc,
pipeline_cxt *context)
{
ListCell *lc;
/* loop through all clauses */
foreach(lc, clause)
{
Expr *expr = (Expr *) lfirst(lc);
/* Extract clause from RestrictInfo */
if (IsA(expr, RestrictInfo))
{
RestrictInfo *ri = (RestrictInfo *) expr;
expr = ri->clause;
}
mongo_append_expr(expr, child_doc, context);
context->arrayIndex++;
}
}
/*
* mongo_is_foreign_param
* Returns true if given expr is something we'd have to send the
* value of to the foreign server.
*/
bool
mongo_is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr)
{
if (expr == NULL)
return false;
switch (nodeTag(expr))
{
case T_Var:
{
/* It would have to be sent unless it's a foreign Var. */
Var *var = (Var *) expr;
Relids relids;
MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) (baserel->fdw_private);
if (IS_UPPER_REL(baserel))
relids = fpinfo->outerrel->relids;
else
relids = baserel->relids;
if (bms_is_member(var->varno, relids) && var->varlevelsup == 0)
return false; /* foreign Var, so not a param. */
else
return true; /* it'd have to be a param. */
break;
}
case T_Param:
/* Params always have to be sent to the foreign server. */
return true;
default:
break;
}
return false;
}
#if PG_VERSION_NUM >= 160000
/*
* mongo_append_unique_var
* Append var to var list, but only if it isn't already in the list.
*
* Whether a var is already a member of list is determined using varno and
* varattno.
*/
static List *
mongo_append_unique_var(List *varlist, Var *var)
{
ListCell *lc;
foreach(lc, varlist)
{
Var *tlvar = (Var *) lfirst(lc);
if (IsA(tlvar, Var) &&
tlvar->varno == var->varno &&
tlvar->varattno == var->varattno)
return varlist;
}
return lappend(varlist, var);
}
#endif
/*
* get_varname_for_outer_col
* Form variable name from outer relation column name.
*/
char *
get_varname_for_outer_col(const char *str)
{
static char result[NAMEDATALEN + 2];
/*
* Add prefix "v_" to column name to form variable name. Need to prefix
* with any lowercase letter because variable names must begin with only a
* lowercase ASCII letter or a non-ASCII character.
*/
snprintf(result, sizeof(result), "v_%s", str);
/*
* Also, replace occurences of dot (".") in the variable name with
* underscore ("_"), because special characters other than "_" are NOT
* allowed.
*/
mongo_replace_char(result + 2, '.', '_');
return result;
}
/*
* mongo_replace_char
* Find and replace given character from the string.
*/
void
mongo_replace_char(char *str, char find, char replace)
{
int i;
int len = strlen(str);
for (i = 0; i < len; i++)
if (str[i] == find)
str[i] = replace;
}
mongo_fdw-REL-5_5_3/mongo_query.h 0000664 0000000 0000000 00000010716 15066665201 0017016 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_query.h
* FDW query handling for mongo_fdw
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_query.h
*
*-------------------------------------------------------------------------
*/
#ifndef MONGO_QUERY_H
#define MONGO_QUERY_H
#define NUMERICARRAY_OID 1231
/*
* Context for aggregation pipeline formation.
*/
typedef struct pipeline_cxt
{
struct HTAB *colInfoHash; /* columns information hash */
unsigned int arrayIndex; /* Index of the various arrays in the
* pipeline, starting from zero */
bool isBoolExpr; /* is join expression boolean? */
bool isJoinClause; /* is join clause? This is to add null check
* only in case of join clause */
uint32 opExprCount; /* count death of the expression */
ForeignScanState *scanStateNode; /* To evaluate param expression */
} pipeline_cxt;
/*
* ColInfoEntry represents a hash table entry that maps a unique column's varno
* and varattno to the column name and related information. We construct these
* hash table entries to speed up the BSON query document formation.
*/
typedef struct ColInfoHashKey
{
int varNo;
int varAttno;
} ColInfoHashKey;
typedef struct ColInfoEntry
{
ColInfoHashKey key; /* Hash key */
char *colName;
bool isOuter;
} ColInfoHashEntry;
/*
* Indexes of FDW-private information stored in fdw_private lists.
*
* These items are indexed with the enum mongoFdwScanPrivateIndex, so an item
* can be fetched with list_nth(). For example, to get the column list:
* col_list = strVal(list_nth(fdw_private, mongoFdwPrivateColumnList));
*/
enum mongoFdwScanPrivateIndex
{
/*
* Column list to form column mapping hash i.e. to get only needed columns
* from all fetched columns from remote.
*/
mongoFdwPrivateColumnList,
/* Expressions to execute remotely */
mongoFdwPrivateRemoteExprList,
/* Relation Type (BASE/JOIN/UPPER/UPPER_JOIN) */
mongoFdwPrivateRelType,
/*
* List of column name, attribute number, range table index, and whether
* this column is of outer relation or not.
*
* The columns which are part of the join clauses are listed.
*/
mongoFdwPrivateJoinClauseColNameList,
mongoFdwPrivareJoinClauseColNumList,
mongoFdwPrivateJoinClauseRtiList,
mongoFdwPrivateJoinClauseIsOuterList,
/* ORDER BY clause information */
mongoFdwPrivatePathKeyList,
mongoFdwPrivateIsAscSortList,
/* LIMIT/OFFSET clause information */
mongoFdwPrivateHasLimitClause,
mongoFdwPrivateLimitCountList,
mongoFdwPrivateLimitOffsetList,
/* Upper relation information */
/* Upper relation grouping operation name list */
mongoFdwPrivateAggType,
/* List of column names involved in grouping operation list */
mongoFdwPrivateAggColList,
/* GROUP BY clause expression */
mongoFdwPrivateGroupByColList,
/* Having expression */
mongoFdwPrivateHavingExpr,
/* Is the grouping expression part of HAVING expression or not? */
mongoFdwPrivateIsHavingList,
/*
* String describing join i.e. names of relations being joined and types
* of join, added when the scan is join
*/
mongoFdwPrivateRelations,
/*
* List of column names and whether those are part of inner or outer
* relation stored to form Column Mapping Hash. These are needed column
* means those are part of target and restriction columns.
*/
mongoFdwPrivateColNameList,
mongoFdwPrivateColIsInnerList,
/* Inner and Outer relation names */
mongoFdwPrivateJoinInnerOuterRelName,
/* List of join clauses to form a pipeline */
mongoFdwPrivateJoinClauseList,
/* Join-type */
mongoFdwPrivateJoinType
};
/* Function to be used in mongo_fdw.c */
extern bool append_mongo_value(BSON *queryDocument, const char *keyName,
Datum value, bool isnull, Oid id);
/* Functions to be used in deparse.c */
extern char *mongo_operator_name(const char *operatorName);
extern void append_constant_value(BSON *queryDocument, const char *keyName,
Const *constant);
extern void mongo_append_expr(Expr *node, BSON *child_doc,
pipeline_cxt *context);
extern void append_param_value(BSON *queryDocument, const char *keyName,
Param *paramNode,
ForeignScanState *scanStateNode);
extern char *get_varname_for_outer_col(const char *str);
extern void mongo_replace_char(char* str, char find, char replace);
#endif /* MONGO_QUERY_H */
mongo_fdw-REL-5_5_3/mongo_wrapper.c 0000664 0000000 0000000 00000040500 15066665201 0017316 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_wrapper.c
* Wrapper functions for remote MongoDB servers
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_wrapper.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include
#include "mongo_wrapper.h"
#define ITER_TYPE(i) ((bson_type_t) * ((i)->raw + (i)->type))
/*
* mongoConnect
* Connect to MongoDB server using Host/ip and Port number.
*/
MONGO_CONN *
mongoConnect(MongoFdwOptions *opt)
{
MONGO_CONN *client;
char *uri;
if (opt->svr_username && opt->svr_password)
{
if (opt->authenticationDatabase)
{
if (opt->replicaSet)
{
if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s&replicaSet=%s",
opt->svr_username,
opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->readPreference,
opt->ssl ? "true" : "false",
opt->authenticationDatabase,
opt->replicaSet);
else
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s&replicaSet=%s",
opt->svr_username,
opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->ssl ? "true" : "false",
opt->authenticationDatabase,
opt->replicaSet);
}
else if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s",
opt->svr_username, opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->readPreference,
opt->ssl ? "true" : "false",
opt->authenticationDatabase);
else
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s",
opt->svr_username, opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->ssl ? "true" : "false",
opt->authenticationDatabase);
}
else if (opt->replicaSet)
{
if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s",
opt->svr_username, opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->readPreference,
opt->ssl ? "true" : "false",
opt->replicaSet);
else
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&replicaSet=%s",
opt->svr_username, opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->ssl ? "true" : "false",
opt->replicaSet);
}
else if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s",
opt->svr_username, opt->svr_password,
opt->svr_address, opt->svr_port,
opt->svr_database, opt->readPreference,
opt->ssl ? "true" : "false");
else
uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s",
opt->svr_username, opt->svr_password,
opt->svr_address,
opt->svr_port, opt->svr_database,
opt->ssl ? "true" : "false");
}
else if (opt->replicaSet)
{
if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s",
opt->svr_address, opt->svr_port,
opt->svr_database, opt->readPreference,
opt->ssl ? "true" : "false",
opt->replicaSet);
else
uri = bson_strdup_printf("mongodb://%s:%hu/%s?ssl=%s&replicaSet=%s",
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->ssl ? "true" : "false",
opt->replicaSet);
}
else if (opt->readPreference)
uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s",
opt->svr_address, opt->svr_port,
opt->svr_database, opt->readPreference,
opt->ssl ? "true" : "false");
else
uri = bson_strdup_printf("mongodb://%s:%hu/%s?ssl=%s",
opt->svr_address, opt->svr_port,
opt->svr_database,
opt->ssl ? "true" : "false");
client = mongoc_client_new(uri);
if (opt->ssl)
{
mongoc_ssl_opt_t *ssl_opts = (mongoc_ssl_opt_t *) malloc(sizeof(mongoc_ssl_opt_t));
ssl_opts->pem_file = opt->pem_file;
ssl_opts->pem_pwd = opt->pem_pwd;
ssl_opts->ca_file = opt->ca_file;
ssl_opts->ca_dir = opt->ca_dir;
ssl_opts->crl_file = opt->crl_file;
ssl_opts->weak_cert_validation = opt->weak_cert_validation;
mongoc_client_set_ssl_opts(client, ssl_opts);
free(ssl_opts);
}
bson_free(uri);
if (client == NULL)
ereport(ERROR,
(errmsg("could not connect to %s:%d", opt->svr_address,
opt->svr_port),
errhint("Mongo driver connection error.")));
return client;
}
/*
* mongoDisconnect
* Disconnect from MongoDB server.
*/
void
mongoDisconnect(MONGO_CONN *conn)
{
if (conn)
mongoc_client_destroy(conn);
}
/*
* mongoInsert
* Insert a document 'b' into MongoDB.
*/
bool
mongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b)
{
mongoc_collection_t *c;
bson_error_t error;
bool r = false;
c = mongoc_client_get_collection(conn, database, collection);
r = mongoc_collection_insert(c, MONGOC_INSERT_NONE, b, NULL, &error);
mongoc_collection_destroy(c);
if (!r)
ereport(ERROR,
(errmsg("failed to insert row"),
errhint("Mongo error: \"%s\"", error.message)));
return true;
}
/*
* mongoUpdate
* Update a document 'b' into MongoDB.
*/
bool
mongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b,
BSON *op)
{
mongoc_collection_t *c;
bson_error_t error;
bool r = false;
c = mongoc_client_get_collection(conn, database, collection);
r = mongoc_collection_update(c, MONGOC_UPDATE_NONE, b, op, NULL, &error);
mongoc_collection_destroy(c);
if (!r)
ereport(ERROR,
(errmsg("failed to update row"),
errhint("Mongo error: \"%s\"", error.message)));
return true;
}
/*
* mongoDelete
* Delete MongoDB's document.
*/
bool
mongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b)
{
mongoc_collection_t *c;
bson_error_t error;
bool r = false;
c = mongoc_client_get_collection(conn, database, collection);
r = mongoc_collection_remove(c, MONGOC_DELETE_SINGLE_REMOVE, b, NULL,
&error);
mongoc_collection_destroy(c);
if (!r)
ereport(ERROR,
(errmsg("failed to delete row"),
errhint("Mongo error: \"%s\"", error.message)));
return true;
}
/*
* mongoCursorCreate
* Performs a query against the configured MongoDB server and return
* cursor which can be destroyed by calling mongoc_cursor_current.
*/
MONGO_CURSOR *
mongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q)
{
mongoc_collection_t *c;
MONGO_CURSOR *cur;
bson_error_t error;
c = mongoc_client_get_collection(conn, database, collection);
cur = mongoc_collection_aggregate(c, MONGOC_QUERY_NONE, q, NULL, NULL);
mongoc_cursor_error(cur, &error);
if (!cur)
ereport(ERROR,
(errmsg("failed to create cursor"),
errhint("Mongo error: \"%s\"", error.message)));
mongoc_collection_destroy(c);
return cur;
}
/*
* mongoCursorDestroy
* Destroy cursor created by calling mongoCursorCreate function.
*/
void
mongoCursorDestroy(MONGO_CURSOR *c)
{
mongoc_cursor_destroy(c);
}
/*
* mongoCursorBson
* Get the current document from cursor.
*/
const BSON *
mongoCursorBson(MONGO_CURSOR *c)
{
return mongoc_cursor_current(c);
}
/*
* mongoCursorNext
* Get the next document from the cursor.
*/
bool
mongoCursorNext(MONGO_CURSOR *c, BSON *b)
{
return mongoc_cursor_next(c, (const BSON **) &b);
}
/*
* bsonCreate
* Allocates a new bson_t structure, and also initialize the bson object.
*
* After that point objects can be appended to that bson object and can be
* iterated. A newly allocated bson_t that should be freed with bson_destroy().
*/
BSON *
bsonCreate(void)
{
BSON *doc;
doc = bson_new();
bson_init(doc);
return doc;
}
/*
* bsonDestroy
* Destroy Bson object created by bsonCreate function.
*/
void
bsonDestroy(BSON *b)
{
bson_destroy(b);
}
/*
* bsonIterInit
* Initialize the bson Iterator.
*/
bool
bsonIterInit(BSON_ITERATOR *it, BSON *b)
{
return bson_iter_init(it, b);
}
bool
bsonIterSubObject(BSON_ITERATOR *it, BSON *b)
{
const uint8_t *buffer;
uint32_t len;
bson_iter_document(it, &len, &buffer);
bson_init_static(b, buffer, len);
return true;
}
int32_t
bsonIterInt32(BSON_ITERATOR *it)
{
BSON_ASSERT(it);
switch ((int) ITER_TYPE(it))
{
case BSON_TYPE_BOOL:
return (int32) bson_iter_bool(it);
case BSON_TYPE_DOUBLE:
{
double val = bson_iter_double(it);
if (val < PG_INT32_MIN || val > PG_INT32_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%f\" is out of range for type integer",
val)));
return (int32) val;
}
case BSON_TYPE_INT64:
{
int64 val = bson_iter_int64(it);
if (val < PG_INT32_MIN || val > PG_INT32_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%ld\" is out of range for type integer",
val)));
return (int32) val;
}
case BSON_TYPE_INT32:
return bson_iter_int32(it);
default:
return 0;
}
}
int64_t
bsonIterInt64(BSON_ITERATOR *it)
{
return bson_iter_as_int64(it);
}
double
bsonIterDouble(BSON_ITERATOR *it)
{
return bson_iter_as_double(it);
}
bool
bsonIterBool(BSON_ITERATOR *it)
{
return bson_iter_as_bool(it);
}
const char *
bsonIterString(BSON_ITERATOR *it)
{
uint32_t len = 0;
return bson_iter_utf8(it, &len);
}
const char *
bsonIterBinData(BSON_ITERATOR *it, uint32_t *len)
{
const uint8_t *binary = NULL;
bson_subtype_t subtype = BSON_SUBTYPE_BINARY;
bson_iter_binary(it, &subtype, len, &binary);
return (char *) binary;
}
const bson_oid_t *
bsonIterOid(BSON_ITERATOR *it)
{
return bson_iter_oid(it);
}
time_t
bsonIterDate(BSON_ITERATOR *it)
{
return bson_iter_date_time(it);
}
const char *
bsonIterKey(BSON_ITERATOR *it)
{
return bson_iter_key(it);
}
int
bsonIterType(BSON_ITERATOR *it)
{
return bson_iter_type(it);
}
int
bsonIterNext(BSON_ITERATOR *it)
{
return bson_iter_next(it);
}
bool
bsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub)
{
return bson_iter_recurse(it, sub);
}
void
bsonOidFromString(bson_oid_t *o, char *str)
{
bson_oid_init_from_string(o, str);
}
bool
bsonAppendOid(BSON *b, const char *key, bson_oid_t *v)
{
return bson_append_oid(b, key, strlen(key), v);
}
bool
bsonAppendBool(BSON *b, const char *key, bool v)
{
return bson_append_bool(b, key, -1, v);
}
bool
bsonAppendStartObject(BSON *b, char *key, BSON *r)
{
return bson_append_document_begin(b, key, strlen(key), r);
}
bool
bsonAppendFinishObject(BSON *b, BSON *r)
{
return bson_append_document_end(b, r);
}
bool
bsonAppendNull(BSON *b, const char *key)
{
return bson_append_null(b, key, strlen(key));
}
bool
bsonAppendInt32(BSON *b, const char *key, int v)
{
return bson_append_int32(b, key, strlen(key), v);
}
bool
bsonAppendInt64(BSON *b, const char *key, int64_t v)
{
return bson_append_int64(b, key, strlen(key), v);
}
bool
bsonAppendDouble(BSON *b, const char *key, double v)
{
return bson_append_double(b, key, strlen(key), v);
}
bool
bsonAppendUTF8(BSON *b, const char *key, char *v)
{
return bson_append_utf8(b, key, strlen(key), v, strlen(v));
}
bool
bsonAppendBinary(BSON *b, const char *key, char *v, size_t len)
{
return bson_append_binary(b, key, (int) strlen(key), BSON_SUBTYPE_BINARY,
(const uint8_t *) v, len);
}
bool
bsonAppendDate(BSON *b, const char *key, time_t v)
{
return bson_append_date_time(b, key, strlen(key), v);
}
bool
bsonAppendBson(BSON *b, char *key, BSON *c)
{
return bson_append_document(b, key, strlen(key), c);
}
bool
bsonAppendStartArray(BSON *b, const char *key, BSON *c)
{
return bson_append_array_begin(b, key, -1, c);
}
bool
bsonAppendFinishArray(BSON *b, BSON *c)
{
return bson_append_array_end(b, c);
}
bool
jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v)
{
bool status = true;
if (!v)
{
bsonAppendNull(bb, k);
return status;
}
switch (json_object_get_type(v))
{
case json_type_int:
bsonAppendInt32(bb, k, json_object_get_int(v));
break;
case json_type_boolean:
bsonAppendBool(bb, k, json_object_get_boolean(v));
break;
case json_type_double:
bsonAppendDouble(bb, k, json_object_get_double(v));
break;
case json_type_string:
bsonAppendUTF8(bb, k, (char *) json_object_get_string(v));
break;
case json_type_object:
{
BSON t;
struct json_object *joj;
joj = json_object_object_get(v, "$oid");
if (joj != NULL)
{
bson_oid_t bsonObjectId;
memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes));
bsonOidFromString(&bsonObjectId, (char *) json_object_get_string(joj));
status = bsonAppendOid(bb, k, &bsonObjectId);
break;
}
joj = json_object_object_get(v, "$date");
if (joj != NULL)
{
status = bsonAppendDate(bb, k, json_object_get_int64(joj));
break;
}
bsonAppendStartObject(bb, (char *) k, &t);
{
json_object_object_foreach(v, kk, vv)
jsonToBsonAppendElement(&t, kk, vv);
}
bsonAppendFinishObject(bb, &t);
}
break;
case json_type_array:
{
int i;
char buf[12];
BSON t;
bsonAppendStartArray(bb, k, &t);
for (i = 0; i < json_object_array_length(v); i++)
{
snprintf(buf, sizeof(buf), "%d", i);
jsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i));
}
bsonAppendFinishObject(bb, &t);
}
break;
default:
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
errmsg("can't handle type for : %s",
json_object_to_json_string(v))));
}
return status;
}
json_object *
jsonTokenerPrase(char *s)
{
return json_tokener_parse(s);
}
/*
* mongoAggregateCount
* Count the number of documents.
*/
double
mongoAggregateCount(MONGO_CONN *conn, const char *database,
const char *collection, const BSON *b)
{
BSON *command;
BSON *reply;
double count = 0;
bson_error_t error;
bool retval;
bson_iter_t it;
command = bsonCreate();
reply = bsonCreate();
bsonAppendUTF8(command, "count", (char *) collection);
if (b) /* Not empty */
bsonAppendBson(command, "query", (BSON *) b);
retval = mongoc_client_command_simple(conn, database, command, NULL, reply,
&error);
if (!retval)
ereport(ERROR,
(errmsg("failed to get the document count"),
errhint("Mongo error: \"%s\"", error.message)));
if (bson_iter_init_find(&it, reply, "n"))
count = bsonIterDouble(&it);
bsonDestroy(reply);
bsonDestroy(command);
return count;
}
void
bsonOidToString(const bson_oid_t *o, char str[25])
{
bson_oid_to_string(o, str);
}
const char *
bsonIterCode(BSON_ITERATOR *i)
{
return bson_iter_code(i, NULL);
}
const char *
bsonIterRegex(BSON_ITERATOR *i)
{
return bson_iter_regex(i, NULL);
}
const bson_value_t *
bsonIterValue(BSON_ITERATOR *i)
{
return bson_iter_value(i);
}
void
bsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray)
{
if (isArray)
dumpJsonArray(output, iter);
else
dumpJsonObject(output, iter);
}
/*
* dumpJsonObject
* Converts BSON document to a JSON string.
*
* isArray signifies if bsonData is contents of array or object.
* [Some of] special BSON datatypes are converted to JSON using
* "Strict MongoDB Extended JSON" [1].
*
* [1] http://docs.mongodb.org/manual/reference/mongodb-extended-json/
*/
void
dumpJsonObject(StringInfo output, BSON_ITERATOR *iter)
{
uint32_t len;
const uint8_t *data;
BSON bson;
bson_iter_document(iter, &len, &data);
if (bson_init_static(&bson, data, len))
{
char *json = bsonAsJson(&bson);
if (json != NULL)
{
appendStringInfoString(output, json);
bson_free(json);
}
}
}
void
dumpJsonArray(StringInfo output, BSON_ITERATOR *iter)
{
uint32_t len;
const uint8_t *data;
BSON bson;
bson_iter_array(iter, &len, &data);
if (bson_init_static(&bson, data, len))
{
char *json;
if ((json = bson_array_as_legacy_extended_json(&bson, NULL)))
{
appendStringInfoString(output, json);
bson_free(json);
}
}
}
char *
bsonAsJson(const BSON *bsonDocument)
{
return bson_as_legacy_extended_json(bsonDocument, NULL);
}
mongo_fdw-REL-5_5_3/mongo_wrapper.h 0000664 0000000 0000000 00000007027 15066665201 0017332 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* mongo_wrapper.h
* Wrapper functions for remote MongoDB servers
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* mongo_wrapper.h
*
*-------------------------------------------------------------------------
*/
#ifndef MONGO_WRAPPER_H
#define MONGO_WRAPPER_H
#include "mongo_fdw.h"
#include "mongoc.h"
#define json_object json_object_tmp
#include
MONGO_CONN *mongoConnect(MongoFdwOptions *opt);
void mongoDisconnect(MONGO_CONN *conn);
bool mongoInsert(MONGO_CONN *conn, char *database, char *collection,
BSON *b);
bool mongoUpdate(MONGO_CONN *conn, char *database, char *collection,
BSON *b, BSON *op);
bool mongoDelete(MONGO_CONN *conn, char *database, char *collection,
BSON *b);
MONGO_CURSOR *mongoCursorCreate(MONGO_CONN *conn, char *database,
char *collection, BSON *q);
const BSON *mongoCursorBson(MONGO_CURSOR *c);
bool mongoCursorNext(MONGO_CURSOR *c, BSON *b);
void mongoCursorDestroy(MONGO_CURSOR *c);
double mongoAggregateCount(MONGO_CONN *conn, const char *database,
const char *collection, const BSON *b);
BSON *bsonCreate(void);
void bsonDestroy(BSON *b);
bool bsonIterInit(BSON_ITERATOR *it, BSON *b);
bool bsonIterSubObject(BSON_ITERATOR *it, BSON *b);
int32_t bsonIterInt32(BSON_ITERATOR *it);
int64_t bsonIterInt64(BSON_ITERATOR *it);
double bsonIterDouble(BSON_ITERATOR *it);
bool bsonIterBool(BSON_ITERATOR *it);
const char *bsonIterString(BSON_ITERATOR *it);
const char *bsonIterBinData(BSON_ITERATOR *it, uint32_t *len);
const bson_oid_t *bsonIterOid(BSON_ITERATOR *it);
time_t bsonIterDate(BSON_ITERATOR *it);
int bsonIterType(BSON_ITERATOR *it);
int bsonIterNext(BSON_ITERATOR *it);
bool bsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub);
void bsonOidFromString(bson_oid_t *o, char *str);
void bsonOidToString(const bson_oid_t *o, char str[25]);
const char *bsonIterCode(BSON_ITERATOR *i);
const char *bsonIterRegex(BSON_ITERATOR *i);
const char *bsonIterKey(BSON_ITERATOR *i);
const bson_value_t *bsonIterValue(BSON_ITERATOR *i);
void bsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer);
BSON *bsonCreate();
bool bsonAppendOid(BSON *b, const char *key, bson_oid_t *v);
bool bsonAppendBool(BSON *b, const char *key, bool v);
bool bsonAppendNull(BSON *b, const char *key);
bool bsonAppendInt32(BSON *b, const char *key, int v);
bool bsonAppendInt64(BSON *b, const char *key, int64_t v);
bool bsonAppendDouble(BSON *b, const char *key, double v);
bool bsonAppendUTF8(BSON *b, const char *key, char *v);
bool bsonAppendBinary(BSON *b, const char *key, char *v, size_t len);
bool bsonAppendDate(BSON *b, const char *key, time_t v);
bool bsonAppendStartArray(BSON *b, const char *key, BSON *c);
bool bsonAppendFinishArray(BSON *b, BSON *c);
bool bsonAppendStartObject(BSON *b, char *key, BSON *r);
bool bsonAppendFinishObject(BSON *b, BSON *r);
bool bsonAppendBson(BSON *b, char *key, BSON *c);
bool jsonToBsonAppendElement(BSON *bb, const char *k,
struct json_object *v);
json_object *jsonTokenerPrase(char *s);
char *bsonAsJson(const BSON *bsonDocument);
void bsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter,
bool isArray);
void dumpJsonObject(StringInfo output, BSON_ITERATOR *iter);
void dumpJsonArray(StringInfo output, BSON_ITERATOR *iter);
#endif /* MONGO_QUERY_H */
mongo_fdw-REL-5_5_3/mongodb_init.sh 0000775 0000000 0000000 00000003332 15066665201 0017304 0 ustar 00root root 0000000 0000000 #!/bin/sh
export MONGO_HOST="localhost"
export MONGO_PORT="27017"
export MONGO_USER_NAME="edb"
export MONGO_PWD="edb"
# Below commands must be run in MongoDB to create mongo_fdw_regress and mongo_fdw_regress1 databases
# used in regression tests with edb user and edb password.
# use mongo_fdw_regress
# db.createUser({user:"edb",pwd:"edb",roles:[{role:"dbOwner", db:"mongo_fdw_regress"},{role:"readWrite", db:"mongo_fdw_regress"}]})
# use mongo_fdw_regress1
# db.createUser({user:"edb",pwd:"edb",roles:[{role:"dbOwner", db:"mongo_fdw_regress1"},{role:"readWrite", db:"mongo_fdw_regress1"}]})
# use mongo_fdw_regress2
# db.createUser({user:"edb",pwd:"edb",roles:[{role:"dbOwner", db:"mongo_fdw_regress2"},{role:"readWrite", db:"mongo_fdw_regress2"}]})
mongoimport --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --db mongo_fdw_regress --collection countries --jsonArray --drop --maintainInsertionOrder --quiet < data/mongo_fixture.json
mongoimport --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --db mongo_fdw_regress --collection warehouse --jsonArray --drop --maintainInsertionOrder --quiet < data/mongo_warehouse.json
mongoimport --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --db mongo_fdw_regress --collection testlog --jsonArray --drop --maintainInsertionOrder --quiet < data/mongo_testlog.json
mongoimport --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --db mongo_fdw_regress --collection testdevice --jsonArray --drop --maintainInsertionOrder --quiet < data/mongo_testdevice.json
mongosh --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --authenticationDatabase "mongo_fdw_regress" < data/mongo_test_data.js > /dev/null
mongo_fdw-REL-5_5_3/option.c 0000664 0000000 0000000 00000020640 15066665201 0015752 0 ustar 00root root 0000000 0000000 /*-------------------------------------------------------------------------
*
* option.c
* FDW option handling for mongo_fdw
*
* Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 2004-2025, EnterpriseDB Corporation.
* Portions Copyright (c) 2012–2014 Citus Data, Inc.
*
* IDENTIFICATION
* option.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "mongo_wrapper.h"
/*
* Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER,
* USER MAPPING or FOREIGN TABLE that uses postgres_fdw.
*
* Raise an ERROR if the option or its value is considered invalid.
*/
extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(mongo_fdw_validator);
/*
* mongo_fdw_validator
* Validates options given to one of the following commands:
* foreign data wrapper, server, user mapping, or foreign table.
*
* This function errors out if the given option name or its value is considered
* invalid.
*/
Datum
mongo_fdw_validator(PG_FUNCTION_ARGS)
{
Datum optionArray = PG_GETARG_DATUM(0);
Oid optionContextId = PG_GETARG_OID(1);
List *optionList = untransformRelOptions(optionArray);
ListCell *optionCell;
foreach(optionCell, optionList)
{
DefElem *optionDef = (DefElem *) lfirst(optionCell);
char *optionName = optionDef->defname;
bool optionValid = false;
int32 optionIndex;
for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++)
{
const MongoValidOption *validOption;
validOption = &(ValidOptionArray[optionIndex]);
if ((optionContextId == validOption->optionContextId) &&
(strncmp(optionName, validOption->optionName, NAMEDATALEN) == 0))
{
optionValid = true;
break;
}
}
/* If invalid option, display an informative error message */
if (!optionValid)
{
StringInfo optionNamesString;
optionNamesString = mongo_option_names_string(optionContextId);
ereport(ERROR,
(errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
errmsg("invalid option \"%s\"", optionName),
errhint("Valid options in this context are: %s.",
optionNamesString->data)));
}
/* If port option is given, error out if its value isn't an integer */
if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0)
{
char *intString = defGetString(optionDef);
long port;
char *endp;
errno = 0;
port = strtol(intString, &endp, 10);
if (intString == endp)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"unsigned short", intString)));
if (errno == ERANGE || port < 0 || port > USHRT_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("port value \"%s\" is out of range for type %s",
intString, "unsigned short")));
if (*endp && *endp != ' ')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"unsigned short", intString)));
}
else if (strcmp(optionName, OPTION_NAME_USE_REMOTE_ESTIMATE) == 0
|| strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0 ||
strcmp(optionName, OPTION_NAME_ENABLE_JOIN_PUSHDOWN) == 0 ||
strcmp(optionName, OPTION_NAME_SSL) == 0 ||
strcmp(optionName, OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN) == 0 ||
strcmp(optionName, OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN) == 0
)
{
/* These accept only boolean values */
(void) defGetBoolean(optionDef);
}
}
PG_RETURN_VOID();
}
/*
* mongo_option_names_string
* Finds all options that are valid for the current context, and
* concatenates these option names in a comma separated string.
*/
StringInfo
mongo_option_names_string(Oid currentContextId)
{
StringInfo optionNamesString = makeStringInfo();
bool firstOptionPrinted = false;
int32 optionIndex;
for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++)
{
const MongoValidOption *validOption;
validOption = &(ValidOptionArray[optionIndex]);
/* If option belongs to current context, append option name */
if (currentContextId == validOption->optionContextId)
{
if (firstOptionPrinted)
appendStringInfoString(optionNamesString, ", ");
appendStringInfoString(optionNamesString, validOption->optionName);
firstOptionPrinted = true;
}
}
return optionNamesString;
}
/*
* mongo_get_options
* Returns the option values to be used when connecting to and querying
* MongoDB.
*
* To resolve these values, the function checks the foreign table's options,
* and if not present, falls back to default values.
*/
MongoFdwOptions *
mongo_get_options(Oid foreignTableId)
{
ForeignTable *foreignTable;
ForeignServer *foreignServer;
UserMapping *mapping;
List *optionList = NIL;
MongoFdwOptions *options;
ListCell *lc;
foreignTable = GetForeignTable(foreignTableId);
foreignServer = GetForeignServer(foreignTable->serverid);
mapping = GetUserMapping(GetUserId(), foreignTable->serverid);
optionList = mongo_list_concat(optionList, foreignServer->options);
optionList = mongo_list_concat(optionList, foreignTable->options);
optionList = mongo_list_concat(optionList, mapping->options);
options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions));
options->use_remote_estimate = false;
options->ssl = false;
options->weak_cert_validation = false;
options->enable_join_pushdown = true;
options->enable_aggregate_pushdown = true;
options->enable_order_by_pushdown = true;
/* Loop through the options */
foreach(lc, optionList)
{
DefElem *def = (DefElem *) lfirst(lc);
if (strcmp(def->defname, OPTION_NAME_READ_PREFERENCE) == 0)
options->readPreference = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_AUTHENTICATION_DATABASE) == 0)
options->authenticationDatabase = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_REPLICA_SET) == 0)
options->replicaSet = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_SSL) == 0)
options->ssl = defGetBoolean(def);
else if (strcmp(def->defname, OPTION_NAME_PEM_FILE) == 0)
options->pem_file = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_PEM_PWD) == 0)
options->pem_pwd = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_CA_FILE) == 0)
options->ca_file = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_CA_DIR) == 0)
options->ca_dir = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_CRL_FILE) == 0)
options->crl_file = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_WEAK_CERT) == 0)
options->weak_cert_validation = defGetBoolean(def);
else if (strcmp(def->defname, OPTION_NAME_ENABLE_JOIN_PUSHDOWN) == 0)
options->enable_join_pushdown = defGetBoolean(def);
else if (strcmp(def->defname,
OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN) == 0)
options->enable_aggregate_pushdown = defGetBoolean(def);
else if (strcmp(def->defname,
OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN) == 0)
options->enable_order_by_pushdown = defGetBoolean(def);
else /* This is for continuation */
if (strcmp(def->defname, OPTION_NAME_ADDRESS) == 0)
options->svr_address = pstrdup(defGetString(def));
else if (strcmp(def->defname, OPTION_NAME_PORT) == 0)
options->svr_port = atoi(defGetString(def));
else if (strcmp(def->defname, OPTION_NAME_DATABASE) == 0)
options->svr_database = pstrdup(defGetString(def));
else if (strcmp(def->defname, OPTION_NAME_COLLECTION) == 0)
options->collectionName = pstrdup(defGetString(def));
else if (strcmp(def->defname, OPTION_NAME_USERNAME) == 0)
options->svr_username = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_PASSWORD) == 0)
options->svr_password = defGetString(def);
else if (strcmp(def->defname, OPTION_NAME_USE_REMOTE_ESTIMATE) == 0)
options->use_remote_estimate = defGetBoolean(def);
}
/* Default values, if required */
if (!options->svr_address)
options->svr_address = pstrdup(DEFAULT_IP_ADDRESS);
if (!options->svr_port)
options->svr_port = DEFAULT_PORT_NUMBER;
if (!options->svr_database)
options->svr_database = pstrdup(DEFAULT_DATABASE_NAME);
if (!options->collectionName)
options->collectionName = get_rel_name(foreignTableId);
return options;
}
void
mongo_free_options(MongoFdwOptions *options)
{
if (options)
{
pfree(options->svr_address);
pfree(options->svr_database);
pfree(options->collectionName);
pfree(options);
}
}
mongo_fdw-REL-5_5_3/sql/ 0000775 0000000 0000000 00000000000 15066665201 0015073 5 ustar 00root root 0000000 0000000 mongo_fdw-REL-5_5_3/sql/aggregate_pushdown.sql 0000664 0000000 0000000 00000101014 15066665201 0021466 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables.
CREATE FOREIGN TABLE fdw137_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE fdw137_t2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
INSERT INTO fdw137_t1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO fdw137_t1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO fdw137_t2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO fdw137_t2 VALUES (0);
-- Create local table.
CREATE TABLE fdw137_local AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1;
-- Simple aggregates. ORDER BY push-down not possible because only column names allowed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1)::int AS sum2 FROM fdw137_t1 WHERE c4 > 600 GROUP BY c4 ORDER BY 1 ASC NULLS FIRST, 2 ASC NULLS FIRST;
-- GROUP BY clause HAVING expressions
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8 ASC NULLS FIRST;
-- Multi-column GROUP BY clause. Push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregation on expression. Don't push-down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST;
-- Aggregate with unshippable GROUP BY clause are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
SELECT avg(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY c1;
-- FDW-134: Test ORDER BY with COLLATE. Shouldn't push-down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY c2 COLLATE "en_US" ASC NULLS FIRST;
-- Using expressions in HAVING clause. Pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
SELECT c3, count(*) FROM fdw137_t1 GROUP BY c3 HAVING abs(max(c8)) = abs(10) ORDER BY 1, 2;
-- Unshippable HAVING clause will be evaluated locally, and other qual in HAVING clause is pushed down
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (avg(c1) / avg(c1)) * random() <= 1 and min(c1) > 100) x;
-- Aggregate over join query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8%2 = 0 ORDER BY 1 DESC NULLS LAST;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c1 = t2.c8) GROUP BY t1.c1, t2.c4 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10 ORDER BY 2 ASC NULLS FIRST;
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Aggregate is not pushed down as aggregation contains random()
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
SELECT sum(c1 * (random() <= 1)::int) AS sum, avg(c1) FROM fdw137_t1 ORDER BY 1;
-- Not pushed down due to local conditions present in underneath input rel
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
SELECT sum(t1.c8) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE ((t1.c8 * t2.c1)/(t1.c8 * t2.c1)) * random() <= 1 ORDER BY 1;
-- Aggregates in subquery are pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
SELECT count(x.a), sum(x.a) FROM (SELECT c8 a, sum(c1) b FROM fdw137_t1 GROUP BY c8 ORDER BY 1, 2) x;
-- Aggregate is still pushed down by taking unshippable expression out
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
SELECT c4 * (random() <= 1)::int AS sum1, sum(c1) AS sum2 FROM fdw137_t1 GROUP BY c4 ORDER BY 1, 2;
-- Testing ORDER BY, DISTINCT, FILTER and Ordered-sets within aggregates
-- ORDER BY within aggregates (same column used to order) are not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
SELECT sum(c1 ORDER BY c1) FROM fdw137_t1 WHERE c1 < 500 GROUP BY c2 ORDER BY 1;
-- ORDER BY within aggregate (different column used to order also using DESC)
-- are not pushed.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
SELECT sum(c8 ORDER BY c1 desc) FROM fdw137_t1 WHERE c1 > 1000 and c8 > 20;
-- DISTINCT within aggregate. Don't push down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
SELECT sum(DISTINCT (c1)) FROM fdw137_t1 WHERE c4 = 600 and c1 < 500;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
SELECT sum(DISTINCT (t1.c1)) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 < 30 GROUP BY (t2.c1) ORDER BY 1;
-- DISTINCT, ORDER BY and FILTER within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
SELECT sum(c1), sum(DISTINCT c1 ORDER BY c1) filter (WHERE c1%3 < 2), c4 FROM fdw137_t1 WHERE c4 = 600 GROUP BY c4;
-- FILTER within aggregate, not pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
SELECT sum(c1) filter (WHERE c1 < 1000 and c4 > 500) FROM fdw137_t1 GROUP BY c4 ORDER BY 1 nulls last;
-- Outer query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
SELECT DISTINCT (SELECT count(*) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 = 500) FROM fdw137_t2 t2 ORDER BY 1;
-- Inner query is aggregation query
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
SELECT DISTINCT (SELECT count(t1.c1) filter (WHERE t2.c1 = 20 and t2.c1 < 30) FROM fdw137_t1 t1 WHERE t1.c1 > 600) FROM fdw137_t2 t2 ORDER BY 1;
-- Ordered-sets within aggregate, not pushed down.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
SELECT c8, rank('10'::varchar) within group (ORDER BY c3), percentile_cont(c8/200::numeric) within group (ORDER BY c1) FROM fdw137_t1 GROUP BY c8 HAVING percentile_cont(c8/200::numeric) within group (ORDER BY c1) < 500 ORDER BY c8;
-- Subquery in FROM clause HAVING aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
SELECT count(*), x.b FROM fdw137_t1, (SELECT c1 a, sum(c1) b FROM fdw137_t2 GROUP BY c1) x WHERE fdw137_t1.c8 = x.a GROUP BY x.b ORDER BY 1, 2;
-- Join with IS NULL check in HAVING
EXPLAIN (VERBOSE, COSTS OFF)
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
SELECT avg(t1.c1), sum(t2.c1) FROM fdw137_t1 t1 join fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t2.c1 HAVING avg(t1.c1) is null ORDER BY 1 nulls last, 2;
-- ORDER BY expression is part of the target list but not pushed down to
-- foreign server.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
SELECT sum(c1) * (random() <= 1)::int AS sum FROM fdw137_t1 ORDER BY 1;
-- LATERAL join, with parameterization
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 t2 GROUP BY t2.c1) qry WHERE t1.c8 * 2 = qry.sum ORDER BY 1;
-- Check with placeHolderVars
EXPLAIN (VERBOSE, COSTS OFF)
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
SELECT q.b, count(fdw137_t1.c1), sum(q.a) FROM fdw137_t1 left join (SELECT min(13), avg(fdw137_t1.c1), sum(fdw137_t2.c1) FROM fdw137_t1 right join fdw137_t2 ON (fdw137_t1.c8 = fdw137_t2.c1) WHERE fdw137_t1.c8 = 20) q(a, b, c) ON (fdw137_t1.c8 = q.b) WHERE fdw137_t1.c1 between 100 and 500 GROUP BY q.b ORDER BY 1 nulls last, 2;
-- Not supported cases
-- The COUNT of column
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(c8) FROM fdw137_t1 ;
SELECT count(c8) FROM fdw137_t1 ;
-- Grouping sets
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 10 GROUP BY rollup(c8) ORDER BY 1 nulls last;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
SELECT c8, sum(c1) FROM fdw137_t1 WHERE c8 > 3 GROUP BY cube(c8) ORDER BY 1 nulls last;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
SELECT c8, c4, sum(c1) FROM fdw137_t1 WHERE c8 > 20 GROUP BY grouping sets(c8, c4) ORDER BY 1 nulls last, 2 nulls last;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
SELECT c8, sum(c1), grouping(c8) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1 nulls last;
-- DISTINCT itself is not pushed down, whereas underneath aggregate is pushed
EXPLAIN (VERBOSE, COSTS OFF)
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
SELECT DISTINCT sum(c1) s FROM fdw137_t1 WHERE c1 > 1000 GROUP BY c1 ORDER BY 1;
-- WindowAgg
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
SELECT c8, sum(c8), count(c8) over (partition by c8%2) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 desc) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
SELECT c8, array_agg(c8) over (partition by c8%2 ORDER BY c8 range between current row and unbounded following) FROM fdw137_t1 WHERE c8 > 10 GROUP BY c8 ORDER BY 1;
-- User defined function for user defined aggregate, VARIADIC
CREATE FUNCTION least_accum(anyelement, variadic anyarray)
returns anyelement language sql AS
'SELECT least($1, min($2[i])) FROM generate_subscripts($2,2) g(i)';
CREATE aggregate least_agg(variadic items anyarray) (
stype = anyelement, sfunc = least_accum
);
-- Not pushed down due to user defined aggregate
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2;
-- Test partition-wise aggregate
SET enable_partitionwise_aggregate TO ON;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
-- Plan with partitionwise aggregates is enabled
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
SELECT c1, sum(c2), min(c2), count(*) FROM fprt1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 2;
-- Check with whole-row reference
-- Should have all the columns in the target list for the given relation
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1;
SET enable_partitionwise_aggregate TO OFF;
-- Support enable_aggregate_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'non-bolean');
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
-- Test the option at table level. Setting option at table level does not
-- affect the setting at server level.
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
-- Test option for aggregation over join. Allow aggregation only if enabled for
-- both the relations involved in the join.
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
-- FDW-560: Aggregation over nested join. As nested join push down is not
-- supported, aggregation shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) INNER JOIN fdw137_t1 t3 ON (t3.c1 = t1.c1) GROUP BY t1.c8 ORDER BY 2;
-- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON.
-- Shouldn't push down join as well as aggregation.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2;
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32, a33, a34, a35 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
SELECT a32, sum(a32) FROM f_test_large GROUP BY
a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15,
a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30,
a31, a32 ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
-- FDW-131: Limit and offset pushdown with Aggregate pushdown.
SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1;
-- Limit 0, Offset 0 with aggregates.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
-- Limit NULL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2;
-- Limit ALL
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
SELECT sum(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2;
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
-- Should throw an error.
SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
-- Should throw an error.
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw137_t2));
-- FDW-559: Test mongo_fdw.enable_aggregate_pushdown GUC.
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_aggregate_pushdown;
-- Negative testing for GUC value.
SET mongo_fdw.enable_aggregate_pushdown to 'abc';
--Disable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Shouldn't pushdown aggregate because GUC is OFF.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
--Enable the GUC enable_aggregate_pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Should pushdown aggregate because GUC is ON.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
SELECT count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY 1;
-- Test for aggregation over join when server and table options for both the
-- tables is true and guc is enabled. Should pushdown.
SET mongo_fdw.enable_aggregate_pushdown to on;
SET mongo_fdw.enable_join_pushdown to on;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
--Disable the GUC enable_join_pushdown. Shouldn't pushdown aggregate.
SET mongo_fdw.enable_join_pushdown to off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
SET mongo_fdw.enable_join_pushdown to on;
--Disable the GUC enable_aggregate_pushdown. Shouldn't pushdown.
SET mongo_fdw.enable_aggregate_pushdown to false;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) GROUP BY t1.c8 ORDER BY 2 ASC NULLS FIRST;
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_aggregate_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_join_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_aggregate_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
ALTER FOREIGN TABLE fdw137_t2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 GROUP BY t1.c8 HAVING avg(t1.c8)*1 > 10
ORDER BY 2 ASC NULLS FIRST;
-- When option enable_aggregate_pushdown is disabled. Shouldn't pushdown
-- aggregate as well as ORDER BY too.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'false');
EXPLAIN (VERBOSE, COSTS OFF)
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1 ASC NULLS FIRST;
ALTER FOREIGN TABLE fdw137_t1 OPTIONS (SET enable_aggregate_pushdown 'true');
-- Cleanup
DELETE FROM fdw137_t1 WHERE c8 IS NULL;
DELETE FROM fdw137_t1 WHERE c8 = 60;
DELETE FROM fdw137_t2 WHERE c1 IS NULL;
DELETE FROM fdw137_t2 WHERE c1 = 50;
DROP FOREIGN TABLE fdw137_t1;
DROP FOREIGN TABLE fdw137_t2;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE f_test_large;
DROP TABLE fprt1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/connection_validation.sql 0000664 0000000 0000000 00000005137 15066665201 0022173 0 ustar 00root root 0000000 0000000 \set VERBOSITY terse
\set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables and validate
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
--
-- fdw-108: After a change to a pg_foreign_server or pg_user_mapping catalog
-- entry, connection should be invalidated.
--
-- Alter one of the SERVER option
-- Set wrong address for mongo_server
ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.10');
ALTER SERVER mongo_server OPTIONS (SET port '9999');
-- Should fail with an error
INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED');
UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2;
DELETE FROM f_mongo_test WHERE a = 2;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Set correct address for mongo_server
ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST);
ALTER SERVER mongo_server OPTIONS (SET port :MONGO_PORT);
-- Should able to insert the data
INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED');
DELETE FROM f_mongo_test WHERE a = 2;
-- Drop user mapping and create with invalid username and password for public
-- user mapping
DROP USER MAPPING FOR public SERVER mongo_server;
CREATE USER MAPPING FOR public SERVER mongo_server
OPTIONS (username 'wrong', password 'wrong');
-- Should fail with an error
INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED');
UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3;
DELETE FROM f_mongo_test WHERE a = 3;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Drop user mapping and create without username and password for public
-- user mapping
DROP USER MAPPING FOR public SERVER mongo_server;
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Should able to insert the data
INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED');
DELETE FROM f_mongo_test WHERE a = 3;
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/dml.sql 0000664 0000000 0000000 00000017475 15066665201 0016406 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress,
-- mongo_fdw_regress1 and mongo_fdw_regress2 databases on MongoDB with all
-- permission for MONGO_USER_NAME user with MONGO_PASS password and ran
-- mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_mongo_test1 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress1', collection 'mongo_test1');
CREATE FOREIGN TABLE f_mongo_test2 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress2', collection 'mongo_test2');
-- Creating foreign table without specifying database.
CREATE FOREIGN TABLE f_mongo_test3 (_id name, a int, b varchar) SERVER mongo_server
OPTIONS (collection 'mongo_test3');
CREATE FOREIGN TABLE f_mongo_test6 (_id name, a int, b text[]) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl6');
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test)
-- exist in a database (mongo_fdw_regress) in mongoDB.
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
INSERT INTO f_mongo_test VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
UPDATE f_mongo_test SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
DELETE FROM f_mongo_test WHERE a = 10;
SELECT a,b FROM f_mongo_test ORDER BY 1, 2;
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test1)
-- not exist in a database (mongo_fdw_regress1) in mongoDB.
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
INSERT INTO f_mongo_test1 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
UPDATE f_mongo_test1 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
DELETE FROM f_mongo_test1 WHERE a = 10;
SELECT a,b FROM f_mongo_test1 ORDER BY 1, 2;
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test2)
-- not exist in a non exist database (mongo_fdw_regress2) in mongoDB.
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
INSERT INTO f_mongo_test2 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
UPDATE f_mongo_test2 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
DELETE FROM f_mongo_test2 WHERE a = 10;
SELECT a,b FROM f_mongo_test2 ORDER BY 1, 2;
-- Verify the INSERT/UPDATE/DELETE operations on a collection (mongo_test)
-- when foreign table created without database option.
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
INSERT INTO f_mongo_test3 VALUES ('0', 10 , 'INSERT');
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
UPDATE f_mongo_test3 SET b = 'UPDATE' WHERE a = 10;
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
DELETE FROM f_mongo_test3 WHERE a = 10;
SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2;
-- FDW-158: Fix server crash when analyzing a foreign table.
ANALYZE f_mongo_test;
-- Should give correct number of rows now.
SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test';
-- Check count using select query on table.
SELECT count(*) FROM f_mongo_test;
-- Some more variants of vacuum and analyze
VACUUM f_mongo_test;
VACUUM FULL f_mongo_test;
VACUUM FREEZE f_mongo_test;
ANALYZE f_mongo_test;
ANALYZE f_mongo_test(a);
VACUUM ANALYZE f_mongo_test;
-- FDW-226: Fix COPY FROM and foreign partition routing results in a
-- server crash
-- Should fail as foreign table direct copy is not supported
COPY f_mongo_test TO '/tmp/data.txt' delimiter ',';
COPY f_mongo_test (a) TO '/tmp/data.txt' delimiter ',';
COPY f_mongo_test (b) TO '/tmp/data.txt' delimiter ',';
-- Should pass
COPY (SELECT * FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT a, b FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT a FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
COPY (SELECT b FROM f_mongo_test) TO '/tmp/data.txt' delimiter ',';
-- Should throw an error as copy to foreign table is not supported
DO
$$
BEGIN
COPY f_mongo_test FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
DO
$$
BEGIN
COPY f_mongo_test(a, b) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
DO
$$
BEGIN
COPY f_mongo_test(a) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
DO
$$
BEGIN
COPY f_mongo_test(b) FROM '/tmp/data.txt' delimiter ',';
EXCEPTION WHEN others THEN
IF SQLERRM = 'COPY and foreign partition routing not supported in mongo_fdw' OR
SQLERRM = 'cannot copy to foreign table "f_mongo_test"' THEN
RAISE NOTICE 'ERROR: COPY and foreign partition routing not supported in mongo_fdw';
ELSE
RAISE NOTICE '%', SQLERRM;
END IF;
END;
$$
LANGUAGE plpgsql;
--FDW-466: Document update for array elements shouldn't lead to the crash
INSERT INTO f_mongo_test6 VALUES (0, 1, ARRAY ['INSERT', 'DELETE']);
SELECT a, b FROM f_mongo_test6 ORDER BY a;
UPDATE f_mongo_test6 SET b[1] = 'UPDATE' WHERE a = 1;
SELECT a, b FROM f_mongo_test6 ORDER BY a;
DELETE FROM f_mongo_test6 WHERE b[2] = 'DELETE';
SELECT a, b FROM f_mongo_test6 ORDER BY a;
-- If first column type is not NAME then UPDATE/DELETE should result into an error.
CREATE FOREIGN TABLE f_mongo_test7 (_id text, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl7');
SELECT a, b FROM f_mongo_test7 ORDER BY 1;
UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE a = 10;
DELETE FROM f_mongo_test7 WHERE a = 10;
DROP FOREIGN TABLE f_mongo_test7;
-- If first column name is not _id then UPDATE/DELETE should result into an error.
CREATE FOREIGN TABLE f_mongo_test7 (id1 NAME, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl7');
SELECT a, b FROM f_mongo_test7 ORDER BY 1;
UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE a = 10;
DELETE FROM f_mongo_test7 WHERE a = 10;
-- When _id is non-objectId type on MongoDB. Should result into an error.
CREATE FOREIGN TABLE f_mongo_test8 (_id NAME, a int, b text) SERVER mongo_server
OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl8');
SELECT * FROM f_mongo_test8 ORDER BY 1;
UPDATE f_mongo_test8 SET b = 'UPDATED' WHERE a = 2;
DELETE FROM f_mongo_test8 WHERE a = 2;
SELECT a, b FROM f_mongo_test8 ORDER BY 1;
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_mongo_test1;
DROP FOREIGN TABLE f_mongo_test2;
DROP FOREIGN TABLE f_mongo_test3;
DROP FOREIGN TABLE f_mongo_test6;
DROP FOREIGN TABLE f_mongo_test7;
DROP FOREIGN TABLE f_mongo_test8;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/join_pushdown.sql 0000664 0000000 0000000 00000101773 15066665201 0020513 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server1;
-- Create foreign tables.
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, c1 INTEGER, c2 TEXT, c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server1 OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
INSERT INTO f_test_tbl1 VALUES (0, 1500, 'EMP15', 'FINANCE', 1300, '2000-12-25', 950.0, 400, 60);
INSERT INTO f_test_tbl1 VALUES (0, 1600, 'EMP16', 'ADMIN', 600);
INSERT INTO f_test_tbl2 VALUES (0, 50, 'TESTING', 'NASHIK');
INSERT INTO f_test_tbl2 VALUES (0);
-- Create local table.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
-- Push down LEFT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST OFFSET 50;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- Push down RIGHT OUTER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl1 e RIGHT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON (d.c1 = 20 AND e.c2 = 'EMP1') ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- Push INNER JOIN.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) AND e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON ((d.c1 = e.c8 OR e.c4 > d.c1) OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 60;
-- Column comparing with 'Constant' pushed down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') ORDER BY 1, 3;
-- INNER JOIN with WHERE clause. Should execute where condition separately
-- (NOT added into join clauses) on remote side.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
-- INNER JOIN in which join clause is not pushable but WHERE condition is
-- pushable with join clause 'TRUE'.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
SELECT d.c1, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (abs(d.c8) = e.c1) WHERE d.c1 = 100 ORDER BY e.c3 DESC NULLS LAST, d.c1 DESC NULLS LAST;
SET mongo_fdw.enable_order_by_pushdown TO ON;
SET enable_mergejoin TO OFF;
SET enable_nestloop TO OFF;
-- Local-Foreign table joins.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
RESET enable_mergejoin;
RESET enable_nestloop;
-- JOIN in sub-query, should be pushed down.
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 IN (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1)) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 LEFT JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
SELECT l.c1, l.c6, l.c8
FROM l_test_tbl1 l
WHERE l.c1 = (SELECT f1.c1 FROM f_test_tbl1 f1 INNER JOIN f_test_tbl2 f2 ON (f1.c8 = f2.c1) LIMIT 1) ORDER BY 1, 3;
-- Execute JOIN through PREPARE statement.
PREPARE pre_stmt_left_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_left_join;
EXECUTE pre_stmt_left_join;
PREPARE pre_stmt_inner_join AS
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 OR e.c4 > d.c1) ORDER BY 1, 3 OFFSET 70;
EXPLAIN (COSTS OFF)
EXECUTE pre_stmt_inner_join;
EXECUTE pre_stmt_inner_join;
-- join + WHERE clause push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c1 = 10 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c8 = 10 ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE d.c2 = 'SALES' ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 WHERE e.c2 = 'EMP2' ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, d.c6, d.c8
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (e.c1 = 20 OR d.c2 = 'EMP1')) WHERE e.c1 = 20 AND d.c8 = 20 ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8 AND (d.c5 = '02-22-1981' OR d.c5 = '12-17-1980')) ORDER BY 1, 3;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' ORDER BY 1;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND d.c1 = 20 OR e.c2 = 'EMP1') WHERE d.c1 = 10 OR e.c8 = 30 ORDER BY 1 DESC NULLS LAST, 3 DESC NULLS LAST;
-- Natural join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d NATURAL JOIN f_test_tbl1 e WHERE e.c1 > d.c8 ORDER BY 1;
-- Self join, should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d INNER JOIN f_test_tbl1 e ON e.c8 = d.c8 ORDER BY 1 OFFSET 65;
-- Join in CTE.
-- Explain plan difference between v11 (or pre) and later.
EXPLAIN (COSTS false, VERBOSE)
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
WITH t (c1_1, c1_3, c2_1) AS (
SELECT d.c1, d.c3, e.c1
FROM f_test_tbl1 d JOIN f_test_tbl2 e ON (d.c8 = e.c1)
) SELECT c1_1, c2_1 FROM t ORDER BY c1_3, c1_1;
-- WHERE with boolean expression. Should push-down.
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl2 e LEFT JOIN f_test_tbl1 d ON (e.c1 = d.c8) WHERE d.c5 = '02-22-1981' OR d.c5 = '12-17-1980' ORDER BY 1;
-- Nested joins(Don't push-down nested join)
SET enable_mergejoin TO OFF;
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65 ;
SELECT d.c1, d.c2, d.c5, e.c1, e.c2
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY d.c1 OFFSET 65;
RESET enable_mergejoin;
-- Not supported expressions won't push-down(e.g. function expression, etc.)
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (ABS(d.c1) = e.c8) ORDER BY 1, 3;
-- Don't pushdown when whole row reference is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT d, e
FROM f_test_tbl1 d LEFT JOIN f_test_tbl2 e ON (e.c1 = d.c8) LEFT JOIN f_test_tbl1 f ON (f.c8 = e.c1) ORDER BY e.c1 OFFSET 65;
-- FDW-733: Don't pushdown when whole row reference is involved in the join
-- clause.
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON (test_varchar.*::text) = (f_test_tbl5._id) ORDER BY 1;
-- Don't pushdown when full document retrieval is involved in the target list.
EXPLAIN (COSTS OFF)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, test_varchar, json_each_text(test_text.__doc::json) AS json_data WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
-- FDW-733: Don't pushdown when full document retrieval is involved in the
-- join clause.
EXPLAIN (COSTS OFF)
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
SELECT test_varchar.__doc::json->'_id'->>'$oid' FROM test_varchar JOIN f_test_tbl5 ON f_test_tbl5._id = test_varchar.__doc::json->'_id'->>'$oid' ORDER BY 1;
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
SELECT f_test_tbl5._id FROM f_test_tbl5 JOIN test_varchar ON test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
EXPLAIN (COSTS OFF)
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
SELECT f_test_tbl5._id FROM f_test_tbl5, test_varchar WHERE test_varchar.__doc::json->'_id'->>'$oid' = f_test_tbl5._id ORDER BY 1;
-- Join two tables from two different foreign servers.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl3 e ON d.c1 = e.c1 ORDER BY 1;
-- SEMI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
SELECT d.c2
FROM f_test_tbl1 d WHERE EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
-- ANTI JOIN, not pushed down
EXPLAIN (COSTS OFF)
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
SELECT d.c2
FROM f_test_tbl1 d WHERE NOT EXISTS (SELECT 1 FROM f_test_tbl2 e WHERE d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
-- FULL OUTER JOIN, should not pushdown.
EXPLAIN (COSTS OFF)
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
SELECT d.c1, e.c1
FROM f_test_tbl1 d FULL JOIN f_test_tbl2 e ON (d.c8 = e.c1) ORDER BY d.c2 LIMIT 10;
-- CROSS JOIN can be pushed down
EXPLAIN (COSTS OFF)
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
SELECT e.c1, d.c2
FROM f_test_tbl1 d CROSS JOIN f_test_tbl2 e ORDER BY e.c1, d.c2 LIMIT 10;
-- FDW-131: Limit and offset pushdown with join pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT round(2.2) OFFSET 2;
-- Limit as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT NULL OFFSET 1;
-- Limit as ALL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (t1.c8 = t2.c1) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
-- Offset as NULL, no LIMIT/OFFSET pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT 3 OFFSET NULL;
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -2;
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST OFFSET -1;
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT -3 OFFSET -1;
-- Limit with expression evaluating to -ve value.
EXPLAIN (COSTS false, VERBOSE)
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
-- Should throw an error.
SELECT t1.c1, t2.c1
FROM f_test_tbl1 t1 JOIN f_test_tbl2 t2 ON (TRUE) ORDER BY t1.c1 ASC NULLS FIRST, t2.c1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM f_test_tbl1));
-- Test partition-wise join
SET enable_partitionwise_join TO on;
-- Create the partition tables
CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1);
CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1');
CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2');
CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2);
CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3');
CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4');
-- Inner join two tables
-- Different explain plan on v10 as partition-wise join is not supported there.
SET enable_mergejoin TO OFF;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
SELECT t1.c1, t2.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2;
-- Inner join three tables
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
SELECT t1.c1, t2.c2, t3.c2
FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER JOIN fprt1 t3 ON (t3.c1 = t2.c2) ORDER BY 1,2;
RESET enable_mergejoin;
-- Join with lateral reference
-- Different explain plan on v10 as partition-wise join is not supported there.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
SELECT t1.c1, t1.c2
FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2
WHERE t1.c1 = t2.c2 AND t1.c2 = t2.c1) q WHERE t1.c1 % 2 = 0 ORDER BY 1,2;
-- With PHVs, partitionwise join selected but no join pushdown
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
SELECT t1.c1, t1.phv, t2.c2, t2.phv
FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN
(SELECT 't2_phv' phv, * FROM fprt2 WHERE c2 % 2 = 0) t2 ON (t1.c1 = t2.c2)
ORDER BY t1.c1, t2.c2;
RESET enable_partitionwise_join;
-- FDW-445: Support enable_join_pushdown option at server level and table level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'abc11');
-- Test the option at server level.
ALTER SERVER mongo_server OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- Test the option with outer rel.
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- Test the option with inner rel.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT t1.c1, t2.c2
FROM f_test_tbl3 t1 JOIN f_test_tbl4 t2 ON (t1.c1 = t2.c8) ORDER BY 1, 2;
-- FDW-558: Test mongo_fdw.enable_join_pushdown GUC.
-- Negative testing for GUC value.
SET mongo_fdw.enable_join_pushdown to 'abc';
-- Check default value. Should be ON.
SHOW mongo_fdw.enable_join_pushdown;
-- Join pushdown should happen as the GUC enable_join_pushdown is true.
ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
--Disable the GUC enable_join_pushdown.
SET mongo_fdw.enable_join_pushdown to false;
-- Join pushdown shouldn't happen as the GUC enable_join_pushdown is false.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
-- Enable the GUC and table level option is set to false, should not pushdown.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
SET mongo_fdw.enable_join_pushdown to true;
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT d.c1, e.c8
FROM f_test_tbl2 d JOIN f_test_tbl1 e ON (d.c1 = e.c8) ORDER BY 1, 2;
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
SET mongo_fdw.enable_join_pushdown to true;
SET mongo_fdw.enable_order_by_pushdown to true;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true');
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- One table level option is OFF. Shouldn't pushdown ORDER BY.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
-- When enable_join_pushdown option is disabled. Shouldn't pushdown join and
-- hence, ORDER BY too.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'false');
ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'false');
EXPLAIN (COSTS OFF)
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1 ASC NULLS FIRST, 3 ASC NULLS FIRST;
-- FDW-721: Fix ORDER BY pushdown on the column of inner relation
CREATE FOREIGN TABLE fdw721_tbl1 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl1');
CREATE FOREIGN TABLE fdw721_tbl2 (_id NAME, c1 INT, c2 INT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'tbl2');
INSERT INTO fdw721_tbl1 VALUES(0, 1, 1);
INSERT INTO fdw721_tbl1 VALUES(0, 2, 2);
INSERT INTO fdw721_tbl1 VALUES(0, 3, 3);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 4);
INSERT INTO fdw721_tbl2 VALUES(0, 1, 5);
INSERT INTO fdw721_tbl2 VALUES(0, 2, 6);
SELECT t1.c1, t1.c2, t2.c1, t2.c2 FROM fdw721_tbl1 t1 LEFT JOIN fdw721_tbl2 t2
ON (t1.c1 = t2.c1) ORDER BY 4 ASC NULLS FIRST;
DELETE FROM f_test_tbl1 WHERE c8 IS NULL;
DELETE FROM f_test_tbl1 WHERE c8 = 60;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DELETE FROM f_test_tbl2 WHERE c1 = 50;
DELETE FROM fdw721_tbl1;
DELETE FROM fdw721_tbl2;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP TABLE l_test_tbl1;
DROP FOREIGN TABLE ftprt1_p1;
DROP FOREIGN TABLE ftprt1_p2;
DROP FOREIGN TABLE ftprt2_p1;
DROP FOREIGN TABLE ftprt2_p2;
DROP FOREIGN TABLE fdw721_tbl1;
DROP FOREIGN TABLE fdw721_tbl2;
DROP TABLE IF EXISTS fprt1;
DROP TABLE IF EXISTS fprt2;
DROP USER MAPPING FOR public SERVER mongo_server1;
DROP SERVER mongo_server1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/limit_offset_pushdown.sql 0000664 0000000 0000000 00000012574 15066665201 0022240 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress,
-- mongo_fdw_regress1 and mongo_fdw_regress2 databases on MongoDB with all
-- permission for MONGO_USER_NAME user with MONGO_PASS password and ran
-- mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
CREATE FOREIGN TABLE fdw131_t1 (_id NAME, c1 INTEGER, c2 TEXT, c3 TEXT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1;
-- LIMIT/OFFSET pushdown.
-- Limit with Offset should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
-- If ORDER BY is not pushable then limit/Offset shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 LIMIT 2 OFFSET 2;
-- With ORDER BY pushdown disabled, limit shouldn't get pushdown.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 2 OFFSET 2;
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Only limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 DESC NULLS LAST LIMIT 3;
-- Expression in limit clause. Should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 DESC NULLS LAST LIMIT round(3.2) OFFSET 2;
-- Only Offset without limit should get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST OFFSET 2;
-- Limit ALL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL;
-- Limit ALL with OFFSET
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT ALL OFFSET 1;
-- Limit NULL
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 3 ASC NULLS FIRST LIMIT NULL OFFSET 2;
-- Limit 0 and Offset 0
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT 0 OFFSET 0;
-- Offset NULL.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 ASC NULLS FIRST LIMIT 5 OFFSET NULL;
-- Limit with placeholder. Shouldn't get pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 2 LIMIT (SELECT COUNT(*) FROM fdw131_t1);
-- Limit with expression, shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (10 - (SELECT COUNT(*) FROM fdw131_t1));
-- Limit with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1;
-- Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST OFFSET -2;
-- Limit/Offset with -ve value. Shouldn't pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
-- Should throw an error.
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT -1 OFFSET -2;
-- Limit with expression evaluating to -ve value.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
SELECT c1, c2, c3 FROM fdw131_t1 ORDER BY 1 ASC NULLS FIRST LIMIT (1 - (SELECT COUNT(*) FROM fdw131_t1));
DROP FOREIGN TABLE fdw131_t1;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/pushdown.sql 0000664 0000000 0000000 00000035465 15066665201 0017500 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR(9), c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id name, c1 INTEGER, c2 VARCHAR(14), c3 VARCHAR(13))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE f_test_tbl3 (_id name, name TEXT, marks FLOAT ARRAY, pass BOOLEAN)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl3');
-- Inserts some values in mongo_test collection.
INSERT INTO f_mongo_test VALUES ('0', 1, 'One');
INSERT INTO f_mongo_test VALUES ('0', 2, 'Two');
INSERT INTO f_mongo_test VALUES ('0', 3, 'Three');
SET datestyle TO ISO;
-- Sample data
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1;
-- WHERE clause pushdown
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e
WHERE c6 IN (1600, 2450)
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e
WHERE c6 IN (1600, 2450)
ORDER BY c1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 = 1500
ORDER BY c1 DESC NULLS LAST;
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 = 1500
ORDER BY c1 DESC NULLS LAST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 BETWEEN 1000 AND 4000
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c6 BETWEEN 1000 AND 4000
ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e
WHERE c4 IS NOT NULL
ORDER BY c1;
SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e
WHERE c4 IS NOT NULL
ORDER BY c1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17'
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17'
ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c2 IN ('EMP6', 'EMP12', 'EMP5')
ORDER BY c1;
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c2 IN ('EMP6', 'EMP12', 'EMP5')
ORDER BY c1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'SALESMAN'
ORDER BY c1;
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'SALESMAN'
ORDER BY c1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'MANA%'
ORDER BY c1;
SELECT c1, c2, c6, c8 FROM f_test_tbl1 e
WHERE c3 LIKE 'MANA%'
ORDER BY c1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a FROM f_mongo_test
WHERE a%2 = 1
ORDER BY a ASC NULLS FIRST;
SELECT a FROM f_mongo_test
WHERE a%2 = 1
ORDER BY a ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT a, b FROM f_mongo_test
WHERE a >= 1 AND b LIKE '%O%'
ORDER BY a;
SELECT a, b FROM f_mongo_test
WHERE a >= 1 AND b LIKE '%O%'
ORDER BY a;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17' AND c2 IN ('EMP1', 'EMP5', 'EMP10') AND c1 = 100
ORDER BY c1;
SELECT c1, c2, c5 FROM f_test_tbl1 e
WHERE c5 <= '1980-12-17' AND c2 IN ('EMP1', 'EMP5', 'EMP10') AND c1 = 100
ORDER BY c1;
-- The ORDER BY clause shouldn't push-down due to explicit COLLATE.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 = 'EMP10'
ORDER BY c2 COLLATE "en_US" DESC NULLS LAST;
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 = 'EMP10'
ORDER BY c2 COLLATE "en_US" DESC NULLS LAST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 < 'EMP10'
ORDER BY c2 DESC NULLS LAST;
SELECT c1, c2 FROM f_test_tbl1
WHERE c2 < 'EMP10'
ORDER BY c2 DESC NULLS LAST;
-- Should push down if two columns of same table are
-- involved in single WHERE clause operator expression.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4, c7, c8 FROM f_test_tbl1
WHERE c1 < c4 AND c7 < c8
ORDER BY c1;
SELECT c1, c4, c7, c8 FROM f_test_tbl1
WHERE c1 < c4 AND c7 < c8
ORDER BY c1;
-- With ORDER BY pushdown disabled.
SET mongo_fdw.enable_order_by_pushdown TO OFF;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
SET mongo_fdw.enable_order_by_pushdown TO ON;
-- Nested operator expression in WHERE clause. Should pushdown.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > FALSE
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > FALSE
ORDER BY c1 ASC NULLS FIRST;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > 0::BOOLEAN
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c2 FROM f_test_tbl1
WHERE (c1 > 1000) > 0::BOOLEAN
ORDER BY c1 ASC NULLS FIRST;
-- Shouldn't push down operators where the constant is an array.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, marks FROM f_test_tbl3
WHERE marks = ARRAY[23::FLOAT, 24::FLOAT]
ORDER BY name;
SELECT name, marks FROM f_test_tbl3
WHERE marks = ARRAY[23::FLOAT, 24::FLOAT]
ORDER BY name;
-- Pushdown in prepared statement.
PREPARE pre_stmt_f_mongo_test(int) AS
SELECT b FROM f_mongo_test WHERE a = $1 ORDER BY b;
EXPLAIN (VERBOSE, COSTS FALSE)
EXECUTE pre_stmt_f_mongo_test(1);
EXECUTE pre_stmt_f_mongo_test(1);
EXPLAIN (VERBOSE, COSTS FALSE)
EXECUTE pre_stmt_f_mongo_test(2);
EXECUTE pre_stmt_f_mongo_test(2);
-- FDW-297: Only operator expressions should be pushed down in WHERE clause.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, marks FROM f_test_tbl3
WHERE pass = true
ORDER BY name DESC NULLS LAST;
SELECT name, marks FROM f_test_tbl3
WHERE pass = true
ORDER BY name DESC NULLS LAST;
-- INSERT NULL values and check behaviour.
INSERT INTO f_test_tbl2 VALUES ('0', NULL, NULL, NULL);
-- Should pushdown and shouldn't result row with NULL VALUES.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c1 < 1;
SELECT c1 FROM f_test_tbl2 WHERE c1 < 1;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c2 = c3;
SELECT c1 FROM f_test_tbl2 WHERE c2 = c3;
-- Test with IS NULL, shouldn't push down
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL;
SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL;
-- FDW-134: Test with number of columns more than 32
CREATE FOREIGN TABLE f_test_large (_id int,
a01 int, a02 int, a03 int, a04 int, a05 int, a06 int, a07 int, a08 int, a09 int, a10 int,
a11 int, a12 int, a13 int, a14 int, a15 int, a16 int, a17 int, a18 int, a19 int, a20 int,
a21 int, a22 int, a23 int, a24 int, a25 int, a26 int, a27 int, a28 int, a29 int, a30 int,
a31 int, a32 int, a33 int, a34 int, a35 int)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test_large');
-- Shouldn't pushdown ORDERBY clause due to exceeded number of path keys limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST, a33 ASC NULLS FIRST, a34 DESC NULLS LAST, a35 ASC NULLS FIRST;
-- Should pushdown ORDERBY clause because number of path keys are in limit.
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY
a01 ASC NULLS FIRST, a02 ASC NULLS FIRST, a03 ASC NULLS FIRST, a04 ASC NULLS FIRST, a05 ASC NULLS FIRST,
a06 ASC NULLS FIRST, a07 ASC NULLS FIRST, a08 ASC NULLS FIRST, a09 ASC NULLS FIRST, a10 ASC NULLS FIRST,
a11 ASC NULLS FIRST, a12 ASC NULLS FIRST, a13 ASC NULLS FIRST, a14 ASC NULLS FIRST, a15 ASC NULLS FIRST,
a16 ASC NULLS FIRST, a17 ASC NULLS FIRST, a18 ASC NULLS FIRST, a19 ASC NULLS FIRST, a20 ASC NULLS FIRST,
a21 ASC NULLS FIRST, a22 ASC NULLS FIRST, a23 ASC NULLS FIRST, a24 ASC NULLS FIRST, a25 ASC NULLS FIRST,
a26 ASC NULLS FIRST, a27 ASC NULLS FIRST, a28 ASC NULLS FIRST, a29 ASC NULLS FIRST, a30 ASC NULLS FIRST,
a31 ASC NULLS FIRST, a32 ASC NULLS FIRST;
-- FDW-564: Test ORDER BY with user defined operators. Create the operator
-- family required for the test.
CREATE OPERATOR PUBLIC.<^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4EQ
);
CREATE OPERATOR PUBLIC.=^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4LT
);
CREATE OPERATOR PUBLIC.>^ (
LEFTARG = INT4,
RIGHTARG = INT4,
PROCEDURE = INT4GT
);
CREATE OPERATOR FAMILY my_op_family USING btree;
CREATE FUNCTION MY_OP_CMP(A INT, B INT) RETURNS INT AS
$$ BEGIN RETURN BTINT4CMP(A, B); END $$ LANGUAGE PLPGSQL;
CREATE OPERATOR CLASS my_op_class FOR TYPE INT USING btree FAMILY my_op_family AS
OPERATOR 1 PUBLIC.<^,
OPERATOR 3 PUBLIC.=^,
OPERATOR 5 PUBLIC.>^,
FUNCTION 1 my_op_cmp(INT, INT);
-- FDW-564: User defined operators are not pushed down.
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT * FROM f_mongo_test ORDER BY a USING OPERATOR(public.<^);
EXPLAIN (COSTS FALSE, VERBOSE)
SELECT MIN(a) FROM f_mongo_test GROUP BY b ORDER BY 1 USING OPERATOR(public.<^);
-- FDW-589: Test enable_order_by_pushdown option at server and table level.
-- Test the option at server level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'abc11');
ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
-- Test that setting option at table level does not affect the setting at
-- server level.
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'false');
-- Test the option at table level.
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'false');
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
SELECT c1, c4 FROM f_test_tbl1
WHERE c1 > c4
ORDER BY c1 ASC NULLS FIRST;
ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true');
ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true');
-- FDW-631: Test pushdown of boolean expression
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, pass FROM f_test_tbl3 WHERE pass = false ORDER BY name;
SELECT name, pass FROM f_test_tbl3 WHERE pass = false ORDER BY name;
EXPLAIN (VERBOSE, COSTS FALSE)
SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name;
SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name;
-- FDW-729: print query pipeline to find remote query
SET mongo_fdw.log_remote_query TO true;
SET client_min_messages TO log;
SELECT c1, c2, c6 FROM f_test_tbl1 e
WHERE c6 > 3000
ORDER BY c1 ASC NULLS FIRST;
RESET client_min_messages;
RESET mongo_fdw.log_remote_query;
-- Cleanup
DELETE FROM f_mongo_test WHERE a != 0;
DELETE FROM f_test_tbl2 WHERE c1 IS NULL;
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE f_test_tbl3;
DROP FOREIGN TABLE f_test_large;
DROP OPERATOR CLASS my_op_class USING btree;
DROP FUNCTION my_op_cmp(a INT, b INT);
DROP OPERATOR FAMILY my_op_family USING btree;
DROP OPERATOR public.>^(INT, INT);
DROP OPERATOR public.=^(INT, INT);
DROP OPERATOR public.<^(INT, INT);
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/select.sql 0000664 0000000 0000000 00000042036 15066665201 0017100 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Check version
SELECT mongo_fdw_version();
-- Create foreign tables
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
CREATE FOREIGN TABLE f_test_tbl1 (_id NAME, c1 INTEGER, c2 VARCHAR(10), c3 CHAR(9),c4 INTEGER, c5 pg_catalog.Date, c6 DECIMAL, c7 INTEGER, c8 INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl1');
CREATE FOREIGN TABLE f_test_tbl2 (_id NAME, c1 INTEGER, c2 VARCHAR(14), c3 VARCHAR(13))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl2');
CREATE FOREIGN TABLE countries (_id NAME, name VARCHAR, population INTEGER, capital VARCHAR, hdi FLOAT)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE country_elections (_id NAME, "lastElections.type" VARCHAR, "lastElections.date" pg_catalog.TIMESTAMP)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE main_exports (_id NAME, "mainExports" TEXT[] )
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries');
CREATE FOREIGN TABLE test_json ( __doc json)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_jsonb ( __doc jsonb)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_text ( __doc text)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE test_varchar ( __doc varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'warehouse');
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
CREATE FOREIGN TABLE f_test_tbl4 (_id NAME, a NUMERIC(12, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE f_test_tbl5 (_id NAME, a BOOLEAN)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE f_test_tbl6 (_id NAME, a INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl5');
CREATE FOREIGN TABLE f_test_tbl7 (_id NAME, a INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test_tbl4');
CREATE FOREIGN TABLE testlog (_id NAME, log VARCHAR, "logMeta.logMac" VARCHAR, "logMeta.nestMore.level" INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'testlog');
CREATE FOREIGN TABLE testdevice (_id NAME, name VARCHAR, mac VARCHAR, level INTEGER)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'testdevice');
SET datestyle TO ISO;
-- Retrieve data from foreign table using SELECT statement.
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
ORDER BY c1 DESC, c8;
SELECT DISTINCT c8 FROM f_test_tbl1 ORDER BY 1;
SELECT c2 AS "Employee Name" FROM f_test_tbl1 ORDER BY c2 COLLATE "C";
SELECT c8, c6, c7 FROM f_test_tbl1 ORDER BY 1, 2, 3;
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c1 = 100 ORDER BY 1;
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c1 = 100 OR c1 = 700 ORDER BY 1;
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c3 like 'SALESMAN' ORDER BY 1;
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c1 IN (100, 700) ORDER BY 1;
SELECT c1, c2, c3 FROM f_test_tbl1 WHERE c1 NOT IN (100, 700) ORDER BY 1 LIMIT 5;
SELECT c1, c2, c8 FROM f_test_tbl1 WHERE c8 BETWEEN 10 AND 20 ORDER BY 1;
SELECT c1, c2, c6 FROM f_test_tbl1 ORDER BY 1 OFFSET 5;
-- Retrieve data from foreign table using group by clause.
SELECT c8 "Department", COUNT(c1) "Total Employees" FROM f_test_tbl1
GROUP BY c8 ORDER BY c8;
SELECT c8, SUM(c6) FROM f_test_tbl1
GROUP BY c8 HAVING c8 IN (10, 30) ORDER BY c8;
SELECT c8, SUM(c6) FROM f_test_tbl1
GROUP BY c8 HAVING SUM(c6) > 9400 ORDER BY c8;
-- Retrieve data from foreign table using sub-queries.
SELECT c1, c2, c6 FROM f_test_tbl1
WHERE c8 <> ALL (SELECT c1 FROM f_test_tbl2 WHERE c1 IN (10, 30, 40))
ORDER BY c1;
SELECT c1, c2, c3 FROM f_test_tbl2
WHERE EXISTS (SELECT 1 FROM f_test_tbl1 WHERE f_test_tbl2.c1 = f_test_tbl1.c8)
ORDER BY 1, 2;
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1
WHERE c8 NOT IN (SELECT c1 FROM f_test_tbl2) ORDER BY c1;
-- Retrieve data from foreign table using UNION operator.
SELECT c1, c2 FROM f_test_tbl2 UNION
SELECT c1, c2 FROM f_test_tbl1 ORDER BY c1;
SELECT c1, c2 FROM f_test_tbl2 UNION ALL
SELECT c1, c2 FROM f_test_tbl1 ORDER BY c1;
-- Retrieve data from foreign table using INTERSECT operator.
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 800 INTERSECT
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 400 ORDER BY c1;
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 800 INTERSECT ALL
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 >= 400 ORDER BY c1;
-- Retrieve data from foreign table using EXCEPT operator.
SELECT c1, c2 FROM f_test_tbl1 EXCEPT
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 > 900 ORDER BY c1;
SELECT c1, c2 FROM f_test_tbl1 EXCEPT ALL
SELECT c1, c2 FROM f_test_tbl1 WHERE c1 > 900 ORDER BY c1;
-- Retrieve data from foreign table using CTE (with clause).
WITH
with_qry AS (SELECT c1, c2, c3 FROM f_test_tbl2)
SELECT e.c2, e.c6, w.c1, w.c2 FROM f_test_tbl1 e, with_qry w
WHERE e.c8 = w.c1 ORDER BY e.c8, e.c2 COLLATE "C";
WITH
test_tbl2_costs AS (SELECT d.c2, SUM(c6) test_tbl2_total FROM f_test_tbl1 e, f_test_tbl2 d
WHERE e.c8 = d.c1 GROUP BY 1),
avg_cost AS (SELECT SUM(test_tbl2_total)/COUNT(*) avg FROM test_tbl2_costs)
SELECT * FROM test_tbl2_costs
WHERE test_tbl2_total > (SELECT avg FROM avg_cost) ORDER BY c2 COLLATE "C";
-- Retrieve data from foreign table using window clause.
SELECT c8, c1, c6, AVG(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
ORDER BY c8, c1;
SELECT c8, c1, c6, COUNT(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
WHERE c8 IN (10, 30, 40, 50, 60, 70) ORDER BY c8, c1;
SELECT c8, c1, c6, SUM(c6) OVER (PARTITION BY c8) FROM f_test_tbl1
ORDER BY c8, c1;
-- Views
CREATE VIEW smpl_vw AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1;
SELECT * FROM smpl_vw ORDER BY 1;
CREATE VIEW comp_vw (s1, s2, s3, s6, s7, s8, d2) AS
SELECT s.c1, s.c2, s.c3, s.c6, s.c7, s.c8, d.c2
FROM f_test_tbl2 d, f_test_tbl1 s WHERE d.c1 = s.c8 AND d.c1 = 10
ORDER BY s.c1;
SELECT * FROM comp_vw ORDER BY 1;
CREATE TEMPORARY VIEW temp_vw AS
SELECT c1, c2, c3 FROM f_test_tbl2;
SELECT * FROM temp_vw ORDER BY 1, 2;
CREATE VIEW mul_tbl_view AS
SELECT d.c1 dc1, d.c2 dc2, e.c1 ec1, e.c2 ec2, e.c6 ec6
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY d.c1;
SELECT * FROM mul_tbl_view ORDER BY 1, 2, 3;
-- Foreign-Foreign table joins
-- CROSS JOIN.
SELECT f_test_tbl2.c2, f_test_tbl1.c2
FROM f_test_tbl2 CROSS JOIN f_test_tbl1 ORDER BY 1, 2;
-- INNER JOIN.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d, f_test_tbl1 e WHERE d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- OUTER JOINS.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d FULL OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- Local-Foreign table joins.
CREATE TABLE l_test_tbl1 AS
SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1;
CREATE TABLE l_test_tbl2 AS
SELECT c1, c2, c3 FROM f_test_tbl2;
-- CROSS JOIN.
SELECT f_test_tbl2.c2, l_test_tbl1.c2 FROM f_test_tbl2 CROSS JOIN l_test_tbl1 ORDER BY 1, 2;
-- INNER JOIN.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM l_test_tbl2 d, f_test_tbl1 e WHERE d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d INNER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- OUTER JOINS.
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d LEFT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d RIGHT OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8
FROM f_test_tbl2 d FULL OUTER JOIN l_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
-- Retrieve complex data containing Sub-fields, dates, Arrays
SELECT * FROM countries ORDER BY _id;
SELECT * FROM country_elections ORDER BY _id;
SELECT * FROM main_exports ORDER BY _id;
-- Retrieve complex data containing Json objects (__doc tests)
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_json, json_each_text(test_json.__doc) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_jsonb, jsonb_each_text(test_jsonb.__doc) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_text, json_each_text(test_text.__doc::json) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
SELECT json_data.key AS key1, json_data.value AS value1
FROM test_varchar, json_each_text(test_varchar.__doc::json) AS json_data
WHERE key NOT IN ('_id') ORDER BY json_data.key COLLATE "C";
-- Inserts some values in mongo_test collection.
INSERT INTO f_mongo_test VALUES ('0', 1, 'One');
INSERT INTO f_mongo_test VALUES ('0', 2, 'Two');
INSERT INTO f_mongo_test VALUES ('0', 3, 'Three');
INSERT INTO f_mongo_test VALUES ('0', 4, 'Four');
INSERT INTO f_mongo_test VALUES ('0', 5, 'Five');
INSERT INTO f_mongo_test VALUES ('0', 6, 'Six');
INSERT INTO f_mongo_test VALUES ('0', 7, 'Seven');
INSERT INTO f_mongo_test VALUES ('0', 8, 'Eight');
INSERT INTO f_mongo_test VALUES ('0', 9, 'Nine');
INSERT INTO f_mongo_test VALUES ('0', 10, 'Ten');
-- Retrieve Data From foreign tables in functions.
CREATE OR REPLACE FUNCTION test_param_where() RETURNS void AS $$
DECLARE
n varchar;
BEGIN
FOR x IN 1..9 LOOP
SELECT b INTO n FROM f_mongo_test WHERE a = x;
RAISE NOTICE 'Found number %', n;
END LOOP;
return;
END
$$ LANGUAGE plpgsql;
SELECT test_param_where();
-- FDW-103: Parameter expression should work correctly with WHERE clause.
SELECT a, b FROM f_mongo_test WHERE a = (SELECT 2) ORDER BY a;
SELECT a, b FROM f_mongo_test WHERE b = (SELECT 'Seven'::text) ORDER BY a;
-- Create local table and load data into it.
CREATE TABLE l_mongo_test AS SELECT a, b FROM f_mongo_test;
-- Check correlated query.
SELECT a, b FROM l_mongo_test lt
WHERE lt.b = (SELECT b FROM f_mongo_test ft WHERE lt.b = ft.b)
ORDER BY a;
SELECT a, b FROM l_mongo_test lt
WHERE lt.a = (SELECT a FROM f_mongo_test ft WHERE lt.a = ft.a)
ORDER BY a;
SELECT c1, c8 FROM f_test_tbl1 ft1
WHERE ft1.c8 = (SELECT c1 FROM f_test_tbl2 ft2 WHERE ft1.c8 = ft2.c1)
ORDER BY c1 LIMIT 2;
-- FDW-197: Casting target list should give correct result.
SELECT a::float FROM f_mongo_test ORDER BY a LIMIT 2;
SELECT a::boolean FROM f_mongo_test ORDER BY a LIMIT 2;
SELECT a, b::varchar FROM f_mongo_test ORDER BY a LIMIT 3;
SELECT a::float, b::varchar FROM f_mongo_test ORDER BY a LIMIT 2;
SELECT a::real, b::char(20) FROM f_mongo_test ORDER BY a LIMIT 2;
SELECT c1, c2::text FROM f_test_tbl1 ORDER BY c1 LIMIT 2;
SELECT a, LENGTH(b) FROM f_mongo_test ORDER BY 1 LIMIT 2;
SELECT t1.c6::float, t1.c6::int, t1.c5::timestamptz, t1.c3::text, t2.c1::numeric, t2.c3
FROM f_test_tbl1 t1, f_test_tbl2 t2 WHERE t1.c8 = t2.c1
ORDER BY t2.c1, t1.c6 LIMIT 5;
SELECT SUM(a::float), SUM(a % 2), a % 2 AS "a % 2"FROM f_mongo_test
GROUP BY a % 2 ORDER BY 2;
SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6
FROM f_test_tbl1 ORDER BY c1 LIMIT 5;
-- FDW-249; LEFT JOIN LATERAL should not crash
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.a, t1.b, t3.a, t1_a FROM f_mongo_test t1 LEFT JOIN LATERAL (
SELECT t2.a, t1.a AS t1_a FROM f_mongo_test t2) t3 ON t1.a = t3.a ORDER BY 1 ASC NULLS FIRST;
SELECT t1.a, t1.b, t3.a, t1_a FROM f_mongo_test t1 LEFT JOIN LATERAL (
SELECT t2.a, t1.a AS t1_a FROM f_mongo_test t2) t3 ON t1.a = t3.a ORDER BY 1 ASC NULLS FIRST;
SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 INNER JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
SELECT t1.c1, t3.c1, t3.t1_c8 FROM l_test_tbl1 t1 LEFT JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
SELECT c1, c2, (SELECT r FROM (SELECT c1 AS c1) x, LATERAL (SELECT c1 AS r) y)
FROM f_test_tbl1 ORDER BY 1, 2, 3;
-- LATERAL JOIN with RIGHT should throw error
SELECT t1.c1, t3.c1, t3.t1_c8 FROM f_test_tbl1 t1 RIGHT JOIN LATERAL (
SELECT t2.c1, t1.c8 AS t1_c8 FROM f_test_tbl2 t2) t3 ON t3.c1 = t3.t1_c8
ORDER BY 1, 2, 3;
-- FDW-262: Should throw an error when we select system attribute.
SELECT xmin FROM f_test_tbl1;
SELECT ctid, xmax, tableoid FROM f_test_tbl1;
SELECT xmax, c1 FROM f_test_tbl1;
SELECT count(tableoid) FROM f_test_tbl1;
-- FDW-391: Support whole-row reference.
EXPLAIN (VERBOSE, COSTS OFF)
SELECT t1.c2, t1 FROM f_test_tbl1 t1
WHERE c1 = 100 ORDER BY 1;
-- Force hash-join for consistent result.
SET enable_mergejoin TO off;
SET enable_nestloop TO off;
EXPLAIN (VERBOSE, COSTS OFF)
SELECT d, d.c2, e.c1, e
FROM f_test_tbl2 d LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = e.c8 ORDER BY 1, 3;
RESET enable_mergejoin;
RESET enable_nestloop;
-- FDW-427: The numeric value should display correctly as per precision and
-- scale defined.
SELECT c1 FROM f_test5 ORDER BY 1;
-- Number with the required precision.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(8, 6))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
-- Number with less scale. Should round-off the scale.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(6, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
-- Number only with precision.
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
-- Number with improper precision and scale,
-- resulting in error "numeric field overflow".
DROP FOREIGN TABLE f_test5;
CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(3, 2))
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test5');
SELECT c1 FROM f_test5 ORDER BY 1;
-- FDW-418: Resolve data compatibility.
SELECT a FROM f_test_tbl4 ORDER BY 1;
SELECT a FROM f_test_tbl5 ORDER BY 1;
SELECT a FROM f_test_tbl6 ORDER BY 1;
SELECT a FROM f_test_tbl7 ORDER BY 1;
-- FDW-529: Fix server crash caused due to missed handling of Param node for
-- comparison expressions while preparing query filter.
CREATE OR REPLACE FUNCTION fdw529_test_param_where() RETURNS int AS $$
DECLARE
val1 INT := 5;
val2 INT := 10;
cnt INT;
BEGIN
SELECT count(*) INTO cnt FROM f_mongo_test WHERE a > val1 AND a < val2;
RETURN cnt;
END
$$ LANGUAGE plpgsql;
SELECT fdw529_test_param_where();
SELECT fdw529_test_param_where();
SELECT fdw529_test_param_where();
SELECT fdw529_test_param_where();
SELECT fdw529_test_param_where();
-- This should not crash
SELECT fdw529_test_param_where();
-- FDW-669: Fix issue join pushdown doesn't return a result for join condition
-- on sub-column. This has been fixed by omitting a dot (".") from variables
-- used (declared by $let field) to form the MongoDB query pipeline.
SELECT * FROM testlog t INNER JOIN testdevice d
ON d.level = t."logMeta.nestMore.level";
-- Cleanup
DELETE FROM f_mongo_test WHERE a != 0;
DROP TABLE l_test_tbl1;
DROP TABLE l_test_tbl2;
DROP TABLE l_mongo_test;
DROP VIEW smpl_vw;
DROP VIEW comp_vw;
DROP VIEW temp_vw;
DROP VIEW mul_tbl_view;
DROP FUNCTION test_param_where();
DROP FUNCTION fdw529_test_param_where();
DROP FOREIGN TABLE f_mongo_test;
DROP FOREIGN TABLE f_test_tbl1;
DROP FOREIGN TABLE f_test_tbl2;
DROP FOREIGN TABLE countries;
DROP FOREIGN TABLE country_elections;
DROP FOREIGN TABLE main_exports;
DROP FOREIGN TABLE test_json;
DROP FOREIGN TABLE test_jsonb;
DROP FOREIGN TABLE test_text;
DROP FOREIGN TABLE test_varchar;
DROP FOREIGN TABLE f_test5;
DROP FOREIGN TABLE f_test_tbl4;
DROP FOREIGN TABLE f_test_tbl5;
DROP FOREIGN TABLE f_test_tbl6;
DROP FOREIGN TABLE f_test_tbl7;
DROP FOREIGN TABLE testlog;
DROP FOREIGN TABLE testdevice;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;
mongo_fdw-REL-5_5_3/sql/server_options.sql 0000664 0000000 0000000 00000010750 15066665201 0020700 0 ustar 00root root 0000000 0000000 \set MONGO_HOST `echo \'"$MONGO_HOST"\'`
\set MONGO_PORT `echo \'"$MONGO_PORT"\'`
\set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'`
\set MONGO_PASS `echo \'"$MONGO_PWD"\'`
-- Before running this file user must create database mongo_fdw_regress on
-- MongoDB with all permission for MONGO_USER_NAME user with MONGO_PASS
-- password and ran mongodb_init.sh file to load collections.
\c contrib_regression
CREATE EXTENSION IF NOT EXISTS mongo_fdw;
CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port :MONGO_PORT);
CREATE USER MAPPING FOR public SERVER mongo_server;
-- Port outside ushort range. Error.
CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw
OPTIONS (address :MONGO_HOST, port '65537');
ALTER SERVER mongo_server OPTIONS (SET port '65537');
-- Validate extension, server and mapping details
CREATE OR REPLACE FUNCTION show_details(host TEXT, port TEXT, uid TEXT, pwd TEXT) RETURNS int AS $$
DECLARE
ext TEXT;
srv TEXT;
sopts TEXT;
uopts TEXT;
BEGIN
SELECT e.fdwname, srvname, array_to_string(s.srvoptions, ','), array_to_string(u.umoptions, ',')
INTO ext, srv, sopts, uopts
FROM pg_foreign_data_wrapper e LEFT JOIN pg_foreign_server s ON e.oid = s.srvfdw LEFT JOIN pg_user_mapping u ON s.oid = u.umserver
WHERE e.fdwname = 'mongo_fdw'
ORDER BY 1, 2, 3, 4;
raise notice 'Extension : %', ext;
raise notice 'Server : %', srv;
IF strpos(sopts, host) <> 0 AND strpos(sopts, port) <> 0 THEN
raise notice 'Server_Options : matched';
END IF;
IF strpos(uopts, uid) <> 0 AND strpos(uopts, pwd) <> 0 THEN
raise notice 'User_Mapping_Options : matched';
END IF;
return 1;
END;
$$ language plpgsql;
SELECT show_details(:MONGO_HOST, :MONGO_PORT, :MONGO_USER_NAME, :MONGO_PASS);
-- Create foreign tables and perform basic SQL operations
CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar)
SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
INSERT INTO f_mongo_test VALUES ('0', 2, 'mongo_test insert');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
UPDATE f_mongo_test SET b = 'mongo_test update' WHERE a = 2;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
DELETE FROM f_mongo_test WHERE a = 2;
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Test SSL option when MongoDB server running in non-SSL mode.
-- Set non-boolean value, should throw an error.
ALTER SERVER mongo_server OPTIONS (ssl '1');
ALTER SERVER mongo_server OPTIONS (ssl 'x');
-- Check for default value i.e. false
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Set 'true'.
ALTER SERVER mongo_server OPTIONS (ssl 'true');
-- Results into an error as MongoDB server is running in non-SSL mode.
\set VERBOSITY terse
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
\set VERBOSITY default
-- Switch back to 'false'.
ALTER SERVER mongo_server OPTIONS (SET ssl 'false');
-- Should now be successful.
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Alter server to add authentication_database option
ALTER SERVER mongo_server OPTIONS (ADD authentication_database 'NOT_EXIST_DB');
ALTER USER MAPPING FOR public SERVER mongo_server
OPTIONS (ADD username :MONGO_USER_NAME, password :MONGO_PASS);
-- Below query will fail with authentication error as user cannot be
-- authenticated against given authentication_database.
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Now changed to valid authentication_database so select query should work.
ALTER SERVER mongo_server
OPTIONS (SET authentication_database 'mongo_fdw_regress');
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
ALTER SERVER mongo_server
OPTIONS (DROP authentication_database);
ALTER USER MAPPING FOR public SERVER mongo_server
OPTIONS (DROP username, DROP password);
-- FDW-464: Support use_remote_estimate option at server level.
-- Check only boolean values are accepted.
ALTER SERVER mongo_server OPTIONS (ADD use_remote_estimate 'abc11');
-- Check default behaviour. Should be 'false'.
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Enable remote estimation.
ALTER SERVER mongo_server OPTIONS (ADD use_remote_estimate 'true');
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Disable remote estimation.
ALTER SERVER mongo_server OPTIONS (SET use_remote_estimate 'false');
EXPLAIN(COSTS OFF)
SELECT a, b FROM f_mongo_test ORDER BY 1, 2;
-- Cleanup
DROP FOREIGN TABLE f_mongo_test;
DROP USER MAPPING FOR public SERVER mongo_server;
DROP SERVER mongo_server;
DROP EXTENSION mongo_fdw;