From 4261680135e62dd36e733fff5a4d71f5f5b599d2 Mon Sep 17 00:00:00 2001 From: Jason Petersen Date: Wed, 22 Jan 2014 18:04:04 -0700 Subject: [PATCH 001/239] Remove existing mongo-c-driver directory It'd have conflicted with the submodule. --- mongo-c-driver-v0.6/.astylerc | 4 - mongo-c-driver-v0.6/.gitignore | 26 - mongo-c-driver-v0.6/APACHE-2.0.txt | 202 -- mongo-c-driver-v0.6/HISTORY.md | 201 -- mongo-c-driver-v0.6/Makefile | 199 -- mongo-c-driver-v0.6/README.md | 66 - mongo-c-driver-v0.6/SConstruct | 297 --- mongo-c-driver-v0.6/check_int64.sh | 21 - .../docs/buildscripts/__init__.py | 0 mongo-c-driver-v0.6/docs/buildscripts/docs.py | 56 - mongo-c-driver-v0.6/docs/examples/example.c | 113 -- .../docs/source/sphinx/Makefile | 130 -- .../docs/source/sphinx/make.bat | 155 -- .../docs/source/sphinx/source/bson.rst | 218 --- .../docs/source/sphinx/source/building.rst | 233 --- .../docs/source/sphinx/source/conf.py | 216 --- .../docs/source/sphinx/source/connections.rst | 149 -- .../docs/source/sphinx/source/errors.rst | 73 - .../docs/source/sphinx/source/index.rst | 46 - .../docs/source/sphinx/source/tutorial.rst | 401 ---- .../source/sphinx/source/write_concern.rst | 106 - mongo-c-driver-v0.6/doxygenConfig | 316 --- mongo-c-driver-v0.6/runtests.sh | 44 - mongo-c-driver-v0.6/src/bson.c | 1068 ----------- mongo-c-driver-v0.6/src/bson.h | 1038 ---------- mongo-c-driver-v0.6/src/encoding.c | 167 -- mongo-c-driver-v0.6/src/encoding.h | 54 - mongo-c-driver-v0.6/src/env.h | 39 - mongo-c-driver-v0.6/src/env_posix.c | 165 -- mongo-c-driver-v0.6/src/env_standard.c | 168 -- mongo-c-driver-v0.6/src/env_win32.c | 178 -- mongo-c-driver-v0.6/src/gridfs.c | 712 ------- mongo-c-driver-v0.6/src/gridfs.h | 332 ---- mongo-c-driver-v0.6/src/md5.c | 381 ---- mongo-c-driver-v0.6/src/md5.h | 92 - mongo-c-driver-v0.6/src/mongo.c | 1705 ----------------- mongo-c-driver-v0.6/src/mongo.h | 824 -------- mongo-c-driver-v0.6/src/numbers.c | 127 -- mongo-c-driver-v0.6/test/auth_test.c | 29 - mongo-c-driver-v0.6/test/benchmark_test.c | 454 ----- .../test/bson_subobject_test.c | 50 - mongo-c-driver-v0.6/test/bson_test.c | 274 --- mongo-c-driver-v0.6/test/commands_test.c | 44 - mongo-c-driver-v0.6/test/count_delete_test.c | 64 - mongo-c-driver-v0.6/test/cpptest.cpp | 50 - mongo-c-driver-v0.6/test/cursors_test.c | 204 -- mongo-c-driver-v0.6/test/endian_swap_test.c | 31 - mongo-c-driver-v0.6/test/env_posix_test.c | 109 -- mongo-c-driver-v0.6/test/env_win32_test.c | 131 -- mongo-c-driver-v0.6/test/errors_test.c | 259 --- mongo-c-driver-v0.6/test/examples_test.c | 71 - mongo-c-driver-v0.6/test/functions_test.c | 113 -- mongo-c-driver-v0.6/test/gridfs_test.c | 263 --- mongo-c-driver-v0.6/test/helpers_test.c | 55 - mongo-c-driver-v0.6/test/json_test.c | 169 -- mongo-c-driver-v0.6/test/oid_test.c | 36 - .../test/platform/linux/timeouts.c | 37 - mongo-c-driver-v0.6/test/replica_set_test.c | 154 -- mongo-c-driver-v0.6/test/resize_test.c | 34 - mongo-c-driver-v0.6/test/simple_test.c | 159 -- mongo-c-driver-v0.6/test/sizes_test.c | 22 - mongo-c-driver-v0.6/test/test.h | 56 - mongo-c-driver-v0.6/test/update_test.c | 108 -- mongo-c-driver-v0.6/test/validate_test.c | 138 -- mongo-c-driver-v0.6/test/write_concern_test.c | 284 --- 65 files changed, 13720 deletions(-) delete mode 100644 mongo-c-driver-v0.6/.astylerc delete mode 100644 mongo-c-driver-v0.6/.gitignore delete mode 100644 mongo-c-driver-v0.6/APACHE-2.0.txt delete mode 100644 mongo-c-driver-v0.6/HISTORY.md delete mode 100644 mongo-c-driver-v0.6/Makefile delete mode 100644 mongo-c-driver-v0.6/README.md delete mode 100644 mongo-c-driver-v0.6/SConstruct delete mode 100755 mongo-c-driver-v0.6/check_int64.sh delete mode 100644 mongo-c-driver-v0.6/docs/buildscripts/__init__.py delete mode 100644 mongo-c-driver-v0.6/docs/buildscripts/docs.py delete mode 100644 mongo-c-driver-v0.6/docs/examples/example.c delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/Makefile delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/make.bat delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/bson.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/building.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/conf.py delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/connections.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/errors.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/index.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/tutorial.rst delete mode 100644 mongo-c-driver-v0.6/docs/source/sphinx/source/write_concern.rst delete mode 100644 mongo-c-driver-v0.6/doxygenConfig delete mode 100644 mongo-c-driver-v0.6/runtests.sh delete mode 100644 mongo-c-driver-v0.6/src/bson.c delete mode 100644 mongo-c-driver-v0.6/src/bson.h delete mode 100644 mongo-c-driver-v0.6/src/encoding.c delete mode 100644 mongo-c-driver-v0.6/src/encoding.h delete mode 100644 mongo-c-driver-v0.6/src/env.h delete mode 100644 mongo-c-driver-v0.6/src/env_posix.c delete mode 100644 mongo-c-driver-v0.6/src/env_standard.c delete mode 100644 mongo-c-driver-v0.6/src/env_win32.c delete mode 100644 mongo-c-driver-v0.6/src/gridfs.c delete mode 100644 mongo-c-driver-v0.6/src/gridfs.h delete mode 100644 mongo-c-driver-v0.6/src/md5.c delete mode 100644 mongo-c-driver-v0.6/src/md5.h delete mode 100644 mongo-c-driver-v0.6/src/mongo.c delete mode 100644 mongo-c-driver-v0.6/src/mongo.h delete mode 100644 mongo-c-driver-v0.6/src/numbers.c delete mode 100644 mongo-c-driver-v0.6/test/auth_test.c delete mode 100644 mongo-c-driver-v0.6/test/benchmark_test.c delete mode 100644 mongo-c-driver-v0.6/test/bson_subobject_test.c delete mode 100644 mongo-c-driver-v0.6/test/bson_test.c delete mode 100644 mongo-c-driver-v0.6/test/commands_test.c delete mode 100644 mongo-c-driver-v0.6/test/count_delete_test.c delete mode 100644 mongo-c-driver-v0.6/test/cpptest.cpp delete mode 100644 mongo-c-driver-v0.6/test/cursors_test.c delete mode 100644 mongo-c-driver-v0.6/test/endian_swap_test.c delete mode 100644 mongo-c-driver-v0.6/test/env_posix_test.c delete mode 100644 mongo-c-driver-v0.6/test/env_win32_test.c delete mode 100644 mongo-c-driver-v0.6/test/errors_test.c delete mode 100644 mongo-c-driver-v0.6/test/examples_test.c delete mode 100644 mongo-c-driver-v0.6/test/functions_test.c delete mode 100644 mongo-c-driver-v0.6/test/gridfs_test.c delete mode 100644 mongo-c-driver-v0.6/test/helpers_test.c delete mode 100644 mongo-c-driver-v0.6/test/json_test.c delete mode 100644 mongo-c-driver-v0.6/test/oid_test.c delete mode 100644 mongo-c-driver-v0.6/test/platform/linux/timeouts.c delete mode 100644 mongo-c-driver-v0.6/test/replica_set_test.c delete mode 100644 mongo-c-driver-v0.6/test/resize_test.c delete mode 100644 mongo-c-driver-v0.6/test/simple_test.c delete mode 100644 mongo-c-driver-v0.6/test/sizes_test.c delete mode 100644 mongo-c-driver-v0.6/test/test.h delete mode 100644 mongo-c-driver-v0.6/test/update_test.c delete mode 100644 mongo-c-driver-v0.6/test/validate_test.c delete mode 100644 mongo-c-driver-v0.6/test/write_concern_test.c diff --git a/mongo-c-driver-v0.6/.astylerc b/mongo-c-driver-v0.6/.astylerc deleted file mode 100644 index 9f3851e..0000000 --- a/mongo-c-driver-v0.6/.astylerc +++ /dev/null @@ -1,4 +0,0 @@ ---style=java ---pad-paren-in ---pad-paren-in ---align-pointer=name diff --git a/mongo-c-driver-v0.6/.gitignore b/mongo-c-driver-v0.6/.gitignore deleted file mode 100644 index ea672fa..0000000 --- a/mongo-c-driver-v0.6/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ - -*~ -*.o -*.os -*.obj -*.a -*.lib -*.so -*.dylib -.scon* -*.pyc -*.swp - -.DS_Store - -test_* -benchmark -benchmark.exe - -tags - -config.log - -docs/html -docs/source/sphinx/build -docs/source/doxygen diff --git a/mongo-c-driver-v0.6/APACHE-2.0.txt b/mongo-c-driver-v0.6/APACHE-2.0.txt deleted file mode 100644 index d645695..0000000 --- a/mongo-c-driver-v0.6/APACHE-2.0.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/mongo-c-driver-v0.6/HISTORY.md b/mongo-c-driver-v0.6/HISTORY.md deleted file mode 100644 index 9f1e9af..0000000 --- a/mongo-c-driver-v0.6/HISTORY.md +++ /dev/null @@ -1,201 +0,0 @@ -# MongoDB C Driver History - -## 0.6 -2012-6-3 -** API CHANGE ** - -Version 0.6 supports write concern. This involves a backward-breaking -API change, as the write functions now take an optional write_concern -object. - -The driver now also supports the MONGO_CONTINUE_ON_ERROR flag for -batch inserts. - -The new function prototypes are as follows: - -* int mongo_insert( mongo *conn, const char *ns, const bson *data, - mongo_write_concern *custom_write_concern ); - -* int mongo_insert_batch( mongo *conn, const char *ns, - const bson **data, int num, mongo_write_concern *custom_write_concern ); - -* int mongo_update( mongo *conn, const char *ns, const bson *cond, - const bson *op, int flags, mongo_write_concern *custom_write_concern, - int flags ); - -* int mongo_remove( mongo *conn, const char *ns, const bson *cond, - mongo_write_concern *custom_write_concern ); - -* Allow DBRefs (i.e., allows keys $ref, $id, and $db) -* Added mongo_create_capped_collection(). -* Fixed some bugs in the SCons and Makefile build scripts. -* Fixes for SCons and Makefile shared library install targets. -* Other minor bug fixes. - -## 0.5.2 -2012-5-4 - -* Validate collection and database names on insert. -* Validate insert limits using max BSON size. -* Support getaddrinfo and SO_RCVTIMEO and SO_SNDTIMEO on Windows. -* Store errno/WSAGetLastError() on errors. -* Various bug fixes and refactorings. -* Update error reporting docs. - -## 0.5.1 - -* Env for POSIX, WIN32, and standard C. -* Various bug fixes. - -## 0.5 -2012-3-31 - -* Separate cursor-specific errors into their own enum: mongo_cursor_error_t. -* Catch $err return on bad queries and store the result in conn->getlasterrorcode - and conn->getlasterrstr. -* On queries that return $err, set cursor->err to MONGO_CURSOR_QUERY_FAIL. -* When passing bad BSON to a cursor object, set cursor->err to MONGO_CURSOR_BSON_ERROR, - and store the specific BSON error on the conn->err field. -* Remove bson_copy_basic(). -* bson_copy() will copy finished bson objects only. -* bson_copy() returns BSON_OK on success and BSON_ERROR on failure. -* Added a Makefile for easy compile and install on Linux and OS X. -* Replica set connect fixes. - -## 0.4 - -THIS RELEASE INCLUDES NUMEROUS BACKWARD-BREAKING CHANGES. -These changes have been made for extensibility, consistency, -and ease of use. Please read the following release notes -carefully, and study the updated tutorial. - -API Principles: - -1. Present a consistent interface for all objects: connections, - cursors, bson objects, and bson iterators. -2. Require no knowledge of an object's implementation to use the API. -3. Allow users to allocate objects on the stack or on the heap. -4. Integrate API with new error reporting strategy. -5. Be concise, except where it impairs clarity. - -Changes: - -* mongo_replset_init_conn has been renamed to mongo_replset_init. -* bson_buffer has been removed. All functions for building bson - objects now take objects of type bson. The new pattern looks like this: - - Example: - - bson b[1]; - bson_init( b ); - bson_append_int( b, "foo", 1 ); - bson_finish( b ); - /* The object is ready to use. - When finished, destroy it. */ - bson_destroy( b ); - -* mongo_connection has been renamed to mongo. - - Example: - - mongo conn[1]; - mongo_connect( conn, '127.0.0.1', 27017 ); - /* Connection is ready. Destroy when down. */ - mongo_destroy( conn ); - -* New cursor builder API for clearer code: - - Example: - - mongo_cursor cursor[1]; - mongo_cursor_init( cursor, conn, "test.foo" ); - - bson query[1]; - - bson_init( query ); - bson_append_int( query, "bar", 1 ); - bson_finish( query ); - - bson fields[1]; - - bson_init( fields ); - bson_append_int( fields, "baz", 1 ); - bson_finish( fields ); - - mongo_cursor_set_query( cursor, query ); - mongo_cursor_set_fields( cursor, fields ); - mongo_cursor_set_limit( cursor, 10 ); - mongo_cursor_set_skip( cursor, 10 ); - - while( mongo_cursor_next( cursor ) == MONGO_OK ) - bson_print( mongo_cursor_bson( cursor ) ); - -* bson_iterator_init now takes a (bson*) instead of a (const char*). This is consistent - with bson_find, which also takes a (bson*). If you want to initiate a bson iterator - with a buffer, use the new function bson_iterator_from_buffer. -* With the addition of the mongo_cursor_bson function, it's now no - longer necessary to know how bson and mongo_cursor objects are implemented. - - Example: - - bson b[1]; - bson_iterator i[1]; - - bson_iterator_init( i, b ); - - /* With a cursor */ - bson_iterator_init( i, mongo_cursor_bson( cursor ) ); - -* Added mongo_cursor_data and bson_data functions, which return the - raw bson buffer as a (const char *). -* All constants that were once lower case are now - upper case. These include: MONGO_OP_MSG, MONGO_OP_UPDATE, MONGO_OP_INSERT, - MONGO_OP_QUERY, MONGO_OP_GET_MORE, MONGO_OP_DELETE, MONGO_OP_KILL_CURSORS - BSON_EOO, BSON_DOUBLE, BSON_STRING, BSON_OBJECT, BSON_ARRAY, BSON_BINDATA, - BSON_UNDEFINED, BSON_OID, BSON_BOOL, BSON_DATE, BSON_NULL, BSON_REGEX, BSON_DBREF, - BSON_CODE, BSON_SYMBOL, BSON_CODEWSCOPE, BSON_INT, BSON_TIMESTAMP, BSON_LONG, - MONGO_CONN_SUCCESS, MONGO_CONN_BAD_ARG, MONGO_CONN_NO_SOCKET, MONGO_CONN_FAIL, - MONGO_CONN_NOT_MASTER, MONGO_CONN_BAD_SET_NAME, MONGO_CONN_CANNOT_FIND_PRIMARY - If your programs use any of these constants, you must convert them to their - upper case forms, or you will see compile errors. -* The error handling strategy has been changed. Exceptions are not longer being used. -* Functions taking a mongo_connection object now return either MONGO_OK or MONGO_ERROR. - In case of an error, an error code of type mongo_error_t will be indicated on the - mongo_connection->err field. -* Functions taking a bson object now return either BSON_OK or BSON_ERROR. - In case of an error, an error code of type bson_validity_t will be indicated on the - bson->err or bson_buffer->err field. -* Calls to mongo_cmd_get_last_error store the error status on the - mongo->lasterrcode and mongo->lasterrstr fields. -* bson_print now prints all types. -* Users may now set custom malloc, realloc, free, printf, sprintf, and fprintf fields. -* Groundwork for modules for supporting platform-specific features (e.g., socket timeouts). -* Added mongo_set_op_timeout for setting socket timeout. To take advantage of this, you must - compile with --use-platform=LINUX. The compiles with platform/linux/net.h instead of the - top-level net.h. -* Fixed tailable cursors. -* GridFS API is now in-line with the new driver API. In particular, all of the - following functions now return MONGO_OK or MONGO_ERROR: gridfs_init, - gridfile_init, gridfile_writer_done, gridfs_store_buffer, gridfs_store_file, - and gridfs_find_query. -* Fixed a few memory leaks. - -## 0.3 -2011-4-14 - -* Support replica sets. -* Better standard connection API. -* GridFS write buffers iteratively. -* Fixes for working with large GridFS files (> 3GB) -* bson_append_string_n and family (Gergely Nagy) - -## 0.2 -2011-2-11 - -* GridFS support (Chris Triolo). -* BSON Timestamp type support. - -## 0.1 -2009-11-30 - -* Initial release. diff --git a/mongo-c-driver-v0.6/Makefile b/mongo-c-driver-v0.6/Makefile deleted file mode 100644 index ee21bd4..0000000 --- a/mongo-c-driver-v0.6/Makefile +++ /dev/null @@ -1,199 +0,0 @@ -# MongoDB C Driver Makefile -# -# Copyright 2009, 2010 10gen Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Version -MONGO_MAJOR=0 -MONGO_MINOR=6 -MONGO_PATCH=0 -BSON_MAJOR=$(MONGO_MAJOR) -BSON_MINOR=$(MONGO_MINOR) -BSON_PATCH=$(MONGO_PATCH) - -# Library names -MONGO_LIBNAME=libmongoc -BSON_LIBNAME=libbson - -# Standard or posix env. -ENV?=posix - -# TODO: add replica set test, cpp test, platform tests, json_test -TESTS=test/auth_test test/bson_test test/bson_subobject_test test/count_delete_test \ - test/cursors_test test/endian_swap_test test/errors_test test/examples_test \ - test/functions_test test/gridfs_test test/helpers_test \ - test/oid_test test/resize_test test/simple_test test/sizes_test test/update_test \ - test/validate_test test/write_concern_test test/commands_test -MONGO_OBJECTS=src/bson.o src/encoding.o src/gridfs.o src/md5.o src/mongo.o \ - src/numbers.o -BSON_OBJECTS=src/bson.o src/numbers.o src/encoding.o - -ifeq ($(ENV),posix) - TESTS+=test/env_posix_test - MONGO_OBJECTS+=src/env_posix.o -else - MONGO_OBJECTS+=src/env_standard.o -endif - -DYN_MONGO_OBJECTS=$(foreach i,$(MONGO_OBJECTS),$(patsubst %.o,%.os,$(i))) -DYN_BSON_OBJECTS=$(foreach i,$(BSON_OBJECTS),$(patsubst %.o,%.os,$(i))) - -# Compile flags -ALL_DEFINES=$(DEFINES) -ALL_DEFINES+=-D_POSIX_SOURCE -CC:=$(shell sh -c 'type $(CC) >/dev/null 2>/dev/null && echo $(CC) || echo gcc') -DYN_FLAGS:=-fPIC -DMONGO_DLL_BUILD - -# Endianness check -endian := $(shell sh -c 'echo "ab" | od -x | grep "6261" >/dev/null && echo little || echo big') -ifeq ($(endian),big) - ALL_DEFINES+=-DMONGO_BIG_ENDIAN -endif - -# Int64 type check -int64:=$(shell ./check_int64.sh $(CC) stdint.h && echo stdint) -ifeq ($(int64),stdint) - ALL_DEFINES+=-DMONGO_HAVE_STDINT -else - int64:=$(shell ./check_int64.sh $(CC) unistd.h && echo unistd) - ifeq ($(int64),unistd) - ALL_DEFINES+=-DMONGO_HAVE_UNISTD - endif -endif -$(shell rm header_check.tmp tmp.c) - -TEST_DEFINES=$(ALL_DEFINES) -TEST_DEFINES+=-DTEST_SERVER="\"127.0.0.1\"" - -OPTIMIZATION?=-O3 -WARNINGS?=-Wall -DEBUG?=-ggdb -STD?=c99 -PEDANTIC?=-pedantic -ALL_CFLAGS=-std=$(STD) $(PEDANTIC) $(CFLAGS) $(OPTIMIZATION) $(WARNINGS) $(DEBUG) $(ALL_DEFINES) -ALL_LDFLAGS=$(LDFLAGS) - -# Shared libraries -DYLIBSUFFIX=so -STLIBSUFFIX=a - -MONGO_DYLIBNAME=$(MONGO_LIBNAME).$(DYLIBSUFFIX) -MONGO_DYLIB_MAJOR_NAME=$(MONGO_DYLIBNAME).$(MONGO_MAJOR) -MONGO_DYLIB_MINOR_NAME=$(MONGO_DYLIB_MAJOR_NAME).$(MONGO_MINOR) -MONGO_DYLIB_PATCH_NAME=$(MONGO_DYLIB_MINOR_NAME).$(MONGO_PATCH) -MONGO_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(MONGO_DYLIB_MINOR_NAME) -o $(MONGO_DYLIBNAME) $(ALL_LDFLAGS) $(DYN_MONGO_OBJECTS) - -BSON_DYLIBNAME=$(BSON_LIBNAME).$(DYLIBSUFFIX) -BSON_DYLIB_MAJOR_NAME=$(BSON_DYLIBNAME).$(BSON_MAJOR) -BSON_DYLIB_MINOR_NAME=$(BSON_DYLIB_MAJOR_NAME).$(BSON_MINOR) -BSON_DYLIB_PATCH_NAME=$(BSON_DYLIB_MINOR_NAME).$(BSON_PATCH) -BSON_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(BSON_DYLIB_MINOR_NAME) -o $(BSON_DYLIBNAME) $(ALL_LDFLAGS) $(DYN_BSON_OBJECTS) - -# Static libraries -MONGO_STLIBNAME=$(MONGO_LIBNAME).$(STLIBSUFFIX) -BSON_STLIBNAME=$(BSON_LIBNAME).$(STLIBSUFFIX) - -# Overrides -kernel_name := $(shell sh -c 'uname -s 2>/dev/null || echo not') -ifeq ($(kernel_name),SunOS) - ALL_LDFLAGS+=-ldl -lnsl -lsocket - INSTALL_CMD=cp -r - MONGO_DYLIB_MAKE_CMD=$(CC) -G -o $(MONGO_DYLIBNAME) -h $(MONGO_DYLIB_MINOR_NAME) $(ALL_LDFLAGS) - BSON_DYLIB_MAKE_CMD=$(CC) -G -o $(BSON_DYLIBNAME) -h $(BSON_DYLIB_MINOR_NAME) $(ALL_LDFLAGS) -endif -ifeq ($(kernel_name),Darwin) - ALL_CFLAGS+=-std=$(STD) $(CFLAGS) $(OPTIMIZATION) $(WARNINGS) $(DEBUG) $(ALL_DEFINES) - DYLIBSUFFIX=dylib - MONGO_DYLIB_MINOR_NAME=$(MONGO_LIBNAME).$(DYLIBSUFFIX).$(MONGO_MAJOR).$(MONGO_MINOR) - MONGO_DYLIB_MAJOR_NAME=$(MONGO_LIBNAME).$(DYLIBSUFFIX).$(MONGO_MAJOR) - MONGO_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(MONGO_DYLIB_MINOR_NAME) -o $(MONGO_DYLIBNAME) - - BSON_DYLIB_MINOR_NAME=$(BSON_LIBNAME).$(DYLIBSUFFIX).$(BSON_MAJOR).$(BSON_MINOR) - BSON_DYLIB_MAJOR_NAME=$(BSON_LIBNAME).$(DYLIBSUFFIX).$(BSON_MAJOR) - BSON_DYLIB_MAKE_CMD=$(CC) -shared -Wl,-install_name,$(BSON_DYLIB_MINOR_NAME) -o $(BSON_DYLIBNAME) -endif - -# Installation -ifeq ($(kernel_name),SunOS) - INSTALL?=cp -r -endif -INSTALL?= cp -a -INSTALL_INCLUDE_PATH?=/usr/local/include -INSTALL_LIBRARY_PATH?=/usr/local/lib - -# TARGETS -all: $(MONGO_DYLIBNAME) $(BSON_DYLIBNAME) $(MONGO_STLIBNAME) $(BSON_STLIBNAME) - -# Dependency targets. Run 'make deps' to generate these. -bson.o: src/bson.c src/bson.h src/encoding.h -encoding.o: src/encoding.c src/bson.h src/encoding.h -env_standard.o: src/env_standard.c src/env.h src/mongo.h src/bson.h -env_posix.o: src/env_posix.c src/env.h src/mongo.h src/bson.h -gridfs.o: src/gridfs.c src/gridfs.h src/mongo.h src/bson.h -md5.o: src/md5.c src/md5.h -mongo.o: src/mongo.c src/mongo.h src/bson.h src/md5.h src/env.h -numbers.o: src/numbers.c - -$(MONGO_DYLIBNAME): $(DYN_MONGO_OBJECTS) - $(MONGO_DYLIB_MAKE_CMD) - -$(MONGO_STLIBNAME): $(MONGO_OBJECTS) - $(AR) -rs $@ $(MONGO_OBJECTS) - -$(BSON_DYLIBNAME): $(DYN_BSON_OBJECTS) - $(BSON_DYLIB_MAKE_CMD) - -$(BSON_STLIBNAME): $(BSON_OBJECTS) - $(AR) -rs $@ $(BSON_OBJECTS) - -install: - mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_LIBRARY_PATH) - $(INSTALL) src/mongo.h src/bson.h $(INSTALL_INCLUDE_PATH) - $(INSTALL) $(MONGO_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(MONGO_DYLIB_PATCH_NAME) - $(INSTALL) $(BSON_DYLIBNAME) $(INSTALL_LIBRARY_PATH)/$(BSON_DYLIB_PATCH_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(MONGO_DYLIB_PATCH_NAME) $(MONGO_DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(BSON_DYLIB_PATCH_NAME) $(BSON_DYLIB_MINOR_NAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(MONGO_DYLIB_MINOR_NAME) $(MONGO_DYLIBNAME) - cd $(INSTALL_LIBRARY_PATH) && ln -sf $(BSON_DYLIB_MINOR_NAME) $(BSON_DYLIBNAME) - $(INSTALL) $(MONGO_STLIBNAME) $(INSTALL_LIBRARY_PATH) - $(INSTALL) $(BSON_STLIBNAME) $(INSTALL_LIBRARY_PATH) - -test: $(TESTS) - sh runtests.sh - -valgrind: $(TESTS) - sh runtests.sh -v - -docs: - python docs/buildscripts/docs.py - -clean: - rm -rf $(MONGO_DYLIBNAME) $(MONGO_STLIBNAME) $(BSON_DYLIBNAME) $(BSON_STLIBNAME) src/*.o src/*.os test/*_test - -deps: - $(CC) -MM -DMONGO_HAVE_STDINT src/*.c - -32bit: - $(MAKE) CFLAGS="-m32" LDFLAGS="-pg" - -%_test: %_test.c $(MONGO_STLIBNAME) - $(CC) -o $@ -L. -Isrc $(TEST_DEFINES) $(ALL_LDFLAGS) $< $(MONGO_STLIBNAME) - -%.o: %.c - $(CC) -o $@ -c $(ALL_CFLAGS) $< - -%.os: %.c - $(CC) -o $@ -c $(ALL_CFLAGS) $(DYN_FLAGS) $< - -.PHONY: clean docs test diff --git a/mongo-c-driver-v0.6/README.md b/mongo-c-driver-v0.6/README.md deleted file mode 100644 index d5ecb27..0000000 --- a/mongo-c-driver-v0.6/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# MongoDB C Driver - -This is then 10gen-supported MongoDB C driver. There are two goals for this driver. -The first is to provide a strict, default compilation option for ultimate portability, -no dependencies, and generic embeddability. - -The second is to support more advanced, platform-specific features, like socket timeout, -by providing an interface for platform-specific modules. - -Until the 1.0 release, this driver should be considered alpha. Keep in mind that the API will be in flux until then. - -# Documentation - -Documentation exists in the project's `docs` folder. You can read the latest -docs online at (http://api.mongodb.org/c/current/). - -The docs are built using Sphinx and Doxygen. If you have these tools installed, then -you can build the docs with scons: - - scons docs - -The html docs will appear in docs/html. - -# Building - -First check out the version you want to build. *Always build from a particular tag, since HEAD may be -a work in progress.* For example, to build version 0.6, run: - - git checkout v0.6 - -You can then build the driver with scons: - - scons - -For more build options, see the docs. - -## Running the tests -Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set -test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port -30000. Note that the driver does not recognize 'localhost' as a valid host name. - -To compile and run the tests: - - scons test - -# Error Handling -Most functions return MONGO_OK or BSON_OK on success and MONGO_ERROR or BSON_ERROR on failure. -Specific error codes and error strings are then stored in the `err` and `errstr` fields of the -`mongo` and `bson` objects. It is the client's responsibility to check for errors and handle -them appropriately. - -# ISSUES - -You can report bugs, request new features, and view this driver's roadmap -using [JIRA](http://jira.mongodb.org/browse/CDRIVER). - -# CREDITS - -* Gergely Nagy - Non-null-terminated string support. -* Josh Rotenberg - Initial Doxygen setup and a significant chunk of documentation. - -# LICENSE - -Unless otherwise specified in a source file, sources in this -repository are published under the terms of the Apache License version -2.0, a copy of which is in this repository as APACHE-2.0.txt. diff --git a/mongo-c-driver-v0.6/SConstruct b/mongo-c-driver-v0.6/SConstruct deleted file mode 100644 index 90a8f31..0000000 --- a/mongo-c-driver-v0.6/SConstruct +++ /dev/null @@ -1,297 +0,0 @@ -# -*- mode: python; -*- - -MAJOR_VERSION = "0" -MINOR_VERSION = "6" -PATCH_VERSION = "0" -VERSION = MAJOR_VERSION + "." + MINOR_VERSION + "." + PATCH_VERSION - -# --- options ---- -AddOption('--test-server', - dest='test_server', - default='127.0.0.1', - type='string', - nargs=1, - action='store', - help='IP address of server to use for testing') - -AddOption('--seed-start-port', - dest='seed_start_port', - default=30000, - type='int', - nargs=1, - action='store', - help='IP address of server to use for testing') - -AddOption('--c99', - dest='use_c99', - default=False, - action='store_true', - help='Compile with c99 (recommended for gcc)') - -AddOption('--m32', - dest='use_m32', - default=False, - action='store_true', - help='Compile with m32 (required for 32 bit applications on 64 bit machines)') - -AddOption('--addrinfo', - dest='use_addrinfo', - default=False, - action='store_true', - help='Compile with addrinfo to make use of internet address info when connecting') - -AddOption('--d', - dest='optimize', - default=True, - action='store_false', - help='disable optimizations') - -AddOption('--standard-env', - dest='standard_env', - default=False, - action='store_true', - help='Set this option if you want to use basic, platform-agnostic networking.') - -AddOption('--install-library-path', - dest='install_library_path', - default='/usr/local/lib', - action='store', - help='The shared library install path. Defaults to /usr/local/lib.') - -AddOption('--install-include-path', - dest='install_include_path', - default='/usr/local/include', - action='store', - help='The header install path. Defaults to /usr/local/include.') - -import os, sys - -if GetOption('use_m32'): - msvs_arch = "x86" -else: - msvs_arch = "amd64" -print "Compiling for " + msvs_arch -env = Environment(ENV=os.environ, MSVS_ARCH=msvs_arch, TARGET_ARCH=msvs_arch) - -# ---- Docs ---- -def build_docs(env, target, source): - buildscript_path = os.path.join(os.path.abspath("docs")) - sys.path.insert(0, buildscript_path) - import buildscripts - from buildscripts import docs - docs.main() - -env.Alias("docs", [], [build_docs]) -env.AlwaysBuild("docs") - -# ---- Platforms ---- -PLATFORM_TESTS = [] -if GetOption('standard_env'): - NET_LIB = "src/env_standard.c" -elif os.sys.platform in ["darwin", "linux2"]: - NET_LIB = "src/env_posix.c" - PLATFORM_TESTS = [ "env_posix" ] -elif 'win32' == os.sys.platform: - NET_LIB = "src/env_win32.c" - PLATFORM_TESTS = [ "env_win32" ] -else: - NET_LIB = "src/env_standard.c" - -# ---- Libraries ---- -if os.sys.platform in ["darwin", "linux2"]: - env.Append( CPPFLAGS="-pedantic -Wall -ggdb -DMONGO_HAVE_STDINT" ) - if not GetOption('standard_env'): - env.Append( CPPFLAGS=" -D_POSIX_SOURCE" ) - env.Append( CPPPATH=["/opt/local/include/"] ) - env.Append( LIBPATH=["/opt/local/lib/"] ) - - if GetOption('use_c99'): - env.Append( CFLAGS=" -std=c99 " ) - env.Append( CXXDEFINES="MONGO_HAVE_STDINT" ) - else: - env.Append( CFLAGS=" -ansi " ) - - if GetOption('optimize'): - env.Append( CPPFLAGS=" -O3 " ) - # -O3 benchmarks *significantly* faster than -O2 when disabling networking -elif 'win32' == os.sys.platform: - env.Append( LIBS='ws2_32' ) - -#we shouldn't need these options in c99 mode -if not GetOption('use_c99'): - conf = Configure(env) - - if not conf.CheckType('int64_t'): - if conf.CheckType('int64_t', '#include \n'): - conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" ) - elif conf.CheckType('int64_t', '#include \n'): - conf.env.Append( CPPDEFINES="MONGO_HAVE_UNISTD" ) - elif conf.CheckType('__int64'): - conf.env.Append( CPPDEFINES="MONGO_USE__INT64" ) - elif conf.CheckType('long long int'): - conf.env.Append( CPPDEFINES="MONGO_USE_LONG_LONG_INT" ) - else: - print "*** what is your 64 bit int type? ****" - Exit(1) - - env = conf.Finish() - -have_libjson = False -conf = Configure(env) -if conf.CheckLib('json'): - have_libjson = True -env = conf.Finish() - -if GetOption('use_m32'): - if 'win32' != os.sys.platform: - env.Append( CPPFLAGS=" -m32" ) - env.Append( SHLINKFLAGS=" -m32" ) - -if GetOption('use_addrinfo'): - env.Append( CPPFLAGS=" -D_MONGO_USE_GETADDRINFO" ) - -if sys.byteorder == 'big': - env.Append( CPPDEFINES="MONGO_BIG_ENDIAN" ) - -env.Append( CPPPATH=["src/"] ) - -env.Append( CPPFLAGS=" -DMONGO_DLL_BUILD" ) -coreFiles = ["src/md5.c" ] -mFiles = [ "src/mongo.c", NET_LIB, "src/gridfs.c"] -bFiles = [ "src/bson.c", "src/numbers.c", "src/encoding.c"] - -mHeaders = ["src/mongo.h"] -bHeaders = ["src/bson.h"] -headers = mHeaders + bHeaders - -mLibFiles = coreFiles + mFiles + bFiles -bLibFiles = coreFiles + bFiles - -m = env.Library( "mongoc" , mLibFiles ) -b = env.Library( "bson" , bLibFiles ) -env.Default( env.Alias( "lib" , [ m[0] , b[0] ] ) ) - -# build the objects explicitly so that shared targets use the same -# environment (otherwise scons complains) -mSharedObjs = env.SharedObject(mLibFiles) -bSharedObjs = env.SharedObject(bLibFiles) - -bsonEnv = env.Clone() -if os.sys.platform == "linux2": - env.Append( SHLINKFLAGS = "-shared -Wl,-soname,libmongoc.so." + MAJOR_VERSION + "." + MINOR_VERSION ) - bsonEnv.Append( SHLINKFLAGS = "-shared -Wl,-soname,libbson.so." + MAJOR_VERSION + "." + MINOR_VERSION) - dynm = env.SharedLibrary( "mongoc" , mSharedObjs ) - dynb = bsonEnv.SharedLibrary( "bson" , bSharedObjs ) -else: - dynm = env.SharedLibrary( "mongoc" , mSharedObjs ) - dynb = env.SharedLibrary( "bson" , bSharedObjs ) - -# ---- Install ---- -if os.sys.platform == "darwin": - shared_obj_suffix = "dylib" -else: - shared_obj_suffix = "so" - -install_library_path = env.GetOption("install_library_path") -install_include_path = env.GetOption("install_include_path") -def remove_without_exception(filename): - try: - os.remove(filename) - except: - print "Could not find " + filename + ". Skipping removal." - -def makedirs_without_exception(path): - try: - os.makedirs(path) - except: - print path + ": already exists, skipping" - -mongoc_target = os.path.join(install_library_path, "libmongoc." + shared_obj_suffix) -mongoc_major_target = mongoc_target + "." + MAJOR_VERSION -mongoc_minor_target = mongoc_major_target + "." + MINOR_VERSION -mongoc_patch_target = mongoc_minor_target + "." + PATCH_VERSION - -bson_target = os.path.join(install_library_path, "libbson." + shared_obj_suffix) -bson_major_target = bson_target + "." + MAJOR_VERSION -bson_minor_target = bson_major_target + "." + MINOR_VERSION -bson_patch_target = bson_minor_target + "." + PATCH_VERSION - -def uninstall_shared_libraries(target=None, source=None, env=None): - remove_without_exception(mongoc_major_target) - remove_without_exception(mongoc_minor_target) - remove_without_exception(mongoc_patch_target) - remove_without_exception(mongoc_target) - - remove_without_exception(bson_major_target) - remove_without_exception(bson_minor_target) - remove_without_exception(bson_patch_target) - remove_without_exception(bson_target) - -def install_shared_libraries(target=None, source=None, env=None): - import shutil - uninstall_shared_libraries() - - makedirs_without_exception(install_library_path) - shutil.copy("libmongoc." + shared_obj_suffix, mongoc_patch_target) - os.symlink(mongoc_patch_target, mongoc_minor_target) - os.symlink(mongoc_minor_target, mongoc_target) - - shutil.copy("libbson." + shared_obj_suffix, bson_patch_target) - os.symlink(bson_patch_target, bson_minor_target) - os.symlink(bson_minor_target, bson_target) - -def install_headers(target=None, source=None, env=None): - import shutil - # -- uninstall headers here? - - makedirs_without_exception(install_include_path) - for hdr in headers: - shutil.copy(hdr, install_include_path) - -env.Alias("install", [], [install_shared_libraries, install_headers] ) - -env.Command("uninstall", [], uninstall_shared_libraries) - -env.Default( env.Alias( "sharedlib" , [ dynm[0] , dynb[0] ] ) ) -env.AlwaysBuild("install") - -# ---- Benchmarking ---- -benchmarkEnv = env.Clone() -benchmarkEnv.Append( CPPDEFINES=[('TEST_SERVER', r'\"%s\"'%GetOption('test_server')), -('SEED_START_PORT', r'%d'%GetOption('seed_start_port'))] ) -benchmarkEnv.Append( LIBS=[m, b] ) -benchmarkEnv.Prepend( LIBPATH=["."] ) -benchmarkEnv.Program( "benchmark" , [ "test/benchmark.c"] ) - -# ---- Tests ---- -testEnv = benchmarkEnv.Clone() -testCoreFiles = [ ] - -def run_tests( root, tests, env, alias ): - for name in tests: - filename = "%s/%s_test.c" % (root, name) - exe = "test_" + name - test = env.Program( exe , testCoreFiles + [filename] ) - test_alias = env.Alias(alias, [test], test[0].abspath + ' 2> ' + os.path.devnull) - AlwaysBuild(test_alias) - -tests = Split("write_concern commands sizes resize endian_swap bson bson_subobject simple update errors " -"count_delete auth gridfs validate examples helpers oid functions cursors") -tests += PLATFORM_TESTS - -# Run standard tests -run_tests("test", tests, testEnv, "test") - -if have_libjson: - tests.append('json') - testEnv.Append( LIBS=["json"] ) - -# special case for cpptest -test = testEnv.Program( 'test_cpp' , testCoreFiles + ['test/cpptest.cpp'] ) -test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> '+ os.path.devnull) -AlwaysBuild(test_alias) - -# Run replica set test only -repl_testEnv = benchmarkEnv.Clone() -repl_tests = ["replica_set"] -run_tests("test", repl_tests, repl_testEnv, "repl_test") diff --git a/mongo-c-driver-v0.6/check_int64.sh b/mongo-c-driver-v0.6/check_int64.sh deleted file mode 100755 index 5e2ba54..0000000 --- a/mongo-c-driver-v0.6/check_int64.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# -# Copyright 2009, 2010 10gen Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# $1 is C compiler. $2 is header file -cat <tmp.c && $1 -o header_check.tmp tmp.c -#include <$2> -int main() { int64_t i=0; return 0; } -EOF diff --git a/mongo-c-driver-v0.6/docs/buildscripts/__init__.py b/mongo-c-driver-v0.6/docs/buildscripts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mongo-c-driver-v0.6/docs/buildscripts/docs.py b/mongo-c-driver-v0.6/docs/buildscripts/docs.py deleted file mode 100644 index 1d15e27..0000000 --- a/mongo-c-driver-v0.6/docs/buildscripts/docs.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Build the C client docs. -""" - -from __future__ import with_statement -import os -import shutil -import socket -import subprocess -import time -import urllib2 - -def clean_dir(dir): - try: - shutil.rmtree(dir) - except: - pass - os.makedirs(dir) - -def gen_api(dir): - clean_dir(dir) - clean_dir("docs/source/doxygen") - - with open(os.devnull, 'w') as null: - subprocess.call(["doxygen", "doxygenConfig"], stdout=null, stderr=null) - - os.rename("docs/source/doxygen/html", dir) - -def gen_sphinx(dir): - clean_dir(dir) - os.chdir("docs/source/sphinx") - - with open(os.devnull, 'w') as null: - subprocess.call(["make", "html"], stdout=null, stderr=null) - - os.chdir("../../../") - os.rename("docs/source/sphinx/build/html", dir) - -def version(): - """Get the driver version from doxygenConfig. - """ - with open("doxygenConfig") as f: - for line in f.readlines(): - if line.startswith("PROJECT_NUMBER"): - return line.split("=")[1].strip() - - -def main(): - print("Generating Sphinx docs in docs/html") - gen_sphinx("docs/html") - print("Generating Doxygen docs in docs/html/api") - gen_api("docs/html/api") - - -if __name__ == "__main__": - main() - diff --git a/mongo-c-driver-v0.6/docs/examples/example.c b/mongo-c-driver-v0.6/docs/examples/example.c deleted file mode 100644 index 5ef97af..0000000 --- a/mongo-c-driver-v0.6/docs/examples/example.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "mongo.h" -#include -#include -#include - -int main() { - bson b, sub, out, empty; - bson_iterator it; - mongo conn; - mongo_cursor cursor; - int result; - - /* Create a rich document like this one: - * - * { _id: ObjectId("4d95ea712b752328eb2fc2cc"), - * user_id: ObjectId("4d95ea712b752328eb2fc2cd"), - * - * items: [ - * { sku: "col-123", - * name: "John Coltrane: Impressions", - * price: 1099, - * }, - * - * { sku: "young-456", - * name: "Larry Young: Unity", - * price: 1199 - * } - * ], - * - * address: { - * street: "59 18th St.", - * zip: 10010 - * }, - * - * total: 2298 - * } - */ - bson_init( &b ); - bson_append_new_oid( &b, "_id" ); - bson_append_new_oid( &b, "user_id" ); - - bson_append_start_array( &b, "items" ); - bson_append_start_object( &b, "0" ); - bson_append_string( &b, "name", "John Coltrane: Impressions" ); - bson_append_int( &b, "price", 1099 ); - bson_append_finish_object( &b ); - - bson_append_start_object( &b, "1" ); - bson_append_string( &b, "name", "Larry Young: Unity" ); - bson_append_int( &b, "price", 1199 ); - bson_append_finish_object( &b ); - bson_append_finish_object( &b ); - - bson_append_start_object( &b, "address" ); - bson_append_string( &b, "street", "59 18th St." ); - bson_append_int( &b, "zip", 10010 ); - bson_append_finish_object( &b ); - - bson_append_int( &b, "total", 2298 ); - - /* Finish the BSON obj. */ - bson_finish( &b ); - printf("Here's the whole BSON object:\n"); - bson_print( &b ); - - /* Advance to the 'items' array */ - bson_find( &it, &b, "items" ); - - /* Get the subobject representing items */ - bson_iterator_subobject( &it, &sub ); - - /* Now iterate that object */ - printf("And here's the inner sub-object by itself.\n"); - bson_print( &sub ); - - /* Now make a connection to MongoDB. */ - if( mongo_connect( &conn, "127.0.0.1", 27017 ) != MONGO_OK ) { - switch( conn.err ) { - case MONGO_CONN_NO_SOCKET: - printf( "FAIL: Could not create a socket!\n" ); - break; - case MONGO_CONN_FAIL: - printf( "FAIL: Could not connect to mongod. Make sure it's listening at 127.0.0.1:27017.\n" ); - break; - } - - exit( 1 ); - } - - /* Insert the sample BSON document. */ - if( mongo_insert( &conn, "test.records", &b ) != MONGO_OK ) { - printf( "FAIL: Failed to insert document with error %d\n", conn.err ); - exit( 1 ); - } - - /* Query for the sample document. */ - mongo_cursor_init( &cursor, &conn, "test.records" ); - mongo_cursor_set_query( &cursor, bson_empty( &empty ) ); - if( mongo_cursor_next( &cursor ) != MONGO_OK ) { - printf( "FAIL: Failed to find inserted document." ); - exit( 1 ); - } - - printf( "Found saved BSON object:\n" ); - bson_print( (bson *)mongo_cursor_bson( &cursor ) ); - - mongo_cmd_drop_collection( &conn, "test", "records", NULL ); - mongo_cursor_destroy( &cursor ); - bson_destroy( &b ); - mongo_destroy( &conn ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/Makefile b/mongo-c-driver-v0.6/docs/source/sphinx/Makefile deleted file mode 100644 index 9eb025a..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/MongoDBCDriver.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/MongoDBCDriver.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/MongoDBCDriver" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/MongoDBCDriver" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - make -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/make.bat b/mongo-c-driver-v0.6/docs/source/sphinx/make.bat deleted file mode 100644 index e3cc32a..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/make.bat +++ /dev/null @@ -1,155 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. changes to make an overview over all changed/added/deprecated items - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\MongoDBCDriver.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\MongoDBCDriver.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/bson.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/bson.rst deleted file mode 100644 index 8c36a60..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/bson.rst +++ /dev/null @@ -1,218 +0,0 @@ -BSON -============================= - -BSON (i.e., binary structured object notation) is the binary format used -by MongoDB to store data and express queries and commands. To work with -MongoDB is to trade in BSON objects. This document describes how to -create, read, and destroy BSON objects using the MongoDB C Driver. - -Libraries ---------- - -A brief note on libraries. - -When you compile the driver, the BSON library is included in the -driver. This means that when you include ``mongo.h``, you have access -to all the functions declared in ``bson.h``. - -If you want to use BSON independently, you don't need ``libmongoc``: when you compile -the driver, you'll also get shared and static libraries for ``libbson``. You -can link to this library and simple require ``bson.h``. - -Using BSON objects ------------------- - -The pattern of BSON object usage is pretty simple. Here are the steps: - -1. Initiate a new BSON object. -2. Construct the object using the bson_append_* methods. -3. Pass the object to bson_finish() to finalize it. The object is now ready to use. -4. When you're done with it, pass the object to bson_destroy() to free up any allocated - memory. - -To demonstrate, let's create a BSON object corresponding to the simple JSON object -``{count: 1001}``. - -.. code-block:: c - - bson b[1]; - - bson_init( b ); - bson_append_int( b, "count", 1001 ); - bson_finish( b ); - - // BSON object now ready for use - - bson_destroy( b ); - -That's all there is to creating a basic object. - -Creating complex BSON objects -_____________________________ - -BSON objects can contain arrays as well as sub-objects. Here -we'll see how to create these by building the bson object -corresponding to the following JSON object: - -.. code-block:: javascript - - { - name: "Kyle", - - colors: [ "red", "blue", "green" ], - - address: { - city: "New York", - zip: "10011-4567" - } - } - -.. code-block:: c - - bson b[1]; - - bson_init( b ); - bson_append_string( b, "name", "Kyle" ); - - bson_append_start_array( b, "colors" ); - bson_append_string( b, "0", "red" ); - bson_append_string( b, "1", "blue" ); - bson_append_string( b, "2", "green" ); - bson_append_finish_array( b ); - - bson_append_start_object( b, "address" ); - bson_append_string( b, "city", "New York" ); - bson_append_string( b, "zip", "10011-4567" ); - bson_append_finish_object( b ); - - if( bson_finish( b ) != BSON_OK ) - printf(" Error. "); - -Notice that for the array, we have to manually set the index values -from "0" to *n*, where *n* is the number of elements in the array. - -You'll notice that some knowledge of the BSON specification and -of the available types is necessary. For that, take a few minutes to -consult the `official BSON specification `_. - -Error handling --------------- - -The names of BSON object values, as well as all strings, must be -encoded as valid UTF-8. The BSON library will automatically check -the encoding of strings as you create BSON objects, and if the objects -are invalid, you'll be able to check for this condition. All of the -bson_append_* methods will return either BSON_OK for BSON_ERROR. You -can check in your code for the BSON_ERROR condition and then see the -exact nature of the error by examining the bson->err field. This bitfield -can contain any of the following values: - -* BSON_VALID -* BSON_NOT_UTF8 -* BSON_FIELD_HAS_DOT -* BSON_FIELD_INIT_DOLLAR -* BSON_ALREADY_FINISHED - -The most important of these is ``BSON_NOT_UTF8`` because the BSON -objects cannot be used with MongoDB if they're not valid UTF8. - -To keep your code clean, you may want to check for BSON_OK only when -calling ``bson_finish()``. If the object is not valid, it will not be -finished, so it's quite important to check the return code here. - -Reading BSON objects --------------------- - -You can read through a BSON object using a ``bson_iterator``. For -a complete example, you may want to read through the implementation -of ``bson_print_raw()`` (in ``bson.h``). But the basic idea is to -initialize a ``bson_iterator`` object and then iterate over each -successive element using ``bson_iterator_next()``. Let's take an -example. Suppose we have a finished object of type ``bson*`` called ``b``: - -.. code-block:: c - - - bson_iterator i[1]; - bson_type type; - const char * key; - - bson_iterator_init( i, b ); - - type = bson_iterator_next( i ); - key = bson_iterator_key( i ); - - printf( "Type: %d, Key: %s\n", type, key ); - -We've advanced to the first element in the object, and we can print -both it's BSON numeric type and its key name. To print the value, -we need to use the type to find the correct method for reading the -value. For instance, if the element is a string, then we use -``bson_iterator_string`` to return the result: - -.. code-block:: c - - printf( "Value: %s\n", bson_iterator_string( i ) ); - -In addition to iterating over each successive BSON element, -we can use the ``bson_find()`` function to jump directly -to an element by name. Again, suppose that ``b`` is a pointer -to a ``bson`` object. If we want to jump to the element -named "address", we use ``bson_find()`` like so: - -.. code-block:: c - - bson_iterator i[1], sub[i]; - bson_type type; - - type = bson_find( i, b, "address" ); - -This will initialize the iterator, ``i``, and position -it at the element named "address". The return value -will be the "address" element's type. - -Reading sub-objects and arrays ------------------------------- - -Since "address" is a sub-object, we need to specially -iterate it. To do that, we get the raw value and initialize -a new BSON iterator like so: - -.. code-block:: c - - type = bson_find( i, b, "address" ); - - bson_iterator_subiterator( i, sub ); - -The function ``bson_iterator_subiterator`` initializes -the iterator ``sub`` and points it to the beginning of the -sub-object. From there, we can iterate over -``sub`` until we reach ``BSON_EOO``, indicating the end of the -sub-object. - -If you want to work with a sub-object by itself, there's -a function, ``bson_iterator_subobject``, for initializing -a new ``bson`` object with the value of the sub-object. Note -that this does not copy the object. If you want a copy of the -object, use ``bsop_copy()``. - -.. code-block:: c - - bson copy[1]; - - bson_copy( copy, sub ); - -Getting a Raw BSON Pointer --------------------------- - -Sometimes you'll want to access the ``char *`` that -points to the buffer storing the raw BSON object. For that, -use the ``bson_data()`` function. You can use this in concert -with the bson_iterator_from_buffer() function to initialize an -iterator: - -.. code-block:: c - - bson_iterator i[1]; - - bson_iterator_from_buffer( i, bson_data( b ) ); diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/building.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/building.rst deleted file mode 100644 index 3ad3734..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/building.rst +++ /dev/null @@ -1,233 +0,0 @@ -Building the MongoDB C Driver -============================= - -First checkout the version you want to build. *Always build from a particular tag, since HEAD may be -a work in progress.* For example, to build version 0.5.1, run: - -.. code-block:: bash - - git checkout v0.5.1 - -Then follow the build steps below. - -Compile options with custom defines ----------------------------------- - -Before compiling, you should note the following compile options. - -For big-endian support, define: - -- ``MONGO_BIG_ENDIAN`` - -If your compiler has a plain ``bool`` type, define: - -- ``MONGO_HAVE_BOOL`` - -Alternatively, if you must include ``stdbool.h`` to get ``bool``, define: - -- ``MONGO_HAVE_STDBOOL`` - -If you're not using C99, then you must choose your 64-bit integer type by -defining one of these: - -- ``MONGO_HAVE_STDINT`` - Define this if you have ```` for int64_t. -- ``MONGO_HAVE_UNISTD`` - Define this if you have ```` for int64_t. -- ``MONGO_USE__INT64`` - Define this if ``__int64`` is your compiler's 64bit type (MSVC). -- ``MONGO_USE_LONG_LONG_INT`` - Define this if ``long long int`` is your compiler's 64-bit type. - -Building with Make: -------------------- - -If you're building the driver on posix-compliant platforms, including on OS X -and Linux, then you can build with ``make``. - -To compile the driver, run: - -.. code-block:: bash - - make - -This will build the following libraries: - -* libbson.a -* libbson.so (libbson.dylib) -* libmongoc.a -* lobmongoc.so (libmongoc.dylib) - -You can install the libraries with make as well: - -.. code-block:: bash - - make install - -And you can run the tests: - -.. code-block:: bash - - make test - -You can even build the docs: - -.. code-block:: bash - - make docs - -By default, ``make`` will build the project in ``c99`` mode. If you want to change the -language standard, set the value of STD. For example, if you want to build using -the ANSI C standard, set STD to c89: - -.. code-block:: bash - - make STD=c89 - -Once you've built and installed the libraries, you can compile the sample -with ``gcc`` like so: - -.. code-block:: bash - - gcc --std=c99 -I/usr/local/include -L/usr/local/lib -o example docs/examples/example.c -lmongoc - -If you want to statically link the program, add the ``-static`` option: - -.. code-block:: bash - - gcc --std=c99 -static -I/usr/local/include -L/usr/local/lib -o example docs/examples/example.c -lmongoc - -Then run the program: - -.. code-block:: bash - - ./example - -Building with SCons: --------------------- - -You may also build the driver using the Python build utility, SCons_. -This is required if you're building on Windows. Make sure you've -installed SCons, and then from the project root, enter: - -.. _SCons: http://www.scons.org/ - -.. code-block:: bash - - scons - -This will build static and dynamic libraries for both ``BSON`` and for the -the driver as a complete package. It's recommended that you build in C99 mode -with optimizations enabled: - -.. code-block:: bash - - scons --c99 - -Once you've built the libraries, you can compile a program with ``gcc`` like so: - -.. code-block:: bash - - gcc --std=c99 -static -Isrc -o example docs/example/example.c libmongoc.a - -On Posix systems, you may also install the libraries with scons: - -.. code-block:: bash - - scons install - -To build the docs: - -.. code-block:: bash - - scons docs - -Building on Windows -------------------- - -When building the driver on Windows, you must use the Python build -utility, SCons_. For your compiler, we recommend that you use Visual Studio. -If you don't have Visual Studio, a free version is available. Search for Visual -Studio C++ Express to find it. - -If you're running on 32-bit Windows, you must compile the driver in 32-bit mode: - -.. code-block:: bash - - scons --m32 - -If getaddrinfo and friends aren't available on your version of Windows, you may -compile without these features like so: - -.. code-block:: bash - - scons --m32 --standard-env - -Platform-specific features --------------------------- - -The original goal of the MongoDB C driver was to provide a very basic library -capable of being embedded anywhere. This goal is now evolving somewhat given -the increased use of the driver. In particular, it now makes sense to provide -platform-specific features, such as socket timeouts and DNS resolution, and to -return platform-specific error codes. - -To that end, we've organized all platform-specific code in the following files: - -* ``env_standard.c``: a standard, platform-agnostic implementation. -* ``env_posix.c``: an implementation geared for Posix-compliant systems (Linux, OS X). -* ``env_win32.c``: a Windows implementation. - -Each of these implements the interface defined in ``env.h``. - -When building with ``make``, we use ``env_posix.c``. When building with SCons_, we -use ``env_posix.c`` or ``env_win32.c``, depending on the platform. - -If you want to compile with the generic, platform implementation, you have to do so -explicity. In SCons_: - -.. code-block:: bash - - scons --standard-env - -Using ``make``: - -.. code-block:: bash - - make ENV=standard - -Dependencies ------------- - -The driver itself has no dependencies, but one of the tests shows how to create a JSON-to-BSON -converter. For that test to run, you'll need JSON-C_. - -.. _JSON-C: http://oss.metaparadigm.com/json-c/ - -Test suite ----------- - -Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set -test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port -30000. Note that the driver does not recognize 'localhost' as a valid host name. - -With make: - -.. code-block:: bash - - make test - -To compile and run the tests with SCons: - -.. code-block:: bash - - scons test - -You may optionally specify a remote server: - -.. code-block:: bash - - scons test --test-server=123.4.5.67 - -You may also specify an alternate starting port for the replica set members: - -.. code-block:: bash - - scons test --test-server=123.4.5.67 --seed-start-port=40000 - diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/conf.py b/mongo-c-driver-v0.6/docs/source/sphinx/source/conf.py deleted file mode 100644 index 124f192..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/conf.py +++ /dev/null @@ -1,216 +0,0 @@ -# -*- coding: utf-8 -*- -# -# MongoDB C Driver documentation build configuration file, created by -# sphinx-quickstart on Wed Jun 22 12:23:03 2011. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'MongoDB C Driver' -copyright = u'2011, 10gen' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.4' -# The full version, including alpha/beta/rc tags. -release = '0.4' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# html_theme = 'nature' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'MongoDBCDriverdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'MongoDBCDriver.tex', u'MongoDB C Driver Documentation', - u'10gen', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -#man_pages = [ -# ('index', 'mongodbcdriver', u'MongoDB C Driver Documentation', -# [u'10gen, Inc.'], 1) -#] diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/connections.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/connections.rst deleted file mode 100644 index 20064b4..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/connections.rst +++ /dev/null @@ -1,149 +0,0 @@ -Connections -============================= - -All operations against a MongoDB server or cluster require a connection object. This document -describes how to create and manage these connections. - -Basic connections ------------------ - -Use a basic connection to connect to a single MongoDB instances (``mongod``) or -to the router for a shard cluster (``mongos``). - -.. code-block:: c - - mongo conn[1]; - int result; - - result = mongo_connect( conn, "127.0.0.1", 27017 ); - -First we create the ``mongo`` object to manage the connection. Then we connect -using ``mongo_connect``. If the function returns ``MONGO_OK``, then we've -successfully connected. - -Notice that when specifying the host, we must use dot-decimal notation. If you'd like -to use a hostname, then you'll have to compile the driver with the ``--use-platform=LINUX`` -option and ensure that ``_MONGO_USE_GETADDRINFO`` is defined. - -In the event of an error, the result will be ``MONGO_ERROR``. You can then check the error -value by examining the connection's ``err`` field. Continuing: - -.. code-block:: c - - if( result != MONGO_OK ) { - switch( conn->err ) { - case MONGO_CONN_NO_SOCKET: break; /**< Could not create a socket. */ - case MONGO_CONN_FAIL: break; /**< An error occured while calling connect(). */ - case MONGO_CONN_ADDR_FAIL: break; /**< An error occured while calling getaddrinfo(). */ - case MONGO_CONN_NOT_MASTER: break; /**< Warning: connected to a non-master node (read-only). */ - } - -These are the most likely error scenarios. For all possible errors, -see the enum ``mongo_error_t``, and reference all constants beginning -with ``MONGO_CONN``. - -Once you've finished with your connection object, be sure to pass it to -``mongo_destroy()``. This will close the socket and clean up any allocated -memory: - -.. code-block:: c - - mongo_destroy( conn ); - -Replica set connections ------------------------ - -Use a replica set connection to connect to a replica set. - -The problem with connecting to a replica set is that you don't necessarily -know which node is the primary node at connection time. This MongoDB C driver -automatically figures out which node is the primary and then connects to it. - -To connection, you must provide: - -* The replica set's name - -And - -* At least one seed node. - -Here's how you go about that: - -.. code-block:: c - - mongo conn[1]; - int result; - - mongo_replset_init( conn, "rs-dc-1" ); - mongo_replset_add_seed( conn, '10.4.3.1', 27017 ); - mongo_replset_add_seed( conn, '10.4.3.2', 27017 ); - - result = mongo_replset_connect( conn ); - -First we initiaize the connection object, providing the name of the replica set, -in this case, "rs-dc-1." Next, we add two seed nodes. Finally, we connect -by pass the connection to ``mongo_replset_connect``. - -As with the basic connection, we'll want to check for any errors on connect. Notice -that there are two more error conditions we check for: - -.. code-block:: c - - if( result != MONGO_OK ) { - switch( conn->err ) - MONGO_CONN_NO_SOCKET: break; /**< Could not create a socket. */ - MONGO_CONN_FAIL: break; /**< An error occured while calling connect(). */ - MONGO_CONN_ADDR_FAIL: break; /**< An error occured while calling getaddrinfo(). */ - MONGO_CONN_NOT_MASTER: break; /**< Warning: connected to a non-master node (read-only). */ - MONGO_CONN_BAD_SET_NAME: break; /**< Given rs name doesn't match this replica set. */ - MONGO_CONN_NO_PRIMARY: break; /**< Can't find primary in replica set. Connection closed. */ - } - -When finished, be sure to destroy the connection object: - -.. code-block:: c - - mongo_destroy( conn ); - -Timeouts --------- - -You can set a timeout for read and write operation on the connection at any time: - -.. code-block:: c - - mongo_set_op_timeout( conn, 1000 ); - -This will set a 1000ms read-write timeout on the socket. If an operation fails, -you'll see a generic MONGO_IO_ERROR on the connection's ``err`` field. Future -versions of this driver will provide a more granular error code. - -Note this this will work only if you've compiled with driver with timeout support. - -I/O Errors and Reconnecting --------------------------- - -As you begin to use connection object to read and write data from MongoDB, -you may ocassionally encounter a ``MONGO_IO_ERROR``. In most cases, -you'll want to reconnect when you see this. Here's a very basic example: - -.. code-block:: c - - bson b[1]; - - bson_init( b ); - bson_append_string( b, "hello", "world" ); - bson_finish( b ); - - if( mongo_insert( conn, b ) == MONGO_ERROR && conn->err == MONGO_IO_ERROR ) - mongo_reconnect( conn ); - -When reconnecting, you'll want to check the return value to ensure that the connection -has succeeded. If you ever have any doubts about whether you're really connection, -you can verify the health of the connection like so: - -.. code-block:: c - - mongo_check_connection( conn ); - -This function will return ``MONGO_OK`` if we're in fact connected. diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/errors.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/errors.rst deleted file mode 100644 index dd45009..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/errors.rst +++ /dev/null @@ -1,73 +0,0 @@ -Error Reporting -=============== - -The MongoDB C driver reports errors from three sources: - -* The operating system -* The MongoDB server -* The driver itself -- typically user input errors - -The driver's API is structured such that nearly all functions -return either `MONGO_OK` or `MONGO_ERROR`. When a function returns -`MONGO_ERROR`, you may examine the `mongo` object to see why the -error has occurred. - -Operating system errors ------------------------ - -A good example of an operating system error is a connection failure. - -.. code-block:: c - - mongo conn[1]; - - if ( mongo_connect( conn, "foo.example.com", 27017 ) == MONGO_ERROR ) { - printf( "mongo_error_t: %d\n", conn->err ); - printf( "errno (or WSAGetLastError() on Windows: %d\n", conn->errcode ); - printf( "Error string: %s\n", conn->errstr ); - exit( 1 ); - } - -First, we print the `mongo_error_t` to get the general error type. Consult the `mongo_error_t -definition `_ to interpret this. - -Next, we print the OS-level error code. On POSIX-compliant systems, this will be the value of -`errno `_; -on Windows, it's the value of `WSAGetLastError() `_. - -Finally, we print the error string, which gives is a few more details. This string may be -the OS-level translation of the error code (e.g., POSIX's -`strerror() `_), or it may be -a string generated by the driver itself which better describes the failure. - -MongoDB errors --------------- - -MongoDB itself produces errors that may be returned to the client after any query -or call to the `getlasterror` command. The code and strings for these errors -are stored in the `mongo` object's `lasterrcode` and `lasterrstring`, respectively. -We can force this sort of error by trying to run an invalid command: - -.. code-block:: c - - mongo conn[1]; - int res; - - if( mongo_connect( conn, "foo.example.com", 27017 ) == MONGO_ERROR ) { - exit( 1 ); - } - - if( mongo_simple_int_command( conn, "admin", "badCommand", 1, &out ) == MONGO_ERROR ) { - printf("Last error code: %d\n", conn->lasterrcode ); - printf("Last error string: %s\n", conn->lasterrstr ); - } - - -Clearing errors ---------------- - -To reset errors on the `mongo` object, run the `mongo_clear_errors` function: - -.. code-block:: c - - mongo_clear_errors( conn ); diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/index.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/index.rst deleted file mode 100644 index 8005b19..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/index.rst +++ /dev/null @@ -1,46 +0,0 @@ -MongoDB C Driver Documentation -============================== - -Overview --------- - -The MongoDB C Driver is a 10gen-supported driver for MongoDB. -It's written in pure C. The goal is to be super strict for ultimate -portability, no dependencies, and generic embeddability. - -The driver is still considered alpha but is undergoing active -development. Support for replica sets was just added in v0.3.1. -The API was completely revamped in v0.4. Another backward-breaking -change (support for `write_concern`) was added in v0.6. - -:doc:`tutorial` - An overview of the driver's API. - -:doc:`building` - How to build the driver from source. - -:doc:`bson` - How to work with BSON objects. - -:doc:`connections` - How to connect to single nodes and to replica sets. - -:doc:`write_concern` - How to detect write errors and ensure various durability levels. - -:doc:`errors` - How errors are reported. - -`API Docs `_ - Doxygen-generated API docs. - -`Source code `_ - The source code is hosted on GitHub. - -.. toctree:: - :maxdepth: 2 - - tutorial - building - bson - connections diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/tutorial.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/tutorial.rst deleted file mode 100644 index 857613a..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/tutorial.rst +++ /dev/null @@ -1,401 +0,0 @@ -MongoDB C Driver Tutorial -========================= - -This document shows how to use MongoDB from C. If you're not familiar with MongoDB. -you'll want to get a brief overview of the database and its shell API. The official -tutorial is a great place to start. - -Next, you'll want to install and run MongoDB. - -A working C program complete with examples from this tutorial can be -found in the examples folder of the source distribution. - -C API ------ - -When writing programs with the C driver, you'll be using four different -entities: connections, cursors, bson objects, and bson iterators. The APIs -for each of these follow a similiar pattern. You start by allocating an object, -either on the stack or the heap (the examples that follow all use the stack). You then -call an ``init`` function and use other function to build the object. When you're finished, -you pass the object to a ``destroy`` function. - -So, for instance, to create a new connection, start by allocating a ``mongo`` object: - -.. code-block:: c - - mongo conn; - -Next, initialize it: - -.. code-block:: c - - mongo_init( &conn ); - -Set any optional values, like a timeout, and then call ``mongo_connect``: - -.. code-block:: c - - mongo_set_op_timeout( &conn, 1000 ); - mongo_connect( &conn, "127.0.0.1", 27017 ); - -When you're finished, destroy the mongo object: - -.. code-block:: c - - mongo_destroy( &conn ); - -There are more details, but that's the basic pattern. Keep this in mind -as you learn the API and start using the driver. - -Connecting ----------- - -Let's start by that connects to the database: - -.. code-block:: c - - #include - #include "mongo.h" - - int main() { - mongo conn[1]; - int status = mongo_connect( conn, "127.0.0.1", 27017 ); - - if( status != MONGO_OK ) { - switch ( conn->err ) { - case MONGO_CONN_SUCCESS: printf( "connection succeeded\n" ); break; - case MONGO_CONN_NO_SOCKET: printf( "no socket\n" ); return 1; - case MONGO_CONN_FAIL: printf( "connection failed\n" ); return 1; - case MONGO_CONN_NOT_MASTER: printf( "not master\n" ); return 1; - } - } - - mongo_destroy( conn ); - - return 0; - } - -Building the sample program ---------------------------- - -If you are using ``gcc`` on Linux or OS X, you can compile with something like this, -depending on location of your include files: - -.. code-block:: bash - - $ gcc -Isrc --std=c99 /path/to/mongo-c-driver/src/*.c -I /path/to/mongo-c-driver/src/ tutorial.c -o tutorial - $ ./tutorial - connection succeeded - connection closed - - -Connecting to a replica set ---------------------------- - -The API for connecting to a replica set is slightly different. First you initialize -the connection object, specifying the replica set's name (in this case, "shard1"), -then you add seed nodes, and finally you connect. Here's an example: - -.. code-block:: c - - #include "mongo.h" - - int main() { - mongo conn[1]; - - mongo_replset_init( conn, "shard1" ); - mongo_replset_add_seed( conn, "10.4.3.22", 27017 ); - mongo_replset_add_seed( conn, "10.4.3.32", 27017 ); - - status = mongo_replset_connect( conn ); - - if( status != MONGO_OK ) { - // Check conn->err for error code. - } - - mongo_destroy( conn ); - - return 0; - } - -BSON ----- - -MongoDB database stores data in a format called *BSON*. BSON is a JSON-like binary object format. -To create BSON objects - - -.. code-block:: c - - bson b[1]; - - bson_init( b ) - bson_append_string( b, "name", "Joe" ); - bson_append_int( b, "age", 33 ); - bson_finish( b ); - - mongo_insert( conn, b ); - - bson_destroy( b ); - -Use the ``bson_append_new_oid()`` function to add an object id to your object. -The server will add an object id to the ``_id`` field if it is not included explicitly, -but it's best to create it client-side. When you do create the id, be sure to place it -at the beginning of the object, as we do here: - -.. code-block:: c - - bson b[1]; - - bson_init( b ); - bson_append_new_oid( b, "_id" ); - bson_append_string( b, "name", "Joe" ); - bson_append_int( b, "age", 33 ); - bson_finish( b ); - -When you're done using the ``bson`` object, remember pass it to -``bson_destroy()`` to free up the memory allocated by the buffer. - -.. code-block:: c - - bson_destroy( b ); - -Inserting a single document ---------------------------- - -Here's how we save our person object to the database's "people" collection: - -.. code-block:: c - - mongo_insert( conn, "tutorial.people", b ); - -The first parameter to ``mongo_insert`` is the pointer to the ``mongo_connection`` -object. The second parameter is the namespace, which include the database name, followed -by a dot followed by the collection name. Thus, ``tutorial`` is the database and ``people`` -is the collection name. The third parameter is a pointer to the ``bson`` object that -we created before. - -Inserting a batch of documents ------------------------------- - -We can do batch inserts as well: - -.. code-block:: c - - static void tutorial_insert_batch( mongo_connection *conn ) { - bson *p, **ps; - char *names[4]; - int ages[] = { 29, 24, 24, 32 }; - int i, n = 4; - names[0] = "Eliot"; names[1] = "Mike"; names[2] = "Mathias"; names[3] = "Richard"; - - ps = ( bson ** )malloc( sizeof( bson * ) * n); - - for ( i = 0; i < n; i++ ) { - p = ( bson * )malloc( sizeof( bson ) ); - bson_init( p ); - bson_append_new_oid( p_buf, "_id" ); - bson_append_string( p_buf, "name", names[i] ); - bson_append_int( p_buf, "age", ages[i] ); - bson_finish( p ); - ps[i] = p; - } - - mongo_insert_batch( conn, "tutorial.persons", ps, n ); - - for ( i = 0; i < n; i++ ) { - bson_destroy( ps[i] ); - free( ps[i] ); - } - } - -Simple Queries --------------- - -Let's now fetch all objects from the ``persons`` collection, and display them. - -.. code-block:: c - - static void tutorial_empty_query( mongo_connection *conn) { - mongo_cursor cursor[1]; - mongo_cursor_init( cursor, conn, "tutorial.persons" ); - - while( mongo_cursor_next( cursor ) == MONGO_OK ) - bson_print( &cursor->current ); - - mongo_cursor_destroy( cursor ); - } - -Here we use the most basic possible cursor, which iterates over all documents. This is the -equivalent of running ``db.persons.find()`` from the shell. - -You initialize a cursor with ``mongo_cursor_init()``. Whenever you finish with a cursor, -you must pass it to ``mongo_cursor_destroy()``. - -We use ``bson_print()`` to print an abbreviated JSON string representation of the object. - -Let's now write a function which prints out the name of all persons -whose age is 24: - -.. code-block:: c - - static void tutorial_simple_query( mongo_connection *conn ) { - bson query[1]; - mongo_cursor cursor[1]; - - bson_init( query ); - bson_append_int( query_buf, "age", 24 ); - bson_finish( query ); - - mongo_cursor_init( cursor, conn, "tutorial.persons" ); - mongo_cursor_set_query( cursor, query ); - - while( mongo_cursor_next( cursor ) == MONGO_OK ) { - bson_iterator iterator[1]; - if ( bson_find( iterator, mongo_cursor_bson( cursor ), "name" )) { - printf( "name: %s\n", bson_iterator_string( iterator ) ); - } - } - - bson_destroy( query ); - mongo_cursor_destroy( cursor ); - } - -Our query above, written as JSON, is equivalent to the following from the JavaScript shell: - -.. code-block:: javascript - - use tutorial - db.persons.find( { age: 24 } ) - -Complex Queries ---------------- - -Sometimes we want to do more then a simple query. We may want the results to -be sorted in a special way, or what the query to use a certain index. - -Let's add a sort clause to our previous query. This requires some knowledge of the -implementation of query specs in MongoDB. A query spec can either consist of: - -1. A query matcher alone, as in our previous example. - -or - -2. A query matcher, sort clause, hint enforcer, or explain directive. Each of these - is wrapped by the keys ``$query``, ``$orderby``, ``$hint``, and ``$explain``, respectively. - Most of the time, you'll only use ``$query`` and ``$orderby``. - -To add a sort clause to our previous query, we change our query spec from this: - -.. code-block:: c - - bson_init( query ); - bson_append_int( query, "age", 24 ); - bson_finish( query ); - -to this: - -.. code-block:: c - - bson_init( query ); - bson_append_start_object( query, "$query" ); - bson_append_int( query, "age", 24 ); - bson_append_finish_object( query ); - - bson_append_start_object( query, "$orderby" ); - bson_append_int( query, "name", 1); - bson_append_finish_object( query ); - bson_finish( query ); - -This is equivalent to the following query from the MongoDB shell: - -.. code-block:: javascript - - db.persons.find( { age: 24 } ).sort( { name: 1 } ); - - -Updating documents ------------------- - -Use the ``mongo_update()`` function to perform updates. -For example the following update in the MongoDB shell: - -.. code-block:: javascript - - use tutorial - db.persons.update( { name : 'Joe', age : 33 }, - { $inc : { visits : 1 } } ) - -is equivalent to the following C function: - -.. code-block:: c - - static void tutorial_update( mongo_connection *conn ) { - bson cond[1], op[1]; - - bson_init( cond ); - bson_append_string( cond, "name", "Joe"); - bson_append_int( cond, "age", 33); - bson_finish( cond ); - - bson_init( op ); - bson_append_start_object( op, "$inc" ); - bson_append_int( op, "visits", 1 ); - bson_append_finish_object( op ); - bson_finish( op ); - - mongo_update( conn, "tutorial.persons", cond, op, MONGO_UPDATE_BASIC ); - - bson_destroy( cond ); - bson_destroy( op ); - } - -The final argument to ``mongo_update()`` is a bitfield storing update options. If -you want to update all documents matching the ``cond``, you must use ``MONGO_UPDATE_MULTI``. -For upserts, use ``MONGO_UPDATE_UPSERT``. Here's an example: - -.. code-block:: c - - mongo_update( conn, "tutorial.persons", cond, op, MONGO_UPDATE_MULTI ); - -Indexing --------- - -Now we'll create a couple of indexes. The first is a simple index on ``name``, and -the second is a compound index on ``name`` and ``age``. - -.. code-block:: c - - static void tutorial_index( mongo_connection *conn ) { - bson key[1]; - - bson_init( key ); - bson_append_int( key, "name", 1 ); - bson_finish( key ); - - mongo_create_index( conn, "tutorial.persons", key, 0, NULL ); - - bson_destroy( key ); - - printf( "simple index created on \"name\"\n" ); - - bson_init( key ); - bson_append_int( key, "age", 1 ); - bson_append_int( key, "name", 1 ); - bson_finish( key ); - - mongo_create_index( conn, "tutorial.persons", key, 0, NULL ); - - bson_destroy( key ); - - printf( "compound index created on \"age\", \"name\"\n" ); - } - - - -Further Reading ---------------- - -This overview just touches on the basics of using Mongo from C. For more examples, -check out the other documentation pages, and have a look at the driver's test cases. diff --git a/mongo-c-driver-v0.6/docs/source/sphinx/source/write_concern.rst b/mongo-c-driver-v0.6/docs/source/sphinx/source/write_concern.rst deleted file mode 100644 index 03c766b..0000000 --- a/mongo-c-driver-v0.6/docs/source/sphinx/source/write_concern.rst +++ /dev/null @@ -1,106 +0,0 @@ -Write Concern (a.k.a. "Safe Mode") -================================== - -All writes issued from the drivers for MongoDB are "fire-and-forget" by default. -In practice, this means that by default, failed writes aren't reported. -For this reason, "fire-and-forget" writes are recommended -only for cases where losing a few writes is acceptable (logging, anayltics, etc.). - -In all other scenarios, you should ensure that your writes run as a round trip -to the server. This requires that you enable write concern or "safe mode", as it's -called elsewhere. - -In addition to reporting write errors, write concern also allows you to ensure -that your write are replicated to a particular number of servers to a set -of servers tagged with a given value. See the -`write concern docs `_ for details. - -Implementation and API ----------------------- - -Write concern is implemented by appending a call to the ``getlasterror`` -command after each write. You can certainly do this manually, but nearly all of the drivers -provide a write concern API for simplicty. To read about the options for ``getlasterror``, -and hence the options for write concern, -`see the MongoDB getlasterror docs `_. - -The MongoDB C driver supports write concern on two levels. You can set the write -concern on a ``mongo`` connection object, in which case that write concern level will -be used for every write. You can also specify a write concern for any individual -write operation (``mongo_insert()``, ``mongo_insert_batch()``, ``mongo_update()``, -or ``mongo_remove``). This will override any default write concern set on the -connection level. - -Example -------- - -.. code-block:: c - - #include - #include - - #define ASSERT(x) \ - do{ \ - if(!(x)){ \ - printf("\nFailed ASSERT [%s] (%d):\n %s\n\n", __FILE__, __LINE__, #x); \ - exit(1); \ - }\ - }while(0) - - int main() { - mongo conn[1]; - mongo_write_concern write_concern[1]; - bson b[1]; - - if( mongo_connect( conn, "127.0.0.1", 27017 ) == MONGO_ERROR ) { - printf( "Failed to connect!\n" ); - exit(1); - } - - mongo_cmd_drop_collection( conn, "test", "foo", NULL ); - - /* Initialize the write concern object.*/ - mongo_write_concern_init( write_concern ); - write_concern->w = 1; - mongo_write_concern_finish( write_concern ); - - bson_init( b ); - bson_append_new_oid( b ); - bson_finish( b ); - - ASSERT( mongo_insert( conn, "test.foo", b, wc ) == MONGO_ERROR ); - - /* If we try to insert the same document again, - we'll get an error due to the unique index on _id.*/ - ASSERT( mongo_insert( conn, "test.foo", b, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - printf( "Error message: %s\n", conn->lasterrstr ); - - /* Clear any stored errors.*/ - mongo_clear_errors( conn ); - - /* We'll get the same error if we set a default write concern - on the connection object but don't set it on insert.*/ - mongo_set_write_concern( conn, write_concern ); - ASSERT( mongo_insert( conn, "test.foo", b, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - printf( "Error message: %s\n", conn->lasterrstr ); - - mongo_write_concern_destroy( write_concern ); - bson_destroy( b ); - mongo_destroy( conn ); - - return 0; - } - -Notes ------ - -As you'll see in the code sample, the process for creating a write concern object -is to initialize it, manually set any write concern values (e.g., ``w``, ``wtimeout`` -for values of ``w`` greater than 1, ``j``, etc.), and then call ``mongo_write_concern_finish()`` -on it. This will effectively create the equivalent ``getlasterror`` command. Note you must call -``mongo_write_concern_destroy()`` when you're finished with the write concern object. - -And for a longer example, see the -`C driver's write concern tests `_. diff --git a/mongo-c-driver-v0.6/doxygenConfig b/mongo-c-driver-v0.6/doxygenConfig deleted file mode 100644 index 310ffd2..0000000 --- a/mongo-c-driver-v0.6/doxygenConfig +++ /dev/null @@ -1,316 +0,0 @@ -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = MongoDB C Driver -PROJECT_NUMBER = 0.6 -OUTPUT_DIRECTORY = docs/source/doxygen -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ABBREVIATE_BRIEF = "The $name class" \ - "The $name widget" \ - "The $name file" \ - is \ - provides \ - specifies \ - contains \ - represents \ - a \ - an \ - the -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = NO -STRIP_FROM_PATH = -STRIP_FROM_INC_PATH = -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = YES -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 8 -ALIASES = -OPTIMIZE_OUTPUT_FOR_C = NO -OPTIMIZE_OUTPUT_JAVA = NO -OPTIMIZE_FOR_FORTRAN = NO -OPTIMIZE_OUTPUT_VHDL = NO -EXTENSION_MAPPING = -BUILTIN_STL_SUPPORT = NO -CPP_CLI_SUPPORT = NO -SIP_SUPPORT = NO -IDL_PROPERTY_SUPPORT = YES -DISTRIBUTE_GROUP_DOC = NO -SUBGROUPING = YES -TYPEDEF_HIDES_STRUCT = NO -SYMBOL_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# *** -# ERH - this controls whether all classes in files are documented or just the ones with tags -# *** -EXTRACT_ALL = NO - -EXTRACT_PRIVATE = NO -EXTRACT_STATIC = NO -EXTRACT_LOCAL_CLASSES = NO -EXTRACT_LOCAL_METHODS = NO -EXTRACT_ANON_NSPACES = NO -HIDE_UNDOC_MEMBERS = NO -HIDE_UNDOC_CLASSES = NO -HIDE_FRIEND_COMPOUNDS = NO -HIDE_IN_BODY_DOCS = NO -INTERNAL_DOCS = NO -CASE_SENSE_NAMES = NO -HIDE_SCOPE_NAMES = NO -SHOW_INCLUDE_FILES = YES -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO -SHOW_FILES = YES -SHOW_NAMESPACES = YES -FILE_VERSION_FILTER = -LAYOUT_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_NO_PARAMDOC = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- -INPUT = src -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.vhd \ - *.vhdl -RECURSIVE = YES -EXCLUDE = -EXCLUDE_SYMLINKS = NO -EXCLUDE_PATTERNS = -EXCLUDE_SYMBOLS = -EXAMPLE_PATH = -EXAMPLE_PATTERNS = * -EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -USE_HTAGS = NO -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- -ALPHABETICAL_INDEX = NO -COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- -GENERATE_HTML = YES -HTML_OUTPUT = html -HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = -HTML_ALIGN_MEMBERS = YES -HTML_DYNAMIC_SECTIONS = NO -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -GENERATE_HTMLHELP = NO -CHM_FILE = -HHC_LOCATION = -GENERATE_CHI = NO -CHM_INDEX_ENCODING = -BINARY_TOC = NO -TOC_EXPAND = NO -GENERATE_QHP = NO -QCH_FILE = -QHP_NAMESPACE = -QHP_VIRTUAL_FOLDER = doc -QHP_CUST_FILTER_NAME = -QHP_CUST_FILTER_ATTRS = -QHP_SECT_FILTER_ATTRS = -QHG_LOCATION = -DISABLE_INDEX = NO -ENUM_VALUES_PER_LINE = 4 -GENERATE_TREEVIEW = NONE -TREEVIEW_WIDTH = 250 -FORMULA_FONTSIZE = 10 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- -GENERATE_LATEX = YES -LATEX_OUTPUT = latex -LATEX_CMD_NAME = latex -MAKEINDEX_CMD_NAME = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = -PDF_HYPERLINKS = YES -USE_PDFLATEX = YES -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- -GENERATE_RTF = NO -RTF_OUTPUT = rtf -COMPACT_RTF = NO -RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- -GENERATE_MAN = NO -MAN_OUTPUT = man -MAN_EXTENSION = .3 -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- -GENERATE_XML = NO -XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- -GENERATE_PERLMOD = NO -PERLMOD_LATEX = NO -PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO -EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = -EXPAND_AS_DEFINED = -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- -TAGFILES = -GENERATE_TAGFILE = -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES -MSCGEN_PATH = -HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO -DOT_FONTNAME = FreeSans -DOT_FONTSIZE = 10 -DOT_FONTPATH = -CLASS_GRAPH = YES -COLLABORATION_GRAPH = YES -GROUP_GRAPHS = YES -UML_LOOK = NO -TEMPLATE_RELATIONS = NO -INCLUDE_GRAPH = YES -INCLUDED_BY_GRAPH = YES -CALL_GRAPH = NO -CALLER_GRAPH = NO -GRAPHICAL_HIERARCHY = YES -DIRECTORY_GRAPH = YES -DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = -DOT_GRAPH_MAX_NODES = 50 -MAX_DOT_GRAPH_DEPTH = 0 -DOT_TRANSPARENT = NO -DOT_MULTI_TARGETS = NO -GENERATE_LEGEND = YES -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Options related to the search engine -#--------------------------------------------------------------------------- -SEARCHENGINE = NO diff --git a/mongo-c-driver-v0.6/runtests.sh b/mongo-c-driver-v0.6/runtests.sh deleted file mode 100644 index 3723753..0000000 --- a/mongo-c-driver-v0.6/runtests.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# Arguments -v for valgrind - -usage() -{ -cat < -#include -#include -#include -#include - -#include "bson.h" -#include "encoding.h" - -const int initialBufferSize = 128; - -/* only need one of these */ -static const int zero = 0; - -/* Custom standard function pointers. */ -void *( *bson_malloc_func )( size_t ) = malloc; -void *( *bson_realloc_func )( void *, size_t ) = realloc; -void ( *bson_free_func )( void * ) = free; -#ifdef R_SAFETY_NET -bson_printf_func bson_printf; -#else -bson_printf_func bson_printf = printf; -#endif -bson_fprintf_func bson_fprintf = fprintf; -bson_sprintf_func bson_sprintf = sprintf; - -static int _bson_errprintf( const char *, ... ); -bson_printf_func bson_errprintf = _bson_errprintf; - -/* ObjectId fuzz functions. */ -static int ( *oid_fuzz_func )( void ) = NULL; -static int ( *oid_inc_func )( void ) = NULL; - -/* ---------------------------- - READING - ------------------------------ */ - -MONGO_EXPORT bson* bson_create() { - return (bson*)bson_malloc(sizeof(bson)); -} - -MONGO_EXPORT void bson_dispose(bson* b) { - bson_free(b); -} - -MONGO_EXPORT bson *bson_empty( bson *obj ) { - static char *data = "\005\0\0\0\0"; - bson_init_data( obj, data ); - obj->finished = 1; - obj->err = 0; - obj->errstr = NULL; - obj->stackPos = 0; - return obj; -} - -MONGO_EXPORT int bson_copy( bson *out, const bson *in ) { - if ( !out ) return BSON_ERROR; - if ( !in->finished ) return BSON_ERROR; - bson_init_size( out, bson_size( in ) ); - memcpy( out->data, in->data, bson_size( in ) ); - out->finished = 1; - - return BSON_OK; -} - -int bson_init_data( bson *b, char *data ) { - b->data = data; - return BSON_OK; -} - -int bson_init_finished_data( bson *b, char *data ) { - bson_init_data( b, data ); - b->finished = 1; - return BSON_OK; -} - -static void _bson_reset( bson *b ) { - b->finished = 0; - b->stackPos = 0; - b->err = 0; - b->errstr = NULL; -} - -MONGO_EXPORT int bson_size( const bson *b ) { - int i; - if ( ! b || ! b->data ) - return 0; - bson_little_endian32( &i, b->data ); - return i; -} - -MONGO_EXPORT int bson_buffer_size( const bson *b ) { - return (b->cur - b->data + 1); -} - - -MONGO_EXPORT const char *bson_data( const bson *b ) { - return (const char *)b->data; -} - -static char hexbyte( char hex ) { - switch ( hex ) { - case '0': - return 0x0; - case '1': - return 0x1; - case '2': - return 0x2; - case '3': - return 0x3; - case '4': - return 0x4; - case '5': - return 0x5; - case '6': - return 0x6; - case '7': - return 0x7; - case '8': - return 0x8; - case '9': - return 0x9; - case 'a': - case 'A': - return 0xa; - case 'b': - case 'B': - return 0xb; - case 'c': - case 'C': - return 0xc; - case 'd': - case 'D': - return 0xd; - case 'e': - case 'E': - return 0xe; - case 'f': - case 'F': - return 0xf; - default: - return 0x0; /* something smarter? */ - } -} - -MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str ) { - int i; - for ( i=0; i<12; i++ ) { - oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] ); - } -} - -MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str ) { - static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - int i; - for ( i=0; i<12; i++ ) { - str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4]; - str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ]; - } - str[24] = '\0'; -} - -MONGO_EXPORT void bson_set_oid_fuzz( int ( *func )( void ) ) { - oid_fuzz_func = func; -} - -MONGO_EXPORT void bson_set_oid_inc( int ( *func )( void ) ) { - oid_inc_func = func; -} - -MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid ) { - static int incr = 0; - static int fuzz = 0; - int i; - int t = time( NULL ); - - if( oid_inc_func ) - i = oid_inc_func(); - else - i = incr++; - - if ( !fuzz ) { - if ( oid_fuzz_func ) - fuzz = oid_fuzz_func(); - else { - srand( t ); - fuzz = rand(); - } - } - - bson_big_endian32( &oid->ints[0], &t ); - oid->ints[1] = fuzz; - bson_big_endian32( &oid->ints[2], &i ); -} - -MONGO_EXPORT time_t bson_oid_generated_time( bson_oid_t *oid ) { - time_t out; - bson_big_endian32( &out, &oid->ints[0] ); - - return out; -} - -MONGO_EXPORT void bson_print( const bson *b ) { - bson_print_raw( b->data , 0 ); -} - -MONGO_EXPORT void bson_print_raw( const char *data , int depth ) { - bson_iterator i; - const char *key; - int temp; - bson_timestamp_t ts; - char oidhex[25]; - bson scope; - bson_iterator_from_buffer( &i, data ); - - while ( bson_iterator_next( &i ) ) { - bson_type t = bson_iterator_type( &i ); - if ( t == 0 ) - break; - key = bson_iterator_key( &i ); - - for ( temp=0; temp<=depth; temp++ ) - bson_printf( "\t" ); - bson_printf( "%s : %d \t " , key , t ); - switch ( t ) { - case BSON_DOUBLE: - bson_printf( "%f" , bson_iterator_double( &i ) ); - break; - case BSON_STRING: - bson_printf( "%s" , bson_iterator_string( &i ) ); - break; - case BSON_SYMBOL: - bson_printf( "SYMBOL: %s" , bson_iterator_string( &i ) ); - break; - case BSON_OID: - bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); - bson_printf( "%s" , oidhex ); - break; - case BSON_BOOL: - bson_printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); - break; - case BSON_DATE: - bson_printf( "%ld" , ( long int )bson_iterator_date( &i ) ); - break; - case BSON_BINDATA: - bson_printf( "BSON_BINDATA" ); - break; - case BSON_UNDEFINED: - bson_printf( "BSON_UNDEFINED" ); - break; - case BSON_NULL: - bson_printf( "BSON_NULL" ); - break; - case BSON_REGEX: - bson_printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) ); - break; - case BSON_CODE: - bson_printf( "BSON_CODE: %s", bson_iterator_code( &i ) ); - break; - case BSON_CODEWSCOPE: - bson_printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); - bson_init( &scope ); - bson_iterator_code_scope( &i, &scope ); - bson_printf( "\n\t SCOPE: " ); - bson_print( &scope ); - break; - case BSON_INT: - bson_printf( "%d" , bson_iterator_int( &i ) ); - break; - case BSON_LONG: - bson_printf( "%lld" , ( uint64_t )bson_iterator_long( &i ) ); - break; - case BSON_TIMESTAMP: - ts = bson_iterator_timestamp( &i ); - bson_printf( "i: %d, t: %d", ts.i, ts.t ); - break; - case BSON_OBJECT: - case BSON_ARRAY: - bson_printf( "\n" ); - bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); - break; - default: - bson_errprintf( "can't print type : %d\n" , t ); - } - bson_printf( "\n" ); - } -} - -/* ---------------------------- - ITERATOR - ------------------------------ */ - -MONGO_EXPORT bson_iterator* bson_iterator_create() { - return ( bson_iterator* )malloc( sizeof( bson_iterator ) ); -} - -MONGO_EXPORT void bson_iterator_dispose(bson_iterator* i) { - free(i); -} - -MONGO_EXPORT void bson_iterator_init( bson_iterator *i, const bson *b ) { - i->cur = b->data + 4; - i->first = 1; -} - -MONGO_EXPORT void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) { - i->cur = buffer + 4; - i->first = 1; -} - -MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) { - bson_iterator_init( it, (bson *)obj ); - while( bson_iterator_next( it ) ) { - if ( strcmp( name, bson_iterator_key( it ) ) == 0 ) - break; - } - return bson_iterator_type( it ); -} - -MONGO_EXPORT bson_bool_t bson_iterator_more( const bson_iterator *i ) { - return *( i->cur ); -} - -MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i ) { - int ds; - - if ( i->first ) { - i->first = 0; - return ( bson_type )( *i->cur ); - } - - switch ( bson_iterator_type( i ) ) { - case BSON_EOO: - return BSON_EOO; /* don't advance */ - case BSON_UNDEFINED: - case BSON_NULL: - ds = 0; - break; - case BSON_BOOL: - ds = 1; - break; - case BSON_INT: - ds = 4; - break; - case BSON_LONG: - case BSON_DOUBLE: - case BSON_TIMESTAMP: - case BSON_DATE: - ds = 8; - break; - case BSON_OID: - ds = 12; - break; - case BSON_STRING: - case BSON_SYMBOL: - case BSON_CODE: - ds = 4 + bson_iterator_int_raw( i ); - break; - case BSON_BINDATA: - ds = 5 + bson_iterator_int_raw( i ); - break; - case BSON_OBJECT: - case BSON_ARRAY: - case BSON_CODEWSCOPE: - ds = bson_iterator_int_raw( i ); - break; - case BSON_DBREF: - ds = 4+12 + bson_iterator_int_raw( i ); - break; - case BSON_REGEX: { - const char *s = bson_iterator_value( i ); - const char *p = s; - p += strlen( p )+1; - p += strlen( p )+1; - ds = p-s; - break; - } - - default: { - char msg[] = "unknown type: 000000000000"; - bson_numstr( msg+14, ( unsigned )( i->cur[0] ) ); - bson_fatal_msg( 0, msg ); - return 0; - } - } - - i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds; - - return ( bson_type )( *i->cur ); -} - -MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i ) { - return ( bson_type )i->cur[0]; -} - -MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i ) { - return i->cur + 1; -} - -MONGO_EXPORT const char *bson_iterator_value( const bson_iterator *i ) { - const char *t = i->cur + 1; - t += strlen( t ) + 1; - return t; -} - -/* types */ - -int bson_iterator_int_raw( const bson_iterator *i ) { - int out; - bson_little_endian32( &out, bson_iterator_value( i ) ); - return out; -} - -double bson_iterator_double_raw( const bson_iterator *i ) { - double out; - bson_little_endian64( &out, bson_iterator_value( i ) ); - return out; -} - -int64_t bson_iterator_long_raw( const bson_iterator *i ) { - int64_t out; - bson_little_endian64( &out, bson_iterator_value( i ) ); - return out; -} - -bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) { - return bson_iterator_value( i )[0]; -} - -MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i ) { - return ( bson_oid_t * )bson_iterator_value( i ); -} - -MONGO_EXPORT int bson_iterator_int( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_INT: - return bson_iterator_int_raw( i ); - case BSON_LONG: - return bson_iterator_long_raw( i ); - case BSON_DOUBLE: - return bson_iterator_double_raw( i ); - default: - return 0; - } -} - -MONGO_EXPORT double bson_iterator_double( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_INT: - return bson_iterator_int_raw( i ); - case BSON_LONG: - return bson_iterator_long_raw( i ); - case BSON_DOUBLE: - return bson_iterator_double_raw( i ); - default: - return 0; - } -} - -MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_INT: - return bson_iterator_int_raw( i ); - case BSON_LONG: - return bson_iterator_long_raw( i ); - case BSON_DOUBLE: - return bson_iterator_double_raw( i ); - default: - return 0; - } -} - -MONGO_EXPORT bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) { - bson_timestamp_t ts; - bson_little_endian32( &( ts.i ), bson_iterator_value( i ) ); - bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 ); - return ts; -} - - -MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i ) { - int time; - bson_little_endian32( &time, bson_iterator_value( i ) + 4 ); - return time; -} - - -MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i ) { - int increment; - bson_little_endian32( &increment, bson_iterator_value( i ) ); - return increment; -} - - -MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_BOOL: - return bson_iterator_bool_raw( i ); - case BSON_INT: - return bson_iterator_int_raw( i ) != 0; - case BSON_LONG: - return bson_iterator_long_raw( i ) != 0; - case BSON_DOUBLE: - return bson_iterator_double_raw( i ) != 0; - case BSON_EOO: - case BSON_NULL: - return 0; - default: - return 1; - } -} - -MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_STRING: - case BSON_SYMBOL: - return bson_iterator_value( i ) + 4; - default: - return ""; - } -} - -int bson_iterator_string_len( const bson_iterator *i ) { - return bson_iterator_int_raw( i ); -} - -MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i ) { - switch ( bson_iterator_type( i ) ) { - case BSON_STRING: - case BSON_CODE: - return bson_iterator_value( i ) + 4; - case BSON_CODEWSCOPE: - return bson_iterator_value( i ) + 8; - default: - return NULL; - } -} - -MONGO_EXPORT void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) { - if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) { - int code_len; - bson_little_endian32( &code_len, bson_iterator_value( i )+4 ); - bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) ); - _bson_reset( scope ); - scope->finished = 1; - } else { - bson_empty( scope ); - } -} - -MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i ) { - return bson_iterator_long_raw( i ); -} - -MONGO_EXPORT time_t bson_iterator_time_t( const bson_iterator *i ) { - return bson_iterator_date( i ) / 1000; -} - -MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i ) { - return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) - ? bson_iterator_int_raw( i ) - 4 - : bson_iterator_int_raw( i ); -} - -MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i ) { - return bson_iterator_value( i )[4]; -} - -MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i ) { - return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) - ? bson_iterator_value( i ) + 9 - : bson_iterator_value( i ) + 5; -} - -MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i ) { - return bson_iterator_value( i ); -} - -MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i ) { - const char *p = bson_iterator_value( i ); - return p + strlen( p ) + 1; - -} - -MONGO_EXPORT void bson_iterator_subobject( const bson_iterator *i, bson *sub ) { - bson_init_data( sub, ( char * )bson_iterator_value( i ) ); - _bson_reset( sub ); - sub->finished = 1; -} - -MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) { - bson_iterator_from_buffer( sub, bson_iterator_value( i ) ); -} - -/* ---------------------------- - BUILDING - ------------------------------ */ - -static void _bson_init_size( bson *b, int size ) { - if( size == 0 ) - b->data = NULL; - else - b->data = ( char * )bson_malloc( size ); - b->dataSize = size; - b->cur = b->data + 4; - _bson_reset( b ); -} - -MONGO_EXPORT void bson_init( bson *b ) { - _bson_init_size( b, initialBufferSize ); -} - -void bson_init_size( bson *b, int size ) { - _bson_init_size( b, size ); -} - -void bson_append_byte( bson *b, char c ) { - b->cur[0] = c; - b->cur++; -} - -void bson_append( bson *b, const void *data, int len ) { - memcpy( b->cur , data , len ); - b->cur += len; -} - -void bson_append32( bson *b, const void *data ) { - bson_little_endian32( b->cur, data ); - b->cur += 4; -} - -void bson_append64( bson *b, const void *data ) { - bson_little_endian64( b->cur, data ); - b->cur += 8; -} - -int bson_ensure_space( bson *b, const int bytesNeeded ) { - int pos = b->cur - b->data; - char *orig = b->data; - int new_size; - - if ( pos + bytesNeeded <= b->dataSize ) - return BSON_OK; - - new_size = 1.5 * ( b->dataSize + bytesNeeded ); - - if( new_size < b->dataSize ) { - if( ( b->dataSize + bytesNeeded ) < INT_MAX ) - new_size = INT_MAX; - else { - b->err = BSON_SIZE_OVERFLOW; - return BSON_ERROR; - } - } - - b->data = bson_realloc( b->data, new_size ); - if ( !b->data ) - bson_fatal_msg( !!b->data, "realloc() failed" ); - - b->dataSize = new_size; - b->cur += b->data - orig; - - return BSON_OK; -} - -MONGO_EXPORT int bson_finish( bson *b ) { - int i; - - if( b->err & BSON_NOT_UTF8 ) - return BSON_ERROR; - - if ( ! b->finished ) { - if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; - bson_append_byte( b, 0 ); - i = b->cur - b->data; - bson_little_endian32( b->data, &i ); - b->finished = 1; - } - - return BSON_OK; -} - -MONGO_EXPORT void bson_destroy( bson *b ) { - if (b) { - bson_free( b->data ); - b->err = 0; - b->data = 0; - b->cur = 0; - b->finished = 1; - } -} - -static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) { - const int len = strlen( name ) + 1; - - if ( b->finished ) { - b->err |= BSON_ALREADY_FINISHED; - return BSON_ERROR; - } - - if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) { - return BSON_ERROR; - } - - if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) { - bson_builder_error( b ); - return BSON_ERROR; - } - - bson_append_byte( b, ( char )type ); - bson_append( b, name, len ); - return BSON_OK; -} - -/* ---------------------------- - BUILDING TYPES - ------------------------------ */ - -MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i ) { - if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append32( b , &i ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i ) { - if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append64( b , &i ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d ) { - if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append64( b , &d ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) { - if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append_byte( b , i != 0 ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_null( bson *b, const char *name ) { - if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR ) - return BSON_ERROR; - return BSON_OK; -} - -MONGO_EXPORT int bson_append_undefined( bson *b, const char *name ) { - if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR ) - return BSON_ERROR; - return BSON_OK; -} - -int bson_append_string_base( bson *b, const char *name, - const char *value, int len, bson_type type ) { - - int sl = len + 1; - if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR ) - return BSON_ERROR; - if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) { - return BSON_ERROR; - } - bson_append32( b , &sl ); - bson_append( b , value , sl - 1 ); - bson_append( b , "\0" , 1 ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *value ) { - return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING ); -} - -MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *value ) { - return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL ); -} - -MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *value ) { - return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE ); -} - -MONGO_EXPORT int bson_append_string_n( bson *b, const char *name, const char *value, int len ) { - return bson_append_string_base( b, name, value, len, BSON_STRING ); -} - -MONGO_EXPORT int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) { - return bson_append_string_base( b, name, value, len, BSON_SYMBOL ); -} - -MONGO_EXPORT int bson_append_code_n( bson *b, const char *name, const char *value, int len ) { - return bson_append_string_base( b, name, value, len, BSON_CODE ); -} - -MONGO_EXPORT int bson_append_code_w_scope_n( bson *b, const char *name, - const char *code, int len, const bson *scope ) { - - int sl = len + 1; - int size = 4 + 4 + sl + bson_size( scope ); - if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR ) - return BSON_ERROR; - bson_append32( b, &size ); - bson_append32( b, &sl ); - bson_append( b, code, sl ); - bson_append( b, scope->data, bson_size( scope ) ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) { - return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope ); -} - -MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) { - if ( type == BSON_BIN_BINARY_OLD ) { - int subtwolen = len + 4; - if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR ) - return BSON_ERROR; - bson_append32( b, &subtwolen ); - bson_append_byte( b, type ); - bson_append32( b, &len ); - bson_append( b, str, len ); - } else { - if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR ) - return BSON_ERROR; - bson_append32( b, &len ); - bson_append_byte( b, type ); - bson_append( b, str, len ); - } - return BSON_OK; -} - -MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) { - if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append( b , oid , 12 ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_new_oid( bson *b, const char *name ) { - bson_oid_t oid; - bson_oid_gen( &oid ); - return bson_append_oid( b, name, &oid ); -} - -MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) { - const int plen = strlen( pattern )+1; - const int olen = strlen( opts )+1; - if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR ) - return BSON_ERROR; - if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR ) - return BSON_ERROR; - bson_append( b , pattern , plen ); - bson_append( b , opts , olen ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson ) { - if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR ) - return BSON_ERROR; - bson_append( b , bson->data , bson_size( bson ) ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) { - bson_iterator next = *elem; - int size; - - bson_iterator_next( &next ); - size = next.cur - elem->cur; - - if ( name_or_null == NULL ) { - if( bson_ensure_space( b, size ) == BSON_ERROR ) - return BSON_ERROR; - bson_append( b, elem->cur, size ); - } else { - int data_size = size - 2 - strlen( bson_iterator_key( elem ) ); - bson_append_estart( b, elem->cur[0], name_or_null, data_size ); - bson_append( b, bson_iterator_value( elem ), data_size ); - } - - return BSON_OK; -} - -MONGO_EXPORT int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) { - if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR; - - bson_append32( b , &( ts->i ) ); - bson_append32( b , &( ts->t ) ); - - return BSON_OK; -} - -MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int increment ) { - if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR; - - bson_append32( b , &increment ); - bson_append32( b , &time ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis ) { - if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR; - bson_append64( b , &millis ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_time_t( bson *b, const char *name, time_t secs ) { - return bson_append_date( b, name, ( bson_date_t )secs * 1000 ); -} - -MONGO_EXPORT int bson_append_start_object( bson *b, const char *name ) { - if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR; - b->stack[ b->stackPos++ ] = b->cur - b->data; - bson_append32( b , &zero ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_start_array( bson *b, const char *name ) { - if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR; - b->stack[ b->stackPos++ ] = b->cur - b->data; - bson_append32( b , &zero ); - return BSON_OK; -} - -MONGO_EXPORT int bson_append_finish_object( bson *b ) { - char *start; - int i; - if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; - bson_append_byte( b , 0 ); - - start = b->data + b->stack[ --b->stackPos ]; - i = b->cur - start; - bson_little_endian32( start, &i ); - - return BSON_OK; -} - -MONGO_EXPORT double bson_int64_to_double( int64_t i64 ) { - return (double)i64; -} - -MONGO_EXPORT int bson_append_finish_array( bson *b ) { - return bson_append_finish_object( b ); -} - -/* Error handling and allocators. */ - -static bson_err_handler err_handler = NULL; - -MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func ) { - bson_err_handler old = err_handler; - err_handler = func; - return old; -} - -MONGO_EXPORT void bson_free( void *ptr ) { - bson_free_func( ptr ); -} - -MONGO_EXPORT void *bson_malloc( int size ) { - void *p; - p = bson_malloc_func( size ); - bson_fatal_msg( !!p, "malloc() failed" ); - return p; -} - -void *bson_realloc( void *ptr, int size ) { - void *p; - p = bson_realloc_func( ptr, size ); - bson_fatal_msg( !!p, "realloc() failed" ); - return p; -} - -int _bson_errprintf( const char *format, ... ) { - va_list ap; - int ret; - va_start( ap, format ); -#ifndef R_SAFETY_NET - ret = vfprintf( stderr, format, ap ); -#endif - va_end( ap ); - - return ret; -} - -/** - * This method is invoked when a non-fatal bson error is encountered. - * Calls the error handler if available. - * - * @param - */ -void bson_builder_error( bson *b ) { - if( err_handler ) - err_handler( "BSON error." ); -} - -void bson_fatal( int ok ) { - bson_fatal_msg( ok, "" ); -} - -void bson_fatal_msg( int ok , const char *msg ) { - if ( ok ) - return; - - if ( err_handler ) { - err_handler( msg ); - } -#ifndef R_SAFETY_NET - bson_errprintf( "error: %s\n" , msg ); - exit( -5 ); -#endif -} - - -/* Efficiently copy an integer to a string. */ -extern const char bson_numstrs[1000][4]; - -void bson_numstr( char *str, int i ) { - if( i < 1000 ) - memcpy( str, bson_numstrs[i], 4 ); - else - bson_sprintf( str,"%d", i ); -} - -MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp ) { - const char *in = ( const char * )inp; - char *out = ( char * )outp; - - out[0] = in[7]; - out[1] = in[6]; - out[2] = in[5]; - out[3] = in[4]; - out[4] = in[3]; - out[5] = in[2]; - out[6] = in[1]; - out[7] = in[0]; - -} - -MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp ) { - const char *in = ( const char * )inp; - char *out = ( char * )outp; - - out[0] = in[3]; - out[1] = in[2]; - out[2] = in[1]; - out[3] = in[0]; -} diff --git a/mongo-c-driver-v0.6/src/bson.h b/mongo-c-driver-v0.6/src/bson.h deleted file mode 100644 index b866948..0000000 --- a/mongo-c-driver-v0.6/src/bson.h +++ /dev/null @@ -1,1038 +0,0 @@ -/** - * @file bson.h - * @brief BSON Declarations - */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_H_ -#define BSON_H_ - -#include -#include -#include -#include -#include - -#ifdef __GNUC__ - #define MONGO_INLINE static __inline__ - #define MONGO_EXPORT -#else - #define MONGO_INLINE static - #ifdef MONGO_STATIC_BUILD - #define MONGO_EXPORT - #elif defined(MONGO_DLL_BUILD) - #define MONGO_EXPORT __declspec(dllexport) - #else - #define MONGO_EXPORT __declspec(dllimport) - #endif -#endif - -#ifdef __cplusplus -#define MONGO_EXTERN_C_START extern "C" { -#define MONGO_EXTERN_C_END } -#else -#define MONGO_EXTERN_C_START -#define MONGO_EXTERN_C_END -#endif - -#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L -#include -#elif defined(MONGO_HAVE_UNISTD) -#include -#elif defined(MONGO_USE__INT64) -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#elif defined(MONGO_USE_LONG_LONG_INT) -typedef long long int int64_t; -typedef unsigned long long int uint64_t; -#else -#error Must compile with c99 or define MONGO_HAVE_STDINT, MONGO_HAVE_UNISTD, MONGO_USE__INT64, or MONGO_USE_LONG_INT. -#endif - -#ifdef MONGO_BIG_ENDIAN -#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) ) -#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) ) -#define bson_big_endian64(out, in) ( memcpy(out, in, 8) ) -#define bson_big_endian32(out, in) ( memcpy(out, in, 4) ) -#else -#define bson_little_endian64(out, in) ( memcpy(out, in, 8) ) -#define bson_little_endian32(out, in) ( memcpy(out, in, 4) ) -#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) ) -#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) ) -#endif - -MONGO_EXTERN_C_START - -#define BSON_OK 0 -#define BSON_ERROR -1 - -enum bson_error_t { - BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */ -}; - -enum bson_validity_t { - BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */ - BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */ - BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */ - BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */ - BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */ -}; - -enum bson_binary_subtype_t { - BSON_BIN_BINARY = 0, - BSON_BIN_FUNC = 1, - BSON_BIN_BINARY_OLD = 2, - BSON_BIN_UUID = 3, - BSON_BIN_MD5 = 5, - BSON_BIN_USER = 128 -}; - -typedef enum { - BSON_EOO = 0, - BSON_DOUBLE = 1, - BSON_STRING = 2, - BSON_OBJECT = 3, - BSON_ARRAY = 4, - BSON_BINDATA = 5, - BSON_UNDEFINED = 6, - BSON_OID = 7, - BSON_BOOL = 8, - BSON_DATE = 9, - BSON_NULL = 10, - BSON_REGEX = 11, - BSON_DBREF = 12, /**< Deprecated. */ - BSON_CODE = 13, - BSON_SYMBOL = 14, - BSON_CODEWSCOPE = 15, - BSON_INT = 16, - BSON_TIMESTAMP = 17, - BSON_LONG = 18 -} bson_type; - -typedef int bson_bool_t; - -typedef struct { - const char *cur; - bson_bool_t first; -} bson_iterator; - -typedef struct { - char *data; /**< Pointer to a block of data in this BSON object. */ - char *cur; /**< Pointer to the current position. */ - int dataSize; /**< The number of bytes allocated to char *data. */ - bson_bool_t finished; /**< When finished, the BSON object can no longer be modified. */ - int stack[32]; /**< A stack used to keep track of nested BSON elements. */ - int stackPos; /**< Index of current stack position. */ - int err; /**< Bitfield representing errors or warnings on this buffer */ - char *errstr; /**< A string representation of the most recent error or warning. */ -} bson; - -#pragma pack(1) -typedef union { - char bytes[12]; - int ints[3]; -} bson_oid_t; -#pragma pack() - -typedef int64_t bson_date_t; /* milliseconds since epoch UTC */ - -typedef struct { - int i; /* increment */ - int t; /* time in seconds */ -} bson_timestamp_t; - -/* ---------------------------- - READING - ------------------------------ */ - -MONGO_EXPORT bson* bson_create(); -MONGO_EXPORT void bson_dispose(bson* b); - -/** - * Size of a BSON object. - * - * @param b the BSON object. - * - * @return the size. - */ -MONGO_EXPORT int bson_size( const bson *b ); -MONGO_EXPORT int bson_buffer_size( const bson *b ); - -/** - * Print a string representation of a BSON object. - * - * @param b the BSON object to print. - */ -MONGO_EXPORT void bson_print( const bson *b ); - -/** - * Return a pointer to the raw buffer stored by this bson object. - * - * @param b a BSON object - */ -MONGO_EXPORT const char *bson_data( const bson *b ); - -/** - * Print a string representation of a BSON object. - * - * @param bson the raw data to print. - * @param depth the depth to recurse the object.x - */ -MONGO_EXPORT void bson_print_raw( const char *bson , int depth ); - -/** - * Advance a bson_iterator to the named field. - * - * @param it the bson_iterator to use. - * @param obj the BSON object to use. - * @param name the name of the field to find. - * - * @return the type of the found object or BSON_EOO if it is not found. - */ -MONGO_EXPORT bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ); - - -MONGO_EXPORT bson_iterator* bson_iterator_create(); -MONGO_EXPORT void bson_iterator_dispose(bson_iterator*); -/** - * Initialize a bson_iterator. - * - * @param i the bson_iterator to initialize. - * @param bson the BSON object to associate with the iterator. - */ -MONGO_EXPORT void bson_iterator_init( bson_iterator *i , const bson *b ); - -/** - * Initialize a bson iterator from a const char* buffer. Note - * that this is mostly used internally. - * - * @param i the bson_iterator to initialize. - * @param buffer the buffer to point to. - */ -MONGO_EXPORT void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ); - -/* more returns true for eoo. best to loop with bson_iterator_next(&it) */ -/** - * Check to see if the bson_iterator has more data. - * - * @param i the iterator. - * - * @return returns true if there is more data. - */ -MONGO_EXPORT bson_bool_t bson_iterator_more( const bson_iterator *i ); - -/** - * Point the iterator at the next BSON object. - * - * @param i the bson_iterator. - * - * @return the type of the next BSON object. - */ -MONGO_EXPORT bson_type bson_iterator_next( bson_iterator *i ); - -/** - * Get the type of the BSON object currently pointed to by the iterator. - * - * @param i the bson_iterator - * - * @return the type of the current BSON object. - */ -MONGO_EXPORT bson_type bson_iterator_type( const bson_iterator *i ); - -/** - * Get the key of the BSON object currently pointed to by the iterator. - * - * @param i the bson_iterator - * - * @return the key of the current BSON object. - */ -MONGO_EXPORT const char *bson_iterator_key( const bson_iterator *i ); - -/** - * Get the value of the BSON object currently pointed to by the iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT const char *bson_iterator_value( const bson_iterator *i ); - -/* these convert to the right type (return 0 if non-numeric) */ -/** - * Get the double value of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT double bson_iterator_double( const bson_iterator *i ); - -/** - * Get the int value of the BSON object currently pointed to by the iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT int bson_iterator_int( const bson_iterator *i ); - -/** - * Get the long value of the BSON object currently pointed to by the iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT int64_t bson_iterator_long( const bson_iterator *i ); - -/* return the bson timestamp as a whole or in parts */ -/** - * Get the timestamp value of the BSON object currently pointed to by - * the iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ); -MONGO_EXPORT int bson_iterator_timestamp_time( const bson_iterator *i ); -MONGO_EXPORT int bson_iterator_timestamp_increment( const bson_iterator *i ); - -/** - * Get the boolean value of the BSON object currently pointed to by - * the iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -/* false: boolean false, 0 in any type, or null */ -/* true: anything else (even empty strings and objects) */ -MONGO_EXPORT bson_bool_t bson_iterator_bool( const bson_iterator *i ); - -/** - * Get the double value of the BSON object currently pointed to by the - * iterator. Assumes the correct type is used. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -/* these assume you are using the right type */ -double bson_iterator_double_raw( const bson_iterator *i ); - -/** - * Get the int value of the BSON object currently pointed to by the - * iterator. Assumes the correct type is used. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -int bson_iterator_int_raw( const bson_iterator *i ); - -/** - * Get the long value of the BSON object currently pointed to by the - * iterator. Assumes the correct type is used. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -int64_t bson_iterator_long_raw( const bson_iterator *i ); - -/** - * Get the bson_bool_t value of the BSON object currently pointed to by the - * iterator. Assumes the correct type is used. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ); - -/** - * Get the bson_oid_t value of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -MONGO_EXPORT bson_oid_t *bson_iterator_oid( const bson_iterator *i ); - -/** - * Get the string value of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON object. - */ -/* these can also be used with bson_code and bson_symbol*/ -MONGO_EXPORT const char *bson_iterator_string( const bson_iterator *i ); - -/** - * Get the string length of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the length of the current BSON object. - */ -int bson_iterator_string_len( const bson_iterator *i ); - -/** - * Get the code value of the BSON object currently pointed to by the - * iterator. Works with bson_code, bson_codewscope, and BSON_STRING - * returns NULL for everything else. - * - * @param i the bson_iterator - * - * @return the code value of the current BSON object. - */ -/* works with bson_code, bson_codewscope, and BSON_STRING */ -/* returns NULL for everything else */ -MONGO_EXPORT const char *bson_iterator_code( const bson_iterator *i ); - -/** - * Calls bson_empty on scope if not a bson_codewscope - * - * @param i the bson_iterator. - * @param scope the bson scope. - */ -/* calls bson_empty on scope if not a bson_codewscope */ -MONGO_EXPORT void bson_iterator_code_scope( const bson_iterator *i, bson *scope ); - -/** - * Get the date value of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the date value of the current BSON object. - */ -/* both of these only work with bson_date */ -MONGO_EXPORT bson_date_t bson_iterator_date( const bson_iterator *i ); - -/** - * Get the time value of the BSON object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the time value of the current BSON object. - */ -MONGO_EXPORT time_t bson_iterator_time_t( const bson_iterator *i ); - -/** - * Get the length of the BSON binary object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the length of the current BSON binary object. - */ -MONGO_EXPORT int bson_iterator_bin_len( const bson_iterator *i ); - -/** - * Get the type of the BSON binary object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the type of the current BSON binary object. - */ -MONGO_EXPORT char bson_iterator_bin_type( const bson_iterator *i ); - -/** - * Get the value of the BSON binary object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON binary object. - */ -MONGO_EXPORT const char *bson_iterator_bin_data( const bson_iterator *i ); - -/** - * Get the value of the BSON regex object currently pointed to by the - * iterator. - * - * @param i the bson_iterator - * - * @return the value of the current BSON regex object. - */ -MONGO_EXPORT const char *bson_iterator_regex( const bson_iterator *i ); - -/** - * Get the options of the BSON regex object currently pointed to by the - * iterator. - * - * @param i the bson_iterator. - * - * @return the options of the current BSON regex object. - */ -MONGO_EXPORT const char *bson_iterator_regex_opts( const bson_iterator *i ); - -/* these work with BSON_OBJECT and BSON_ARRAY */ -/** - * Get the BSON subobject currently pointed to by the - * iterator. - * - * @param i the bson_iterator. - * @param sub the BSON subobject destination. - */ -MONGO_EXPORT void bson_iterator_subobject( const bson_iterator *i, bson *sub ); - -/** - * Get a bson_iterator that on the BSON subobject. - * - * @param i the bson_iterator. - * @param sub the iterator to point at the BSON subobject. - */ -MONGO_EXPORT void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ); - -/* str must be at least 24 hex chars + null byte */ -/** - * Create a bson_oid_t from a string. - * - * @param oid the bson_oid_t destination. - * @param str a null terminated string comprised of at least 24 hex chars. - */ -MONGO_EXPORT void bson_oid_from_string( bson_oid_t *oid, const char *str ); - -/** - * Create a string representation of the bson_oid_t. - * - * @param oid the bson_oid_t source. - * @param str the string representation destination. - */ -MONGO_EXPORT void bson_oid_to_string( const bson_oid_t *oid, char *str ); - -/** - * Create a bson_oid object. - * - * @param oid the destination for the newly created bson_oid_t. - */ -MONGO_EXPORT void bson_oid_gen( bson_oid_t *oid ); - -/** - * Set a function to be used to generate the second four bytes - * of an object id. - * - * @param func a pointer to a function that returns an int. - */ -MONGO_EXPORT void bson_set_oid_fuzz( int ( *func )( void ) ); - -/** - * Set a function to be used to generate the incrementing part - * of an object id (last four bytes). If you need thread-safety - * in generating object ids, you should set this function. - * - * @param func a pointer to a function that returns an int. - */ -MONGO_EXPORT void bson_set_oid_inc( int ( *func )( void ) ); - -/** - * Get the time a bson_oid_t was created. - * - * @param oid the bson_oid_t. - */ -MONGO_EXPORT time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */ - -/* ---------------------------- - BUILDING - ------------------------------ */ - -/** - * Initialize a new bson object. If not created - * with bson_new, you must initialize each new bson - * object using this function. - * - * @note When finished, you must pass the bson object to - * bson_destroy( ). - */ -MONGO_EXPORT void bson_init( bson *b ); - -/** - * Initialize a BSON object, and point its data - * pointer to the provided char*. - * - * @param b the BSON object to initialize. - * @param data the raw BSON data. - * - * @return BSON_OK or BSON_ERROR. - */ -int bson_init_data( bson *b , char *data ); -int bson_init_finished_data( bson *b, char *data ) ; - -/** - * Initialize a BSON object, and set its - * buffer to the given size. - * - * @param b the BSON object to initialize. - * @param size the initial size of the buffer. - * - * @return BSON_OK or BSON_ERROR. - */ -void bson_init_size( bson *b, int size ); - -/** - * Grow a bson object. - * - * @param b the bson to grow. - * @param bytesNeeded the additional number of bytes needed. - * - * @return BSON_OK or BSON_ERROR with the bson error object set. - * Exits if allocation fails. - */ -int bson_ensure_space( bson *b, const int bytesNeeded ); - -/** - * Finalize a bson object. - * - * @param b the bson object to finalize. - * - * @return the standard error code. To deallocate memory, - * call bson_destroy on the bson object. - */ -MONGO_EXPORT int bson_finish( bson *b ); - -/** - * Destroy a bson object. - * - * @param b the bson object to destroy. - * - */ -MONGO_EXPORT void bson_destroy( bson *b ); - -/** - * Returns a pointer to a static empty BSON object. - * - * @param obj the BSON object to initialize. - * - * @return the empty initialized BSON object. - */ -/* returns pointer to static empty bson object */ -MONGO_EXPORT bson *bson_empty( bson *obj ); - -/** - * Make a complete copy of the a BSON object. - * The source bson object must be in a finished - * state; otherwise, the copy will fail. - * - * @param out the copy destination BSON object. - * @param in the copy source BSON object. - */ -MONGO_EXPORT int bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */ - -/** - * Append a previously created bson_oid_t to a bson object. - * - * @param b the bson to append to. - * @param name the key for the bson_oid_t. - * @param oid the bson_oid_t to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ); - -/** - * Append a bson_oid_t to a bson. - * - * @param b the bson to append to. - * @param name the key for the bson_oid_t. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_new_oid( bson *b, const char *name ); - -/** - * Append an int to a bson. - * - * @param b the bson to append to. - * @param name the key for the int. - * @param i the int to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_int( bson *b, const char *name, const int i ); - -/** - * Append an long to a bson. - * - * @param b the bson to append to. - * @param name the key for the long. - * @param i the long to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_long( bson *b, const char *name, const int64_t i ); - -/** - * Append an double to a bson. - * - * @param b the bson to append to. - * @param name the key for the double. - * @param d the double to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_double( bson *b, const char *name, const double d ); - -/** - * Append a string to a bson. - * - * @param b the bson to append to. - * @param name the key for the string. - * @param str the string to append. - * - * @return BSON_OK or BSON_ERROR. -*/ -MONGO_EXPORT int bson_append_string( bson *b, const char *name, const char *str ); - -/** - * Append len bytes of a string to a bson. - * - * @param b the bson to append to. - * @param name the key for the string. - * @param str the string to append. - * @param len the number of bytes from str to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_string_n( bson *b, const char *name, const char *str, int len ); - -/** - * Append a symbol to a bson. - * - * @param b the bson to append to. - * @param name the key for the symbol. - * @param str the symbol to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_symbol( bson *b, const char *name, const char *str ); - -/** - * Append len bytes of a symbol to a bson. - * - * @param b the bson to append to. - * @param name the key for the symbol. - * @param str the symbol to append. - * @param len the number of bytes from str to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_symbol_n( bson *b, const char *name, const char *str, int len ); - -/** - * Append code to a bson. - * - * @param b the bson to append to. - * @param name the key for the code. - * @param str the code to append. - * @param len the number of bytes from str to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_code( bson *b, const char *name, const char *str ); - -/** - * Append len bytes of code to a bson. - * - * @param b the bson to append to. - * @param name the key for the code. - * @param str the code to append. - * @param len the number of bytes from str to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_code_n( bson *b, const char *name, const char *str, int len ); - -/** - * Append code to a bson with scope. - * - * @param b the bson to append to. - * @param name the key for the code. - * @param str the string to append. - * @param scope a BSON object containing the scope. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ); - -/** - * Append len bytes of code to a bson with scope. - * - * @param b the bson to append to. - * @param name the key for the code. - * @param str the string to append. - * @param len the number of bytes from str to append. - * @param scope a BSON object containing the scope. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope ); - -/** - * Append binary data to a bson. - * - * @param b the bson to append to. - * @param name the key for the data. - * @param type the binary data type. - * @param str the binary data. - * @param len the length of the data. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ); - -/** - * Append a bson_bool_t to a bson. - * - * @param b the bson to append to. - * @param name the key for the boolean value. - * @param v the bson_bool_t to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_bool( bson *b, const char *name, const bson_bool_t v ); - -/** - * Append a null value to a bson. - * - * @param b the bson to append to. - * @param name the key for the null value. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_null( bson *b, const char *name ); - -/** - * Append an undefined value to a bson. - * - * @param b the bson to append to. - * @param name the key for the undefined value. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_undefined( bson *b, const char *name ); - -/** - * Append a regex value to a bson. - * - * @param b the bson to append to. - * @param name the key for the regex value. - * @param pattern the regex pattern to append. - * @param the regex options. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ); - -/** - * Append bson data to a bson. - * - * @param b the bson to append to. - * @param name the key for the bson data. - * @param bson the bson object to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_bson( bson *b, const char *name, const bson *bson ); - -/** - * Append a BSON element to a bson from the current point of an iterator. - * - * @param b the bson to append to. - * @param name_or_null the key for the BSON element, or NULL. - * @param elem the bson_iterator. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ); - -/** - * Append a bson_timestamp_t value to a bson. - * - * @param b the bson to append to. - * @param name the key for the timestampe value. - * @param ts the bson_timestamp_t value to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ); -MONGO_EXPORT int bson_append_timestamp2( bson *b, const char *name, int time, int increment ); - -/* these both append a bson_date */ -/** - * Append a bson_date_t value to a bson. - * - * @param b the bson to append to. - * @param name the key for the date value. - * @param millis the bson_date_t to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_date( bson *b, const char *name, bson_date_t millis ); - -/** - * Append a time_t value to a bson. - * - * @param b the bson to append to. - * @param name the key for the date value. - * @param secs the time_t to append. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_time_t( bson *b, const char *name, time_t secs ); - -/** - * Start appending a new object to a bson. - * - * @param b the bson to append to. - * @param name the name of the new object. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_start_object( bson *b, const char *name ); - -/** - * Start appending a new array to a bson. - * - * @param b the bson to append to. - * @param name the name of the new array. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_start_array( bson *b, const char *name ); - -/** - * Finish appending a new object or array to a bson. - * - * @param b the bson to append to. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_finish_object( bson *b ); - -/** - * Finish appending a new object or array to a bson. This - * is simply an alias for bson_append_finish_object. - * - * @param b the bson to append to. - * - * @return BSON_OK or BSON_ERROR. - */ -MONGO_EXPORT int bson_append_finish_array( bson *b ); - -void bson_numstr( char *str, int i ); - -void bson_incnumstr( char *str ); - -/* Error handling and standard library function over-riding. */ -/* -------------------------------------------------------- */ - -/* bson_err_handlers shouldn't return!!! */ -typedef void( *bson_err_handler )( const char *errmsg ); - -typedef int (*bson_printf_func)( const char *, ... ); -typedef int (*bson_fprintf_func)( FILE *, const char *, ... ); -typedef int (*bson_sprintf_func)( char *, const char *, ... ); - -extern void *( *bson_malloc_func )( size_t ); -extern void *( *bson_realloc_func )( void *, size_t ); -extern void ( *bson_free_func )( void * ); - -extern bson_printf_func bson_printf; -extern bson_fprintf_func bson_fprintf; -extern bson_sprintf_func bson_sprintf; -extern bson_printf_func bson_errprintf; - -MONGO_EXPORT void bson_free( void *ptr ); - -/** - * Allocates memory and checks return value, exiting fatally if malloc() fails. - * - * @param size bytes to allocate. - * - * @return a pointer to the allocated memory. - * - * @sa malloc(3) - */ -MONGO_EXPORT void *bson_malloc( int size ); - -/** - * Changes the size of allocated memory and checks return value, - * exiting fatally if realloc() fails. - * - * @param ptr pointer to the space to reallocate. - * @param size bytes to allocate. - * - * @return a pointer to the allocated memory. - * - * @sa realloc() - */ -void *bson_realloc( void *ptr, int size ); - -/** - * Set a function for error handling. - * - * @param func a bson_err_handler function. - * - * @return the old error handling function, or NULL. - */ -MONGO_EXPORT bson_err_handler set_bson_err_handler( bson_err_handler func ); - -/* does nothing if ok != 0 */ -/** - * Exit fatally. - * - * @param ok exits if ok is equal to 0. - */ -void bson_fatal( int ok ); - -/** - * Exit fatally with an error message. - * - * @param ok exits if ok is equal to 0. - * @param msg prints to stderr before exiting. - */ -void bson_fatal_msg( int ok, const char *msg ); - -/** - * Invoke the error handler, but do not exit. - * - * @param b the buffer object. - */ -void bson_builder_error( bson *b ); - -/** - * Cast an int64_t to double. This is necessary for embedding in - * certain environments. - * - */ -MONGO_EXPORT double bson_int64_to_double( int64_t i64 ); - -MONGO_EXPORT void bson_swap_endian32( void *outp, const void *inp ); -MONGO_EXPORT void bson_swap_endian64( void *outp, const void *inp ); - -MONGO_EXTERN_C_END -#endif diff --git a/mongo-c-driver-v0.6/src/encoding.c b/mongo-c-driver-v0.6/src/encoding.c deleted file mode 100644 index 45d0d27..0000000 --- a/mongo-c-driver-v0.6/src/encoding.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2009-2012 10gen, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Portions Copyright 2001 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - - -#include "bson.h" -#include "encoding.h" - -/* - * Index into the table below with the first byte of a UTF-8 sequence to - * get the number of trailing bytes that are supposed to follow it. - */ -static const char trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -/* --------------------------------------------------------------------- */ - -/* - * Utility routine to tell whether a sequence of bytes is legal UTF-8. - * This must be called with the length pre-determined by the first byte. - * The length can be set by: - * length = trailingBytesForUTF8[*source]+1; - * and the sequence is illegal right away if there aren't that many bytes - * available. - * If presented with a length > 4, this returns 0. The Unicode - * definition of UTF-8 goes up to 4-byte sequences. - */ -static int isLegalUTF8( const unsigned char *source, int length ) { - unsigned char a; - const unsigned char *srcptr = source + length; - switch ( length ) { - default: - return 0; - /* Everything else falls through when "true"... */ - case 4: - if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; - case 3: - if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; - case 2: - if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0; - switch ( *source ) { - /* no fall-through in this inner switch */ - case 0xE0: - if ( a < 0xA0 ) return 0; - break; - case 0xF0: - if ( a < 0x90 ) return 0; - break; - case 0xF4: - if ( a > 0x8F ) return 0; - break; - default: - if ( a < 0x80 ) return 0; - } - case 1: - if ( *source >= 0x80 && *source < 0xC2 ) return 0; - if ( *source > 0xF4 ) return 0; - } - return 1; -} - -/* If the name is part of a db ref ($ref, $db, or $id), then return true. */ -static int bson_string_is_db_ref( const unsigned char *string, const int length ) { - int result = 0; - - if( length >= 4 ) { - if( string[1] == 'r' && string[2] == 'e' && string[3] == 'f' ) - result = 1; - } - else if( length >= 3 ) { - if( string[1] == 'i' && string[2] == 'd' ) - result = 1; - else if( string[1] == 'd' && string[2] == 'b' ) - result = 1; - } - - return result; -} - -static int bson_validate_string( bson *b, const unsigned char *string, - const int length, const char check_utf8, const char check_dot, - const char check_dollar ) { - - int position = 0; - int sequence_length = 1; - - if( check_dollar && string[0] == '$' ) { - if( !bson_string_is_db_ref( string, length ) ) - b->err |= BSON_FIELD_INIT_DOLLAR; - } - - while ( position < length ) { - if ( check_dot && *( string + position ) == '.' ) { - b->err |= BSON_FIELD_HAS_DOT; - } - - if ( check_utf8 ) { - sequence_length = trailingBytesForUTF8[*( string + position )] + 1; - if ( ( position + sequence_length ) > length ) { - b->err |= BSON_NOT_UTF8; - return BSON_ERROR; - } - if ( !isLegalUTF8( string + position, sequence_length ) ) { - b->err |= BSON_NOT_UTF8; - return BSON_ERROR; - } - } - position += sequence_length; - } - - return BSON_OK; -} - - -int bson_check_string( bson *b, const char *string, - const int length ) { - - return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 ); -} - -int bson_check_field_name( bson *b, const char *string, - const int length ) { - - return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 ); -} diff --git a/mongo-c-driver-v0.6/src/encoding.h b/mongo-c-driver-v0.6/src/encoding.h deleted file mode 100644 index f13c31e..0000000 --- a/mongo-c-driver-v0.6/src/encoding.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2009-2012 10gen, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_ENCODING_H_ -#define BSON_ENCODING_H_ - -MONGO_EXTERN_C_START - -/** - * Check that a field name is valid UTF8, does not start with a '$', - * and contains no '.' characters. Set bson bit field appropriately. - * Note that we don't need to check for '\0' because we're using - * strlen(3), which stops at '\0'. - * - * @param b The bson object to which field name will be appended. - * @param string The field name as char*. - * @param length The length of the field name. - * - * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be - * valid UTF8. This function will also check whether the string - * contains '.' or starts with '$', since the validity of this depends on context. - * Set the value of b->err appropriately. - */ -int bson_check_field_name( bson *b, const char *string, - const int length ); - -/** - * Check that a string is valid UTF8. Sets the buffer bit field appropriately. - * - * @param b The bson object to which string will be appended. - * @param string The string to check. - * @param length The length of the string. - * - * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR. - * Sets b->err on error. - */ -bson_bool_t bson_check_string( bson *b, const char *string, - const int length ); - -MONGO_EXTERN_C_END -#endif diff --git a/mongo-c-driver-v0.6/src/env.h b/mongo-c-driver-v0.6/src/env.h deleted file mode 100644 index 463ee32..0000000 --- a/mongo-c-driver-v0.6/src/env.h +++ /dev/null @@ -1,39 +0,0 @@ -/** @file env.h */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Header for generic net.h */ -#ifndef MONGO_ENV_H_ -#define MONGO_ENV_H_ - -#include "mongo.h" - -MONGO_EXTERN_C_START - -/* This is a no-op in the generic implementation. */ -int mongo_env_set_socket_op_timeout( mongo *conn, int millis ); -int mongo_env_read_socket( mongo *conn, void *buf, int len ); -int mongo_env_write_socket( mongo *conn, const void *buf, int len ); -int mongo_env_socket_connect( mongo *conn, const char *host, int port ); - -/* Initialize socket services */ -MONGO_EXPORT int mongo_env_sock_init( void ); - -/* Close a socket */ -MONGO_EXPORT int mongo_env_close_socket( int socket ); - -MONGO_EXTERN_C_END -#endif diff --git a/mongo-c-driver-v0.6/src/env_posix.c b/mongo-c-driver-v0.6/src/env_posix.c deleted file mode 100644 index f1020ca..0000000 --- a/mongo-c-driver-v0.6/src/env_posix.c +++ /dev/null @@ -1,165 +0,0 @@ -/* env_posix.c */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Networking and other niceties for POSIX systems. */ -#include "env.h" -#include "mongo.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef NI_MAXSERV -# define NI_MAXSERV 32 -#endif - -int mongo_env_close_socket( int socket ) { - return close( socket ); -} - -int mongo_env_sock_init( void ) { - return 0; -} - -int mongo_env_write_socket( mongo *conn, const void *buf, int len ) { - const char *cbuf = buf; -#ifdef __APPLE__ - int flags = 0; -#else - int flags = MSG_NOSIGNAL; -#endif - - while ( len ) { - int sent = send( conn->sock, cbuf, len, flags ); - if ( sent == -1 ) { - if (errno == EPIPE) - conn->connected = 0; - __mongo_set_error( conn, MONGO_IO_ERROR, strerror( errno ), errno ); - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -int mongo_env_read_socket( mongo *conn, void *buf, int len ) { - char *cbuf = buf; - while ( len ) { - int sent = recv( conn->sock, cbuf, len, 0 ); - if ( sent == 0 || sent == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, strerror( errno ), errno ); - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -int mongo_env_set_socket_op_timeout( mongo *conn, int millis ) { - struct timeval tv; - tv.tv_sec = millis / 1000; - tv.tv_usec = ( millis % 1000 ) * 1000; - - if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) ) == -1 ) { - conn->err = MONGO_IO_ERROR; - __mongo_set_error( conn, MONGO_IO_ERROR, "setsockopt SO_RCVTIMEO failed.", errno ); - return MONGO_ERROR; - } - - if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) ) == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, "setsockopt SO_SNDTIMEO failed.", errno ); - return MONGO_ERROR; - } - - return MONGO_OK; -} - -int mongo_env_socket_connect( mongo *conn, const char *host, int port ) { - char port_str[NI_MAXSERV]; - int status; - - struct addrinfo ai_hints; - struct addrinfo *ai_list = NULL; - struct addrinfo *ai_ptr = NULL; - - conn->sock = 0; - conn->connected = 0; - sprintf(port_str,"%d",port); - - bson_sprintf( port_str, "%d", port ); - - memset( &ai_hints, 0, sizeof( ai_hints ) ); -#ifdef AI_ADDRCONFIG - ai_hints.ai_flags = AI_ADDRCONFIG; -#endif - ai_hints.ai_family = AF_UNSPEC; - ai_hints.ai_socktype = SOCK_STREAM; - - status = getaddrinfo( host, port_str, &ai_hints, &ai_list ); - if ( status != 0 ) { - bson_errprintf( "getaddrinfo failed: %s", gai_strerror( status ) ); - conn->err = MONGO_CONN_ADDR_FAIL; - return MONGO_ERROR; - } - - for ( ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next ) { - conn->sock = socket( ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol ); - if ( conn->sock < 0 ) { - conn->sock = 0; - continue; - } - - status = connect( conn->sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen ); - if ( status != 0 ) { - mongo_env_close_socket( conn->sock ); - conn->sock = 0; - continue; - } - - if ( ai_ptr->ai_protocol == IPPROTO_TCP ) { - int flag = 1; - - setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, - ( void * ) &flag, sizeof( flag ) ); - if ( conn->op_timeout_ms > 0 ) - mongo_env_set_socket_op_timeout( conn, conn->op_timeout_ms ); - } - - conn->connected = 1; - break; - } - - freeaddrinfo( ai_list ); - - if ( ! conn->connected ) { - conn->err = MONGO_CONN_FAIL; - return MONGO_ERROR; - } - - return MONGO_OK; -} diff --git a/mongo-c-driver-v0.6/src/env_standard.c b/mongo-c-driver-v0.6/src/env_standard.c deleted file mode 100644 index 36fa9f6..0000000 --- a/mongo-c-driver-v0.6/src/env_standard.c +++ /dev/null @@ -1,168 +0,0 @@ -/* env_standard.c */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Vanilla networking designed to work on all systems. */ -#include "env.h" -#include -#include - -#ifdef _WIN32 - #ifdef _MSC_VER - #include // send,recv,socklen_t etc - #include // addrinfo - #else - #include - #include - typedef int socklen_t; - #endif -#else -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifndef NI_MAXSERV -# define NI_MAXSERV 32 -#endif - -int mongo_env_close_socket( int socket ) { -#ifdef _WIN32 - return closesocket( socket ); -#else - return close( socket ); -#endif -} - -int mongo_env_write_socket( mongo *conn, const void *buf, int len ) { - const char *cbuf = buf; -#ifdef _WIN32 - int flags = 0; -#else -#ifdef __APPLE__ - int flags = 0; -#else - int flags = MSG_NOSIGNAL; -#endif -#endif - - while ( len ) { - int sent = send( conn->sock, cbuf, len, flags ); - if ( sent == -1 ) { - if (errno == EPIPE) - conn->connected = 0; - conn->err = MONGO_IO_ERROR; - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -int mongo_env_read_socket( mongo *conn, void *buf, int len ) { - char *cbuf = buf; - while ( len ) { - int sent = recv( conn->sock, cbuf, len, 0 ); - if ( sent == 0 || sent == -1 ) { - conn->err = MONGO_IO_ERROR; - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -/* This is a no-op in the generic implementation. */ -int mongo_env_set_socket_op_timeout( mongo *conn, int millis ) { - return MONGO_OK; -} - -int mongo_env_socket_connect( mongo *conn, const char *host, int port ) { - struct sockaddr_in sa; - socklen_t addressSize; - int flag = 1; - - if ( ( conn->sock = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) { - conn->sock = 0; - conn->err = MONGO_CONN_NO_SOCKET; - return MONGO_ERROR; - } - - memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) ); - sa.sin_family = AF_INET; - sa.sin_port = htons( port ); - sa.sin_addr.s_addr = inet_addr( host ); - addressSize = sizeof( sa ); - - if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) { - mongo_env_close_socket( conn->sock ); - conn->connected = 0; - conn->sock = 0; - conn->err = MONGO_CONN_FAIL; - return MONGO_ERROR; - } - - setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) ); - - if( conn->op_timeout_ms > 0 ) - mongo_env_set_socket_op_timeout( conn, conn->op_timeout_ms ); - - conn->connected = 1; - - return MONGO_OK; -} - -MONGO_EXPORT int mongo_env_sock_init( void ) { - -#if defined(_WIN32) - WSADATA wsaData; - WORD wVers; -#elif defined(SIGPIPE) - struct sigaction act; -#endif - - static int called_once; - static int retval; - if (called_once) return retval; - called_once = 1; - -#if defined(_WIN32) - wVers = MAKEWORD(1, 1); - retval = (WSAStartup(wVers, &wsaData) == 0); -#elif defined(MACINTOSH) - GUSISetup(GUSIwithInternetSockets); - retval = 1; -#elif defined(SIGPIPE) - retval = 1; - if (sigaction(SIGPIPE, (struct sigaction *)NULL, &act) < 0) - retval = 0; - else if (act.sa_handler == SIG_DFL) { - act.sa_handler = SIG_IGN; - if (sigaction(SIGPIPE, &act, (struct sigaction *)NULL) < 0) - retval = 0; - } -#endif - return retval; -} diff --git a/mongo-c-driver-v0.6/src/env_win32.c b/mongo-c-driver-v0.6/src/env_win32.c deleted file mode 100644 index 4b38928..0000000 --- a/mongo-c-driver-v0.6/src/env_win32.c +++ /dev/null @@ -1,178 +0,0 @@ -/* env_win32.c */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Networking and other niceties for WIN32. */ -#include "env.h" -#include "mongo.h" -#include - -#ifdef _MSC_VER -#include // send,recv,socklen_t etc -#include // addrinfo -#else -#include // send,recv,socklen_t etc -#include -typedef int socklen_t; -#endif - -#ifndef NI_MAXSERV -# define NI_MAXSERV 32 -#endif - -int mongo_env_close_socket( int socket ) { - return closesocket( socket ); -} - -int mongo_env_write_socket( mongo *conn, const void *buf, int len ) { - const char *cbuf = buf; - int flags = 0; - - while ( len ) { - int sent = send( conn->sock, cbuf, len, flags ); - if ( sent == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, NULL, WSAGetLastError() ); - conn->connected = 0; - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -int mongo_env_read_socket( mongo *conn, void *buf, int len ) { - char *cbuf = buf; - - while ( len ) { - int sent = recv( conn->sock, cbuf, len, 0 ); - if ( sent == 0 || sent == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, NULL, WSAGetLastError() ); - return MONGO_ERROR; - } - cbuf += sent; - len -= sent; - } - - return MONGO_OK; -} - -int mongo_env_set_socket_op_timeout( mongo *conn, int millis ) { - if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&millis, - sizeof( millis ) ) == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, "setsockopt SO_RCVTIMEO failed.", - WSAGetLastError() ); - return MONGO_ERROR; - } - - if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&millis, - sizeof( millis ) ) == -1 ) { - __mongo_set_error( conn, MONGO_IO_ERROR, "setsockopt SO_SNDTIMEO failed.", - WSAGetLastError() ); - return MONGO_ERROR; - } - - return MONGO_OK; -} - -int mongo_env_socket_connect( mongo *conn, const char *host, int port ) { - char port_str[NI_MAXSERV]; - char errstr[MONGO_ERR_LEN]; - int status; - - struct addrinfo ai_hints; - struct addrinfo *ai_list = NULL; - struct addrinfo *ai_ptr = NULL; - - conn->sock = 0; - conn->connected = 0; - - bson_sprintf( port_str, "%d", port ); - - memset( &ai_hints, 0, sizeof( ai_hints ) ); - ai_hints.ai_family = AF_UNSPEC; - ai_hints.ai_socktype = SOCK_STREAM; - ai_hints.ai_protocol = IPPROTO_TCP; - - status = getaddrinfo( host, port_str, &ai_hints, &ai_list ); - if ( status != 0 ) { - bson_sprintf( errstr, "getaddrinfo failed with error %d", status ); - __mongo_set_error( conn, MONGO_CONN_ADDR_FAIL, errstr, WSAGetLastError() ); - return MONGO_ERROR; - } - - for ( ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next ) { - conn->sock = socket( ai_ptr->ai_family, ai_ptr->ai_socktype, - ai_ptr->ai_protocol ); - - if ( conn->sock < 0 ) { - __mongo_set_error( conn, MONGO_SOCKET_ERROR, "socket() failed", - WSAGetLastError() ); - conn->sock = 0; - continue; - } - - status = connect( conn->sock, ai_ptr->ai_addr, ai_ptr->ai_addrlen ); - if ( status != 0 ) { - __mongo_set_error( conn, MONGO_SOCKET_ERROR, "connect() failed", - WSAGetLastError() ); - mongo_env_close_socket( conn->sock ); - conn->sock = 0; - continue; - } - - if ( ai_ptr->ai_protocol == IPPROTO_TCP ) { - int flag = 1; - - setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, - ( void * ) &flag, sizeof( flag ) ); - - if ( conn->op_timeout_ms > 0 ) - mongo_env_set_socket_op_timeout( conn, conn->op_timeout_ms ); - } - - conn->connected = 1; - break; - } - - freeaddrinfo( ai_list ); - - if ( ! conn->connected ) { - conn->err = MONGO_CONN_FAIL; - return MONGO_ERROR; - } - else { - mongo_clear_errors( conn ); - return MONGO_OK; - } -} - -MONGO_EXPORT int mongo_env_sock_init( void ) { - - WSADATA wsaData; - WORD wVers; - static int called_once; - static int retval; - - if (called_once) return retval; - - called_once = 1; - wVers = MAKEWORD(1, 1); - retval = (WSAStartup(wVers, &wsaData) == 0); - - return retval; -} diff --git a/mongo-c-driver-v0.6/src/gridfs.c b/mongo-c-driver-v0.6/src/gridfs.c deleted file mode 100644 index db80dea..0000000 --- a/mongo-c-driver-v0.6/src/gridfs.c +++ /dev/null @@ -1,712 +0,0 @@ -/* gridfs.c */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "gridfs.h" -#include -#include -#include -#include - -MONGO_EXPORT gridfs* gridfs_create() { - return (gridfs*)bson_malloc(sizeof(gridfs)); -} - -MONGO_EXPORT void gridfs_dispose(gridfs* gfs) { - free(gfs); -} - -MONGO_EXPORT gridfile* gridfile_create() { - return (gridfile*)bson_malloc(sizeof(gridfile)); -} - -MONGO_EXPORT void gridfile_dispose(gridfile* gf) { - free(gf); -} - -MONGO_EXPORT void gridfile_get_descriptor(gridfile* gf, bson* out) { - *out = *gf->meta; -} - - -static bson *chunk_new( bson_oid_t id, int chunkNumber, - const char *data, int len ) { - bson *b = bson_malloc( sizeof( bson ) ); - - bson_init( b ); - bson_append_oid( b, "files_id", &id ); - bson_append_int( b, "n", chunkNumber ); - bson_append_binary( b, "data", BSON_BIN_BINARY, data, len ); - bson_finish( b ); - return b; -} - -static void chunk_free( bson *oChunk ) { - bson_destroy( oChunk ); - bson_free( oChunk ); -} - -int gridfs_init( mongo *client, const char *dbname, const char *prefix, - gridfs *gfs ) { - - int options; - bson b; - bson_bool_t success; - - gfs->client = client; - - /* Allocate space to own the dbname */ - gfs->dbname = ( const char * )bson_malloc( strlen( dbname )+1 ); - strcpy( ( char * )gfs->dbname, dbname ); - - /* Allocate space to own the prefix */ - if ( prefix == NULL ) prefix = "fs"; - gfs->prefix = ( const char * )bson_malloc( strlen( prefix )+1 ); - strcpy( ( char * )gfs->prefix, prefix ); - - /* Allocate space to own files_ns */ - gfs->files_ns = - ( const char * ) bson_malloc ( strlen( prefix )+strlen( dbname )+strlen( ".files" )+2 ); - strcpy( ( char * )gfs->files_ns, dbname ); - strcat( ( char * )gfs->files_ns, "." ); - strcat( ( char * )gfs->files_ns, prefix ); - strcat( ( char * )gfs->files_ns, ".files" ); - - /* Allocate space to own chunks_ns */ - gfs->chunks_ns = ( const char * ) bson_malloc( strlen( prefix ) + strlen( dbname ) - + strlen( ".chunks" ) + 2 ); - strcpy( ( char * )gfs->chunks_ns, dbname ); - strcat( ( char * )gfs->chunks_ns, "." ); - strcat( ( char * )gfs->chunks_ns, prefix ); - strcat( ( char * )gfs->chunks_ns, ".chunks" ); - - bson_init( &b ); - bson_append_int( &b, "filename", 1 ); - bson_finish( &b ); - options = 0; - success = ( mongo_create_index( gfs->client, gfs->files_ns, &b, options, NULL ) == MONGO_OK ); - bson_destroy( &b ); - if ( !success ) { - bson_free( ( char * )gfs->dbname ); - bson_free( ( char * )gfs->prefix ); - bson_free( ( char * )gfs->files_ns ); - bson_free( ( char * )gfs->chunks_ns ); - return MONGO_ERROR; - } - - bson_init( &b ); - bson_append_int( &b, "files_id", 1 ); - bson_append_int( &b, "n", 1 ); - bson_finish( &b ); - options = MONGO_INDEX_UNIQUE; - success = ( mongo_create_index( gfs->client, gfs->chunks_ns, &b, options, NULL ) == MONGO_OK ); - bson_destroy( &b ); - if ( !success ) { - bson_free( ( char * )gfs->dbname ); - bson_free( ( char * )gfs->prefix ); - bson_free( ( char * )gfs->files_ns ); - bson_free( ( char * )gfs->chunks_ns ); - return MONGO_ERROR; - } - - return MONGO_OK; -} - -MONGO_EXPORT void gridfs_destroy( gridfs *gfs ) { - if ( gfs == NULL ) return; - if ( gfs->dbname ) bson_free( ( char * )gfs->dbname ); - if ( gfs->prefix ) bson_free( ( char * )gfs->prefix ); - if ( gfs->files_ns ) bson_free( ( char * )gfs->files_ns ); - if ( gfs->chunks_ns ) bson_free( ( char * )gfs->chunks_ns ); -} - -static int gridfs_insert_file( gridfs *gfs, const char *name, - const bson_oid_t id, gridfs_offset length, - const char *contenttype ) { - bson command; - bson ret; - bson res; - bson_iterator it; - int result; - int64_t d; - - /* Check run md5 */ - bson_init( &command ); - bson_append_oid( &command, "filemd5", &id ); - bson_append_string( &command, "root", gfs->prefix ); - bson_finish( &command ); - result = mongo_run_command( gfs->client, gfs->dbname, &command, &res ); - bson_destroy( &command ); - if (result != MONGO_OK) - return result; - - /* Create and insert BSON for file metadata */ - bson_init( &ret ); - bson_append_oid( &ret, "_id", &id ); - if ( name != NULL && *name != '\0' ) { - bson_append_string( &ret, "filename", name ); - } - bson_append_long( &ret, "length", length ); - bson_append_int( &ret, "chunkSize", DEFAULT_CHUNK_SIZE ); - d = ( bson_date_t )1000*time( NULL ); - bson_append_date( &ret, "uploadDate", d); - bson_find( &it, &res, "md5" ); - bson_append_string( &ret, "md5", bson_iterator_string( &it ) ); - bson_destroy( &res ); - if ( contenttype != NULL && *contenttype != '\0' ) { - bson_append_string( &ret, "contentType", contenttype ); - } - bson_finish( &ret ); - result = mongo_insert( gfs->client, gfs->files_ns, &ret, NULL ); - bson_destroy( &ret ); - - return result; -} - -MONGO_EXPORT int gridfs_store_buffer( gridfs *gfs, const char *data, - gridfs_offset length, const char *remotename, - const char *contenttype ) { - - char const *end = data + length; - const char *data_ptr = data; - bson_oid_t id; - int chunkNumber = 0; - int chunkLen; - bson *oChunk; - - /* Large files Assertion */ - /* assert( length <= 0xffffffff ); */ - - /* Generate and append an oid*/ - bson_oid_gen( &id ); - - /* Insert the file's data chunk by chunk */ - while ( data_ptr < end ) { - chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ? - DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr ); - oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen ); - mongo_insert( gfs->client, gfs->chunks_ns, oChunk, NULL ); - chunk_free( oChunk ); - chunkNumber++; - data_ptr += chunkLen; - } - - /* Inserts file's metadata */ - return gridfs_insert_file( gfs, remotename, id, length, contenttype ); -} - -MONGO_EXPORT void gridfile_writer_init( gridfile *gfile, gridfs *gfs, - const char *remote_name, const char *content_type ) { - gfile->gfs = gfs; - - bson_oid_gen( &( gfile->id ) ); - gfile->chunk_num = 0; - gfile->length = 0; - gfile->pending_len = 0; - gfile->pending_data = NULL; - - gfile->remote_name = ( char * )bson_malloc( strlen( remote_name ) + 1 ); - strcpy( ( char * )gfile->remote_name, remote_name ); - - gfile->content_type = ( char * )bson_malloc( strlen( content_type ) + 1 ); - strcpy( ( char * )gfile->content_type, content_type ); -} - -MONGO_EXPORT void gridfile_write_buffer( gridfile *gfile, const char *data, - gridfs_offset length ) { - - int bytes_left = 0; - int data_partial_len = 0; - int chunks_to_write = 0; - char *buffer; - bson *oChunk; - gridfs_offset to_write = length + gfile->pending_len; - - if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */ - if( gfile->pending_data ) { - gfile->pending_data = ( char * )bson_realloc( ( void * )gfile->pending_data, gfile->pending_len + to_write ); - memcpy( gfile->pending_data + gfile->pending_len, data, length ); - } else if ( to_write > 0 ) { - gfile->pending_data = ( char * )bson_malloc( to_write ); - memcpy( gfile->pending_data, data, length ); - } - gfile->pending_len += length; - - } else { /* At least one chunk of data to write */ - chunks_to_write = to_write / DEFAULT_CHUNK_SIZE; - bytes_left = to_write % DEFAULT_CHUNK_SIZE; - - /* If there's a pending chunk to be written, we need to combine - * the buffer provided up to DEFAULT_CHUNK_SIZE. - */ - if ( gfile->pending_len > 0 ) { - data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len; - buffer = ( char * )bson_malloc( DEFAULT_CHUNK_SIZE ); - memcpy( buffer, gfile->pending_data, gfile->pending_len ); - memcpy( buffer + gfile->pending_len, data, data_partial_len ); - - oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE ); - mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL ); - chunk_free( oChunk ); - gfile->chunk_num++; - gfile->length += DEFAULT_CHUNK_SIZE; - data += data_partial_len; - - chunks_to_write--; - - bson_free( buffer ); - } - - while( chunks_to_write > 0 ) { - oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE ); - mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL ); - chunk_free( oChunk ); - gfile->chunk_num++; - chunks_to_write--; - gfile->length += DEFAULT_CHUNK_SIZE; - data += DEFAULT_CHUNK_SIZE; - } - - bson_free( gfile->pending_data ); - - /* If there are any leftover bytes, store them as pending data. */ - if( bytes_left == 0 ) - gfile->pending_data = NULL; - else { - gfile->pending_data = ( char * )bson_malloc( bytes_left ); - memcpy( gfile->pending_data, data, bytes_left ); - } - - gfile->pending_len = bytes_left; - } -} - -MONGO_EXPORT int gridfile_writer_done( gridfile *gfile ) { - - /* write any remaining pending chunk data. - * pending data will always take up less than one chunk */ - bson *oChunk; - int response; - if( gfile->pending_data ) { - oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len ); - mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk, NULL ); - chunk_free( oChunk ); - bson_free( gfile->pending_data ); - gfile->length += gfile->pending_len; - } - - /* insert into files collection */ - response = gridfs_insert_file( gfile->gfs, gfile->remote_name, gfile->id, - gfile->length, gfile->content_type ); - - bson_free( gfile->remote_name ); - bson_free( gfile->content_type ); - - return response; -} - -int gridfs_store_file( gridfs *gfs, const char *filename, - const char *remotename, const char *contenttype ) { - - char buffer[DEFAULT_CHUNK_SIZE]; - FILE *fd; - bson_oid_t id; - int chunkNumber = 0; - gridfs_offset length = 0; - gridfs_offset chunkLen = 0; - bson *oChunk; - - /* Open the file and the correct stream */ - if ( strcmp( filename, "-" ) == 0 ) fd = stdin; - else { - fd = fopen( filename, "rb" ); - if (fd == NULL) - return MONGO_ERROR; - } - - /* Generate and append an oid*/ - bson_oid_gen( &id ); - - /* Insert the file chunk by chunk */ - chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd ); - do { - oChunk = chunk_new( id, chunkNumber, buffer, chunkLen ); - mongo_insert( gfs->client, gfs->chunks_ns, oChunk, NULL ); - chunk_free( oChunk ); - length += chunkLen; - chunkNumber++; - chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd ); - } while ( chunkLen != 0 ); - - /* Close the file stream */ - if ( fd != stdin ) fclose( fd ); - - /* Large files Assertion */ - /* assert(length <= 0xffffffff); */ - - /* Optional Remote Name */ - if ( remotename == NULL || *remotename == '\0' ) { - remotename = filename; - } - - /* Inserts file's metadata */ - return gridfs_insert_file( gfs, remotename, id, length, contenttype ); -} - -MONGO_EXPORT void gridfs_remove_filename( gridfs *gfs, const char *filename ) { - bson query; - mongo_cursor *files; - bson file; - bson_iterator it; - bson_oid_t id; - bson b; - - bson_init( &query ); - bson_append_string( &query, "filename", filename ); - bson_finish( &query ); - files = mongo_find( gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0 ); - bson_destroy( &query ); - - /* Remove each file and it's chunks from files named filename */ - while ( mongo_cursor_next( files ) == MONGO_OK ) { - file = files->current; - bson_find( &it, &file, "_id" ); - id = *bson_iterator_oid( &it ); - - /* Remove the file with the specified id */ - bson_init( &b ); - bson_append_oid( &b, "_id", &id ); - bson_finish( &b ); - mongo_remove( gfs->client, gfs->files_ns, &b, NULL ); - bson_destroy( &b ); - - /* Remove all chunks from the file with the specified id */ - bson_init( &b ); - bson_append_oid( &b, "files_id", &id ); - bson_finish( &b ); - mongo_remove( gfs->client, gfs->chunks_ns, &b, NULL ); - bson_destroy( &b ); - } - - mongo_cursor_destroy( files ); -} - -int gridfs_find_query( gridfs *gfs, bson *query, - gridfile *gfile ) { - - bson uploadDate; - bson finalQuery; - bson out; - int i; - - bson_init( &uploadDate ); - bson_append_int( &uploadDate, "uploadDate", -1 ); - bson_finish( &uploadDate ); - - bson_init( &finalQuery ); - bson_append_bson( &finalQuery, "query", query ); - bson_append_bson( &finalQuery, "orderby", &uploadDate ); - bson_finish( &finalQuery ); - - i = ( mongo_find_one( gfs->client, gfs->files_ns, - &finalQuery, NULL, &out ) == MONGO_OK ); - bson_destroy( &uploadDate ); - bson_destroy( &finalQuery ); - if ( !i ) - return MONGO_ERROR; - else { - gridfile_init( gfs, &out, gfile ); - bson_destroy( &out ); - return MONGO_OK; - } -} - -int gridfs_find_filename( gridfs *gfs, const char *filename, - gridfile *gfile ) - -{ - bson query; - int i; - - bson_init( &query ); - bson_append_string( &query, "filename", filename ); - bson_finish( &query ); - i = gridfs_find_query( gfs, &query, gfile ); - bson_destroy( &query ); - return i; -} - -int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile ) - -{ - gfile->gfs = gfs; - gfile->pos = 0; - gfile->meta = ( bson * )bson_malloc( sizeof( bson ) ); - if ( gfile->meta == NULL ) return MONGO_ERROR; - bson_copy( gfile->meta, meta ); - return MONGO_OK; -} - -MONGO_EXPORT void gridfile_destroy( gridfile *gfile ) - -{ - bson_destroy( gfile->meta ); - bson_free( gfile->meta ); -} - -bson_bool_t gridfile_exists( gridfile *gfile ) { - return ( bson_bool_t )( gfile != NULL || gfile->meta == NULL ); -} - -MONGO_EXPORT const char *gridfile_get_filename( gridfile *gfile ) { - bson_iterator it; - - bson_find( &it, gfile->meta, "filename" ); - return bson_iterator_string( &it ); -} - -MONGO_EXPORT int gridfile_get_chunksize( gridfile *gfile ) { - bson_iterator it; - - bson_find( &it, gfile->meta, "chunkSize" ); - return bson_iterator_int( &it ); -} - -MONGO_EXPORT gridfs_offset gridfile_get_contentlength( gridfile *gfile ) { - bson_iterator it; - - bson_find( &it, gfile->meta, "length" ); - - if( bson_iterator_type( &it ) == BSON_INT ) - return ( gridfs_offset )bson_iterator_int( &it ); - else - return ( gridfs_offset )bson_iterator_long( &it ); -} - -MONGO_EXPORT const char *gridfile_get_contenttype( gridfile *gfile ) { - bson_iterator it; - - if ( bson_find( &it, gfile->meta, "contentType" ) ) - return bson_iterator_string( &it ); - else return NULL; -} - -MONGO_EXPORT bson_date_t gridfile_get_uploaddate( gridfile *gfile ) { - bson_iterator it; - - bson_find( &it, gfile->meta, "uploadDate" ); - return bson_iterator_date( &it ); -} - -MONGO_EXPORT const char *gridfile_get_md5( gridfile *gfile ) { - bson_iterator it; - - bson_find( &it, gfile->meta, "md5" ); - return bson_iterator_string( &it ); -} - -const char *gridfile_get_field( gridfile *gfile, const char *name ) { - bson_iterator it; - - bson_find( &it, gfile->meta, name ); - return bson_iterator_value( &it ); -} - -bson_bool_t gridfile_get_boolean( gridfile *gfile, const char *name ) { - bson_iterator it; - - bson_find( &it, gfile->meta, name ); - return bson_iterator_bool( &it ); -} - -MONGO_EXPORT void gridfile_get_metadata( gridfile *gfile, bson* out ) { - bson_iterator it; - - if ( bson_find( &it, gfile->meta, "metadata" ) ) - bson_iterator_subobject( &it, out ); - else - bson_empty( out ); -} - -MONGO_EXPORT int gridfile_get_numchunks( gridfile *gfile ) { - bson_iterator it; - gridfs_offset length; - gridfs_offset chunkSize; - double numchunks; - - bson_find( &it, gfile->meta, "length" ); - - if( bson_iterator_type( &it ) == BSON_INT ) - length = ( gridfs_offset )bson_iterator_int( &it ); - else - length = ( gridfs_offset )bson_iterator_long( &it ); - - bson_find( &it, gfile->meta, "chunkSize" ); - chunkSize = bson_iterator_int( &it ); - numchunks = ( ( double )length/( double )chunkSize ); - return ( numchunks - ( int )numchunks > 0 ) - ? ( int )( numchunks+1 ) - : ( int )( numchunks ); -} - -MONGO_EXPORT void gridfile_get_chunk( gridfile *gfile, int n, bson* out ) { - bson query; - - bson_iterator it; - bson_oid_t id; - int result; - - bson_init( &query ); - bson_find( &it, gfile->meta, "_id" ); - id = *bson_iterator_oid( &it ); - bson_append_oid( &query, "files_id", &id ); - bson_append_int( &query, "n", n ); - bson_finish( &query ); - - result = (mongo_find_one(gfile->gfs->client, - gfile->gfs->chunks_ns, - &query, NULL, out ) == MONGO_OK ); - bson_destroy( &query ); - if (!result) { - bson empty; - bson_empty(&empty); - bson_copy(out, &empty); - } -} - -MONGO_EXPORT mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ) { - bson_iterator it; - bson_oid_t id; - bson gte; - bson query; - bson orderby; - bson command; - mongo_cursor *cursor; - - bson_find( &it, gfile->meta, "_id" ); - id = *bson_iterator_oid( &it ); - - bson_init( &query ); - bson_append_oid( &query, "files_id", &id ); - if ( size == 1 ) { - bson_append_int( &query, "n", start ); - } else { - bson_init( >e ); - bson_append_int( >e, "$gte", start ); - bson_finish( >e ); - bson_append_bson( &query, "n", >e ); - bson_destroy( >e ); - } - bson_finish( &query ); - - bson_init( &orderby ); - bson_append_int( &orderby, "n", 1 ); - bson_finish( &orderby ); - - bson_init( &command ); - bson_append_bson( &command, "query", &query ); - bson_append_bson( &command, "orderby", &orderby ); - bson_finish( &command ); - - cursor = mongo_find( gfile->gfs->client, gfile->gfs->chunks_ns, - &command, NULL, size, 0, 0 ); - - bson_destroy( &command ); - bson_destroy( &query ); - bson_destroy( &orderby ); - - return cursor; -} - -gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ) { - int i; - size_t len; - bson chunk; - bson_iterator it; - const char *data; - const int num = gridfile_get_numchunks( gfile ); - - for ( i=0; ipos < size ) - ? contentlength - gfile->pos - : size; - bytes_left = size; - - first_chunk = ( gfile->pos )/chunksize; - last_chunk = ( gfile->pos+size-1 )/chunksize; - total_chunks = last_chunk - first_chunk + 1; - chunks = gridfile_get_chunks( gfile, first_chunk, total_chunks ); - - for ( i = 0; i < total_chunks; i++ ) { - mongo_cursor_next( chunks ); - chunk = chunks->current; - bson_find( &it, &chunk, "data" ); - chunk_len = bson_iterator_bin_len( &it ); - chunk_data = bson_iterator_bin_data( &it ); - if ( i == 0 ) { - chunk_data += ( gfile->pos )%chunksize; - chunk_len -= ( gfile->pos )%chunksize; - } - if ( bytes_left > chunk_len ) { - memcpy( buf, chunk_data, chunk_len ); - bytes_left -= chunk_len; - buf += chunk_len; - } else { - memcpy( buf, chunk_data, bytes_left ); - } - } - - mongo_cursor_destroy( chunks ); - gfile->pos = gfile->pos + size; - - return size; -} - -MONGO_EXPORT gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ) { - gridfs_offset length; - - length = gridfile_get_contentlength( gfile ); - gfile->pos = length < offset ? length : offset; - return gfile->pos; -} diff --git a/mongo-c-driver-v0.6/src/gridfs.h b/mongo-c-driver-v0.6/src/gridfs.h deleted file mode 100644 index d313e1a..0000000 --- a/mongo-c-driver-v0.6/src/gridfs.h +++ /dev/null @@ -1,332 +0,0 @@ -/** @file gridfs.h - * - * @brief GridFS declarations - * - * */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mongo.h" - -#ifndef MONGO_GRIDFS_H_ -#define MONGO_GRIDFS_H_ - -enum {DEFAULT_CHUNK_SIZE = 256 * 1024}; - -typedef uint64_t gridfs_offset; - -/* A GridFS represents a single collection of GridFS files in the database. */ -typedef struct { - mongo *client; /**> The client to db-connection. */ - const char *dbname; /**> The root database name */ - const char *prefix; /**> The prefix of the GridFS's collections, default is NULL */ - const char *files_ns; /**> The namespace where the file's metadata is stored */ - const char *chunks_ns; /**. The namespace where the files's data is stored in chunks */ -} gridfs; - -/* A GridFile is a single GridFS file. */ -typedef struct { - gridfs *gfs; /**> The GridFS where the GridFile is located */ - bson *meta; /**> The GridFile's bson object where all its metadata is located */ - gridfs_offset pos; /**> The position is the offset in the file */ - bson_oid_t id; /**> The files_id of the gridfile */ - char *remote_name; /**> The name of the gridfile as a string */ - char *content_type; /**> The gridfile's content type */ - gridfs_offset length; /**> The length of this gridfile */ - int chunk_num; /**> The number of the current chunk being written to */ - char *pending_data; /**> A buffer storing data still to be written to chunks */ - int pending_len; /**> Length of pending_data buffer */ -} gridfile; - -MONGO_EXPORT gridfs* gridfs_create(); -MONGO_EXPORT void gridfs_dispose(gridfs* gfs); -MONGO_EXPORT gridfile* gridfile_create(); -MONGO_EXPORT void gridfile_dispose(gridfile* gf); -MONGO_EXPORT void gridfile_get_descriptor(gridfile* gf, bson* out); - -/** - * Initializes a GridFS object - * @param client - db connection - * @param dbname - database name - * @param prefix - collection prefix, default is fs if NULL or empty - * @param gfs - the GridFS object to initialize - * - * @return - MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfs_init( mongo *client, const char *dbname, - const char *prefix, gridfs *gfs ); - -/** - * Destroys a GridFS object. Call this when finished with - * the object.. - * - * @param gfs a grid - */ -MONGO_EXPORT void gridfs_destroy( gridfs *gfs ); - -/** - * Initializes a gridfile for writing incrementally with gridfs_write_buffer. - * Once initialized, you can write any number of buffers with gridfs_write_buffer. - * When done, you must call gridfs_writer_done to save the file metadata. - * - */ -MONGO_EXPORT void gridfile_writer_init( gridfile *gfile, gridfs *gfs, const char *remote_name, - const char *content_type ); - -/** - * Write to a GridFS file incrementally. You can call this function any number - * of times with a new buffer each time. This allows you to effectively - * stream to a GridFS file. When finished, be sure to call gridfs_writer_done. - * - */ -MONGO_EXPORT void gridfile_write_buffer( gridfile *gfile, const char *data, - gridfs_offset length ); - -/** - * Signal that writing of this gridfile is complete by - * writing any buffered chunks along with the entry in the - * files collection. - * - * @return - MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfile_writer_done( gridfile *gfile ); - -/** - * Store a buffer as a GridFS file. - * @param gfs - the working GridFS - * @param data - pointer to buffer to store in GridFS - * @param length - length of the buffer - * @param remotename - filename for use in the database - * @param contenttype - optional MIME type for this object - * - * @return - MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfs_store_buffer( gridfs *gfs, const char *data, gridfs_offset length, - const char *remotename, - const char *contenttype ); - -/** - * Open the file referenced by filename and store it as a GridFS file. - * @param gfs - the working GridFS - * @param filename - local filename relative to the process - * @param remotename - optional filename for use in the database - * @param contenttype - optional MIME type for this object - * - * @return - MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfs_store_file( gridfs *gfs, const char *filename, - const char *remotename, const char *contenttype ); - -/** - * Removes the files referenced by filename from the db - * @param gfs - the working GridFS - * @param filename - the filename of the file/s to be removed - */ -MONGO_EXPORT void gridfs_remove_filename( gridfs *gfs, const char *filename ); - -/** - * Find the first file matching the provided query within the - * GridFS files collection, and return the file as a GridFile. - * - * @param gfs - the working GridFS - * @param query - a pointer to the bson with the query data - * @param gfile - the output GridFile to be initialized - * - * @return MONGO_OK if successful, MONGO_ERROR otherwise - */ -MONGO_EXPORT int gridfs_find_query( gridfs *gfs, bson *query, gridfile *gfile ); - -/** - * Find the first file referenced by filename within the GridFS - * and return it as a GridFile - * @param gfs - the working GridFS - * @param filename - filename of the file to find - * @param gfile - the output GridFile to be intialized - * - * @return MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfs_find_filename( gridfs *gfs, const char *filename, gridfile *gfile ); - -/** - * Initializes a GridFile containing the GridFS and file bson - * @param gfs - the GridFS where the GridFile is located - * @param meta - the file object - * @param gfile - the output GridFile that is being initialized - * - * @return - MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile ); - -/** - * Destroys the GridFile - * - * @param oGridFIle - the GridFile being destroyed - */ -MONGO_EXPORT void gridfile_destroy( gridfile *gfile ); - -/** - * Returns whether or not the GridFile exists - * @param gfile - the GridFile being examined - */ -MONGO_EXPORT bson_bool_t gridfile_exists( gridfile *gfile ); - -/** - * Returns the filename of GridFile - * @param gfile - the working GridFile - * - * @return - the filename of the Gridfile - */ -MONGO_EXPORT const char *gridfile_get_filename( gridfile *gfile ); - -/** - * Returns the size of the chunks of the GridFile - * @param gfile - the working GridFile - * - * @return - the size of the chunks of the Gridfile - */ -MONGO_EXPORT int gridfile_get_chunksize( gridfile *gfile ); - -/** - * Returns the length of GridFile's data - * - * @param gfile - the working GridFile - * - * @return - the length of the Gridfile's data - */ -MONGO_EXPORT gridfs_offset gridfile_get_contentlength( gridfile *gfile ); - -/** - * Returns the MIME type of the GridFile - * - * @param gfile - the working GridFile - * - * @return - the MIME type of the Gridfile - * (NULL if no type specified) - */ -MONGO_EXPORT const char *gridfile_get_contenttype( gridfile *gfile ); - -/** - * Returns the upload date of GridFile - * - * @param gfile - the working GridFile - * - * @return - the upload date of the Gridfile - */ -MONGO_EXPORT bson_date_t gridfile_get_uploaddate( gridfile *gfile ); - -/** - * Returns the MD5 of GridFile - * - * @param gfile - the working GridFile - * - * @return - the MD5 of the Gridfile - */ -MONGO_EXPORT const char *gridfile_get_md5( gridfile *gfile ); - -/** - * Returns the field in GridFile specified by name - * - * @param gfile - the working GridFile - * @param name - the name of the field to be returned - * - * @return - the data of the field specified - * (NULL if none exists) - */ -const char *gridfile_get_field( gridfile *gfile, - const char *name ); - -/** - * Returns a boolean field in GridFile specified by name - * @param gfile - the working GridFile - * @param name - the name of the field to be returned - * - * @return - the boolean of the field specified - * (NULL if none exists) - */ -bson_bool_t gridfile_get_boolean( gridfile *gfile, - const char *name ); - -/** - * Returns the metadata of GridFile - * @param gfile - the working GridFile - * - * @return - the metadata of the Gridfile in a bson object - * (an empty bson is returned if none exists) - */ -MONGO_EXPORT void gridfile_get_metadata( gridfile *gfile, bson* out ); - -/** - * Returns the number of chunks in the GridFile - * @param gfile - the working GridFile - * - * @return - the number of chunks in the Gridfile - */ -MONGO_EXPORT int gridfile_get_numchunks( gridfile *gfile ); - -/** - * Returns chunk n of GridFile - * @param gfile - the working GridFile - * - * @return - the nth chunk of the Gridfile - */ -MONGO_EXPORT void gridfile_get_chunk( gridfile *gfile, int n, bson* out ); - -/** - * Returns a mongo_cursor of *size* chunks starting with chunk *start* - * - * @param gfile - the working GridFile - * @param start - the first chunk in the cursor - * @param size - the number of chunks to be returned - * - * @return - mongo_cursor of the chunks (must be destroyed after use) - */ -MONGO_EXPORT mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ); - -/** - * Writes the GridFile to a stream - * - * @param gfile - the working GridFile - * @param stream - the file stream to write to - */ -MONGO_EXPORT gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ); - -/** - * Reads length bytes from the GridFile to a buffer - * and updates the position in the file. - * (assumes the buffer is large enough) - * (if size is greater than EOF gridfile_read reads until EOF) - * - * @param gfile - the working GridFile - * @param size - the amount of bytes to be read - * @param buf - the buffer to read to - * - * @return - the number of bytes read - */ -MONGO_EXPORT gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf ); - -/** - * Updates the position in the file - * (If the offset goes beyond the contentlength, - * the position is updated to the end of the file.) - * - * @param gfile - the working GridFile - * @param offset - the position to update to - * - * @return - resulting offset location - */ -MONGO_EXPORT gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ); - -#endif diff --git a/mongo-c-driver-v0.6/src/md5.c b/mongo-c-driver-v0.6/src/md5.c deleted file mode 100644 index 68edd29..0000000 --- a/mongo-c-driver-v0.6/src/md5.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. - 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. - 2002-02-21 lpd Added missing #include in test program. - 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). - 1999-05-03 lpd Original version. - */ - -#include "md5.h" -#include - -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ -#ifdef MONGO_BIG_ENDIAN -# define BYTE_ORDER 1 -#else -# define BYTE_ORDER -1 -#endif - -#define T_MASK ((mongo_md5_word_t)~0) -#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) -#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 0x242070db -#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) -#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 0x4787c62a -#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) -#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 0x698098d8 -#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) -#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) -#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 0x6b901122 -#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) -#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 0x49b40821 -#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) -#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 0x265e5a51 -#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) -#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 0x02441453 -#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) -#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 0x21e1cde6 -#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) -#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 0x455a14ed -#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) -#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 0x676f02d9 -#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) -#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) -#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 0x6d9d6122 -#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) -#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 0x4bdecfa9 -#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) -#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 0x289b7ec6 -#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) -#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 0x04881d05 -#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) -#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 0x1fa27cf8 -#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) -#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 0x432aff97 -#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) -#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 0x655b59c3 -#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) -#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) -#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 0x6fa87e4f -#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) -#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 0x4e0811a1 -#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) -#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 0x2ad7d2bb -#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - - -static void -mongo_md5_process(mongo_md5_state_t *pms, const mongo_md5_byte_t *data /*[64]*/) -{ - mongo_md5_word_t - a = pms->abcd[0], b = pms->abcd[1], - c = pms->abcd[2], d = pms->abcd[3]; - mongo_md5_word_t t; -#if BYTE_ORDER > 0 - /* Define storage only for big-endian CPUs. */ - mongo_md5_word_t X[16]; -#else - /* Define storage for little-endian or both types of CPUs. */ - mongo_md5_word_t xbuf[16]; - const mongo_md5_word_t *X; -#endif - - { -#if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const mongo_md5_byte_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!((data - (const mongo_md5_byte_t *)0) & 3)) { - /* data are properly aligned */ - X = (const mongo_md5_word_t *)data; - } else { - /* not aligned */ - memcpy(xbuf, data, 64); - X = xbuf; - } - } -#endif -#if BYTE_ORDER == 0 - else /* dynamic big-endian */ -#endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const mongo_md5_byte_t *xp = data; - int i; - -# if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -# else -# define xbuf X /* (static only) */ -# endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } -#endif - } - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - - /* Round 1. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ -#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + F(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); - SET(c, d, a, b, 10, 17, T11); - SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); - SET(d, a, b, c, 13, 12, T14); - SET(c, d, a, b, 14, 17, T15); - SET(b, c, d, a, 15, 22, T16); -#undef SET - - /* Round 2. */ - /* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ -#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + G(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); - SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); - SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); - SET(b, c, d, a, 12, 20, T32); -#undef SET - - /* Round 3. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + H(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); - SET(c, d, a, b, 11, 16, T35); - SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); - SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); - SET(d, a, b, c, 12, 11, T46); - SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); -#undef SET - - /* Round 4. */ - /* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ -#define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti)\ - t = a + I(b,c,d) + X[k] + Ti;\ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); - SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); - SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); - SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); - SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); - SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); -#undef SET - - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ - pms->abcd[0] += a; - pms->abcd[1] += b; - pms->abcd[2] += c; - pms->abcd[3] += d; -} - -MONGO_EXPORT void -mongo_md5_init(mongo_md5_state_t *pms) -{ - pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; -} - -MONGO_EXPORT void -mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes) -{ - const mongo_md5_byte_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; - mongo_md5_word_t nbits = (mongo_md5_word_t)(nbytes << 3); - - if (nbytes <= 0) - return; - - /* Update the message length. */ - pms->count[1] += nbytes >> 29; - pms->count[0] += nbits; - if (pms->count[0] < nbits) - pms->count[1]++; - - /* Process an initial partial block. */ - if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - mongo_md5_process(pms, pms->buf); - } - - /* Process full blocks. */ - for (; left >= 64; p += 64, left -= 64) - mongo_md5_process(pms, p); - - /* Process a final partial block. */ - if (left) - memcpy(pms->buf, p, left); -} - -MONGO_EXPORT void -mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]) -{ - static const mongo_md5_byte_t pad[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - mongo_md5_byte_t data[8]; - int i; - - /* Save the length before padding. */ - for (i = 0; i < 8; ++i) - data[i] = (mongo_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); - /* Pad to 56 bytes mod 64. */ - mongo_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); - /* Append the length. */ - mongo_md5_append(pms, data, 8); - for (i = 0; i < 16; ++i) - digest[i] = (mongo_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); -} diff --git a/mongo-c-driver-v0.6/src/md5.h b/mongo-c-driver-v0.6/src/md5.h deleted file mode 100644 index 342b6a7..0000000 --- a/mongo-c-driver-v0.6/src/md5.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.h is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . - 1999-05-03 lpd Original version. - */ - -#ifndef MONGO_MD5_H_ -#define MONGO_MD5_H_ - -/* - * This package supports both compile-time and run-time determination of CPU - * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be - * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is - * defined as non-zero, the code will be compiled to run only on big-endian - * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to - * run on either big- or little-endian CPUs, but will run slightly less - * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. - */ -#include "bson.h" - -typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */ -typedef unsigned int mongo_md5_word_t; /* 32-bit word */ - -/* Define the state of the MD5 Algorithm. */ -typedef struct mongo_md5_state_s { - mongo_md5_word_t count[2]; /* message length in bits, lsw first */ - mongo_md5_word_t abcd[4]; /* digest buffer */ - mongo_md5_byte_t buf[64]; /* accumulate block */ -} mongo_md5_state_t; - -#ifdef __cplusplus -extern "C" -{ -#endif - -/* Initialize the algorithm. */ -MONGO_EXPORT void mongo_md5_init(mongo_md5_state_t *pms); - -/* Append a string to the message. */ -MONGO_EXPORT void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes); - -/* Finish the message and return the digest. */ -MONGO_EXPORT void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]); - -#ifdef __cplusplus -} /* end extern "C" */ -#endif - -#endif /* MONGO_MD5_H_ */ diff --git a/mongo-c-driver-v0.6/src/mongo.c b/mongo-c-driver-v0.6/src/mongo.c deleted file mode 100644 index daf92b4..0000000 --- a/mongo-c-driver-v0.6/src/mongo.c +++ /dev/null @@ -1,1705 +0,0 @@ -/* mongo.c */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mongo.h" -#include "md5.h" -#include "env.h" - -#include -#include -#include -#include - -MONGO_EXPORT mongo* mongo_create() { - return (mongo*)bson_malloc(sizeof(mongo)); -} - - -MONGO_EXPORT void mongo_dispose(mongo* conn) { - free(conn); -} - -MONGO_EXPORT int mongo_get_err(mongo* conn) { - return conn->err; -} - - -MONGO_EXPORT int mongo_is_connected(mongo* conn) { - return conn->connected != 0; -} - - -MONGO_EXPORT int mongo_get_op_timeout(mongo* conn) { - return conn->op_timeout_ms; -} - - -const char* _get_host_port(mongo_host_port* hp) { - static char _hp[sizeof(hp->host)+12]; - bson_sprintf(_hp, "%s:%d", hp->host, hp->port); - return _hp; -} - - -MONGO_EXPORT const char* mongo_get_primary(mongo* conn) { - mongo* conn_ = (mongo*)conn; - if( !(conn_->connected) || (conn_->primary->host == '\0') ) - return NULL; - return _get_host_port(conn_->primary); -} - - -MONGO_EXPORT int mongo_get_socket(mongo* conn) { - mongo* conn_ = (mongo*)conn; - return conn_->sock; -} - - -MONGO_EXPORT int mongo_get_host_count(mongo* conn) { - mongo_replset* r = conn->replset; - mongo_host_port* hp; - int count = 0; - if (!r) return 0; - for (hp = r->hosts; hp; hp = hp->next) - ++count; - return count; -} - - -MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i) { - mongo_replset* r = conn->replset; - mongo_host_port* hp; - int count = 0; - if (!r) return 0; - for (hp = r->hosts; hp; hp = hp->next) { - if (count == i) - return _get_host_port(hp); - ++count; - } - return 0; -} - - -MONGO_EXPORT mongo_cursor* mongo_cursor_create() { - return (mongo_cursor*)bson_malloc(sizeof(mongo_cursor)); -} - - -MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor) { - free(cursor); -} - - -MONGO_EXPORT int mongo_get_server_err(mongo* conn) { - return conn->lasterrcode; -} - - -MONGO_EXPORT const char* mongo_get_server_err_string(mongo* conn) { - return conn->lasterrstr; -} - -MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err, const char *str, - int errcode ) { - int errstr_size, str_size; - - conn->err = err; - conn->errcode = errcode; - - if( str ) { - str_size = strlen( str ) + 1; - errstr_size = str_size > MONGO_ERR_LEN ? MONGO_ERR_LEN : str_size; - memcpy( conn->errstr, str, errstr_size ); - conn->errstr[errstr_size-1] = '\0'; - } -} - -MONGO_EXPORT void mongo_clear_errors( mongo *conn ) { - conn->err = 0; - conn->errcode = 0; - conn->lasterrcode = 0; - memset( conn->errstr, 0, MONGO_ERR_LEN ); - memset( conn->lasterrstr, 0, MONGO_ERR_LEN ); -} - -/* Note: this function returns a char* which must be freed. */ -static char *mongo_ns_to_cmd_db( const char *ns ) { - char *current = NULL; - char *cmd_db_name = NULL; - int len = 0; - - for( current = (char *)ns; *current != '.'; current++ ) { - len++; - } - - cmd_db_name = (char *)bson_malloc( len + 6 ); - strncpy( cmd_db_name, ns, len ); - strncpy( cmd_db_name + len, ".$cmd", 6 ); - - return cmd_db_name; -} - -MONGO_EXPORT int mongo_validate_ns( mongo *conn, const char *ns ) { - char *last = NULL; - char *current = NULL; - const char *db_name = ns; - char *collection_name = NULL; - char errmsg[64]; - int ns_len = 0; - - /* If the first character is a '.', fail. */ - if( *ns == '.' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, "ns cannot start with a '.'.", 0 ); - return MONGO_ERROR; - } - - /* Find the division between database and collection names. */ - for( current = (char *)ns; *current != '\0'; current++ ) { - if( *current == '.' ) { - current++; - break; - } - } - - /* Fail because the ns doesn't contain a '.' - * or the collection part starts with a dot. */ - if( *current == '\0' || *current == '.' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, "ns cannot start with a '.'.", 0 ); - return MONGO_ERROR; - } - - /* Fail if collection length is 0. */ - if( *(current + 1) == '\0' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, "Collection name missing.", 0 ); - return MONGO_ERROR; - } - - - /* Point to the beginning of the collection name. */ - collection_name = current; - - /* Ensure that the database name is greater than one char.*/ - if( collection_name - 1 == db_name ) { - __mongo_set_error( conn, MONGO_NS_INVALID, "Database name missing.", 0 ); - return MONGO_ERROR; - } - - /* Go back and validate the database name. */ - for( current = (char *)db_name; *current != '.'; current++ ) { - switch( *current ) { - case ' ': - case '$': - case '/': - case '\\': - __mongo_set_error( conn, MONGO_NS_INVALID, - "Database name may not contain ' ', '$', '/', or '\\'", 0 ); - return MONGO_ERROR; - default: - break; - } - - ns_len++; - } - - /* Add one to the length for the '.' character. */ - ns_len++; - - /* Now validate the collection name. */ - for( current = collection_name; *current != '\0'; current++ ) { - - /* Cannot have two consecutive dots. */ - if( last && *last == '.' && *current == '.' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, - "Collection may not contain two consecutive '.'", 0 ); - return MONGO_ERROR; - } - - /* Cannot contain a '$' */ - if( *current == '$' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, - "Collection may not contain '$'", 0 ); - return MONGO_ERROR; - } - - last = current; - ns_len++; - } - - if( ns_len > 128 ) { - bson_sprintf( errmsg, "Namespace too long; has %d but must <= 128.", - ns_len ); - __mongo_set_error( conn, MONGO_NS_INVALID, errmsg, 0 ); - return MONGO_ERROR; - } - - /* Cannot end with a '.' */ - if( *(current - 1) == '.' ) { - __mongo_set_error( conn, MONGO_NS_INVALID, - "Collection may not end with '.'", 0 ); - return MONGO_ERROR; - } - - return MONGO_OK; -} - -static void mongo_set_last_error( mongo *conn, bson_iterator *it, bson *obj ) { - int result_len = bson_iterator_string_len( it ); - const char *result_string = bson_iterator_string( it ); - int len = result_len < MONGO_ERR_LEN ? result_len : MONGO_ERR_LEN; - memcpy( conn->lasterrstr, result_string, len ); - - if( bson_find( it, obj, "code" ) != BSON_NULL ) - conn->lasterrcode = bson_iterator_int( it ); -} - -static const int ZERO = 0; -static const int ONE = 1; -mongo_message *mongo_message_create( int len , int id , int responseTo , int op ) { - mongo_message *mm = ( mongo_message * )bson_malloc( len ); - - if ( !id ) - id = rand(); - - /* native endian (converted on send) */ - mm->head.len = len; - mm->head.id = id; - mm->head.responseTo = responseTo; - mm->head.op = op; - - return mm; -} - -/* Always calls bson_free(mm) */ -int mongo_message_send( mongo *conn, mongo_message *mm ) { - mongo_header head; /* little endian */ - int res; - bson_little_endian32( &head.len, &mm->head.len ); - bson_little_endian32( &head.id, &mm->head.id ); - bson_little_endian32( &head.responseTo, &mm->head.responseTo ); - bson_little_endian32( &head.op, &mm->head.op ); - - res = mongo_env_write_socket( conn, &head, sizeof( head ) ); - if( res != MONGO_OK ) { - bson_free( mm ); - return res; - } - - res = mongo_env_write_socket( conn, &mm->data, mm->head.len - sizeof( head ) ); - if( res != MONGO_OK ) { - bson_free( mm ); - return res; - } - - bson_free( mm ); - return MONGO_OK; -} - -int mongo_read_response( mongo *conn, mongo_reply **reply ) { - mongo_header head; /* header from network */ - mongo_reply_fields fields; /* header from network */ - mongo_reply *out; /* native endian */ - unsigned int len; - int res; - - mongo_env_read_socket( conn, &head, sizeof( head ) ); - mongo_env_read_socket( conn, &fields, sizeof( fields ) ); - - bson_little_endian32( &len, &head.len ); - - if ( len < sizeof( head )+sizeof( fields ) || len > 64*1024*1024 ) - return MONGO_READ_SIZE_ERROR; /* most likely corruption */ - - out = ( mongo_reply * )bson_malloc( len ); - - out->head.len = len; - bson_little_endian32( &out->head.id, &head.id ); - bson_little_endian32( &out->head.responseTo, &head.responseTo ); - bson_little_endian32( &out->head.op, &head.op ); - - bson_little_endian32( &out->fields.flag, &fields.flag ); - bson_little_endian64( &out->fields.cursorID, &fields.cursorID ); - bson_little_endian32( &out->fields.start, &fields.start ); - bson_little_endian32( &out->fields.num, &fields.num ); - - res = mongo_env_read_socket( conn, &out->objs, len-sizeof( head )-sizeof( fields ) ); - if( res != MONGO_OK ) { - bson_free( out ); - return res; - } - - *reply = out; - - return MONGO_OK; -} - - -char *mongo_data_append( char *start , const void *data , int len ) { - memcpy( start , data , len ); - return start + len; -} - -char *mongo_data_append32( char *start , const void *data ) { - bson_little_endian32( start , data ); - return start + 4; -} - -char *mongo_data_append64( char *start , const void *data ) { - bson_little_endian64( start , data ); - return start + 8; -} - -/* Connection API */ - -static int mongo_check_is_master( mongo *conn ) { - bson out; - bson_iterator it; - bson_bool_t ismaster = 0; - int max_bson_size = MONGO_DEFAULT_MAX_BSON_SIZE; - - out.data = NULL; - - if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { - if( bson_find( &it, &out, "ismaster" ) ) - ismaster = bson_iterator_bool( &it ); - if( bson_find( &it, &out, "maxBsonObjectSize" ) ) { - max_bson_size = bson_iterator_int( &it ); - } - conn->max_bson_size = max_bson_size; - } else { - return MONGO_ERROR; - } - - bson_destroy( &out ); - - if( ismaster ) - return MONGO_OK; - else { - conn->err = MONGO_CONN_NOT_MASTER; - return MONGO_ERROR; - } -} - -MONGO_EXPORT void mongo_init_sockets( void ) { - mongo_env_sock_init(); -} - -MONGO_EXPORT void mongo_init( mongo *conn ) { - memset( conn, 0, sizeof( mongo ) ); - conn->max_bson_size = MONGO_DEFAULT_MAX_BSON_SIZE; -} - -MONGO_EXPORT int mongo_connect( mongo *conn , const char *host, int port ) { - mongo_init( conn ); - - conn->primary = bson_malloc( sizeof( mongo_host_port ) ); - strncpy( conn->primary->host, host, strlen( host ) + 1 ); - conn->primary->port = port; - conn->primary->next = NULL; - - if( mongo_env_socket_connect( conn, host, port ) != MONGO_OK ) - return MONGO_ERROR; - - if( mongo_check_is_master( conn ) != MONGO_OK ) - return MONGO_ERROR; - else - return MONGO_OK; -} - -MONGO_EXPORT void mongo_replset_init( mongo *conn, const char *name ) { - mongo_init( conn ); - - conn->replset = bson_malloc( sizeof( mongo_replset ) ); - conn->replset->primary_connected = 0; - conn->replset->seeds = NULL; - conn->replset->hosts = NULL; - conn->replset->name = ( char * )bson_malloc( strlen( name ) + 1 ); - memcpy( conn->replset->name, name, strlen( name ) + 1 ); - - conn->primary = bson_malloc( sizeof( mongo_host_port ) ); - conn->primary->host[0] = '\0'; - conn->primary->next = NULL; -} - -static void mongo_replset_add_node( mongo_host_port **list, const char *host, int port ) { - mongo_host_port *host_port = bson_malloc( sizeof( mongo_host_port ) ); - host_port->port = port; - host_port->next = NULL; - strncpy( host_port->host, host, strlen( host ) + 1 ); - - if( *list == NULL ) - *list = host_port; - else { - mongo_host_port *p = *list; - while( p->next != NULL ) - p = p->next; - p->next = host_port; - } -} - -static void mongo_replset_free_list( mongo_host_port **list ) { - mongo_host_port *node = *list; - mongo_host_port *prev; - - while( node != NULL ) { - prev = node; - node = node->next; - bson_free( prev ); - } - - *list = NULL; -} - -MONGO_EXPORT void mongo_replset_add_seed( mongo *conn, const char *host, int port ) { - mongo_replset_add_node( &conn->replset->seeds, host, port ); -} - -void mongo_parse_host( const char *host_string, mongo_host_port *host_port ) { - int len, idx, split; - len = split = idx = 0; - - /* Split the host_port string at the ':' */ - while( 1 ) { - if( *( host_string + len ) == '\0' ) - break; - if( *( host_string + len ) == ':' ) - split = len; - - len++; - } - - /* If 'split' is set, we know the that port exists; - * Otherwise, we set the default port. */ - idx = split ? split : len; - memcpy( host_port->host, host_string, idx ); - memcpy( host_port->host + idx, "\0", 1 ); - if( split ) - host_port->port = atoi( host_string + idx + 1 ); - else - host_port->port = MONGO_DEFAULT_PORT; -} - -static void mongo_replset_check_seed( mongo *conn ) { - bson out; - bson hosts; - const char *data; - bson_iterator it; - bson_iterator it_sub; - const char *host_string; - mongo_host_port *host_port = NULL; - - out.data = NULL; - - hosts.data = NULL; - - if( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { - - if( bson_find( &it, &out, "hosts" ) ) { - data = bson_iterator_value( &it ); - bson_iterator_from_buffer( &it_sub, data ); - - /* Iterate over host list, adding each host to the - * connection's host list. */ - while( bson_iterator_next( &it_sub ) ) { - host_string = bson_iterator_string( &it_sub ); - - host_port = bson_malloc( sizeof( mongo_host_port ) ); - mongo_parse_host( host_string, host_port ); - - if( host_port ) { - mongo_replset_add_node( &conn->replset->hosts, - host_port->host, host_port->port ); - - bson_free( host_port ); - host_port = NULL; - } - } - } - } - - bson_destroy( &out ); - bson_destroy( &hosts ); - mongo_env_close_socket( conn->sock ); - conn->sock = 0; - conn->connected = 0; - -} - -/* Find out whether the current connected node is master, and - * verify that the node's replica set name matched the provided name - */ -static int mongo_replset_check_host( mongo *conn ) { - - bson out; - bson_iterator it; - bson_bool_t ismaster = 0; - const char *set_name; - int max_bson_size = MONGO_DEFAULT_MAX_BSON_SIZE; - - out.data = NULL; - - if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { - if( bson_find( &it, &out, "ismaster" ) ) - ismaster = bson_iterator_bool( &it ); - - if( bson_find( &it, &out, "maxBsonObjectSize" ) ) - max_bson_size = bson_iterator_int( &it ); - conn->max_bson_size = max_bson_size; - - if( bson_find( &it, &out, "setName" ) ) { - set_name = bson_iterator_string( &it ); - if( strcmp( set_name, conn->replset->name ) != 0 ) { - bson_destroy( &out ); - conn->err = MONGO_CONN_BAD_SET_NAME; - return MONGO_ERROR; - } - } - } - - bson_destroy( &out ); - - if( ismaster ) { - conn->replset->primary_connected = 1; - } else { - mongo_env_close_socket( conn->sock ); - } - - return MONGO_OK; -} - -MONGO_EXPORT int mongo_replset_connect( mongo *conn ) { - - int res = 0; - mongo_host_port *node; - - conn->sock = 0; - conn->connected = 0; - - /* First iterate over the seed nodes to get the canonical list of hosts - * from the replica set. Break out once we have a host list. - */ - node = conn->replset->seeds; - while( node != NULL ) { - res = mongo_env_socket_connect( conn, ( const char * )&node->host, node->port ); - if( res == MONGO_OK ) { - mongo_replset_check_seed( conn ); - if( conn->replset->hosts ) - break; - } - node = node->next; - } - - /* Iterate over the host list, checking for the primary node. */ - if( !conn->replset->hosts ) { - conn->err = MONGO_CONN_NO_PRIMARY; - return MONGO_ERROR; - } else { - node = conn->replset->hosts; - - while( node != NULL ) { - res = mongo_env_socket_connect( conn, ( const char * )&node->host, node->port ); - - if( res == MONGO_OK ) { - if( mongo_replset_check_host( conn ) != MONGO_OK ) - return MONGO_ERROR; - - /* Primary found, so return. */ - else if( conn->replset->primary_connected ) { - strncpy( conn->primary->host, node->host, strlen( node->host ) + 1 ); - conn->primary->port = node->port; - return MONGO_OK; - } - - /* No primary, so close the connection. */ - else { - mongo_env_close_socket( conn->sock ); - conn->sock = 0; - conn->connected = 0; - } - } - - node = node->next; - } - } - - - conn->err = MONGO_CONN_NO_PRIMARY; - return MONGO_ERROR; -} - -MONGO_EXPORT int mongo_set_op_timeout( mongo *conn, int millis ) { - conn->op_timeout_ms = millis; - if( conn->sock && conn->connected ) - mongo_env_set_socket_op_timeout( conn, millis ); - - return MONGO_OK; -} - -MONGO_EXPORT int mongo_reconnect( mongo *conn ) { - int res; - mongo_disconnect( conn ); - - if( conn->replset ) { - conn->replset->primary_connected = 0; - mongo_replset_free_list( &conn->replset->hosts ); - conn->replset->hosts = NULL; - res = mongo_replset_connect( conn ); - return res; - } else - return mongo_env_socket_connect( conn, conn->primary->host, conn->primary->port ); -} - -MONGO_EXPORT int mongo_check_connection( mongo *conn ) { - if( ! conn->connected ) - return MONGO_ERROR; - - if( mongo_simple_int_command( conn, "admin", "ping", 1, NULL ) == MONGO_OK ) - return MONGO_OK; - else - return MONGO_ERROR; -} - -MONGO_EXPORT void mongo_disconnect( mongo *conn ) { - if( ! conn->connected ) - return; - - if( conn->replset ) { - conn->replset->primary_connected = 0; - mongo_replset_free_list( &conn->replset->hosts ); - conn->replset->hosts = NULL; - } - - mongo_env_close_socket( conn->sock ); - - conn->sock = 0; - conn->connected = 0; -} - -MONGO_EXPORT void mongo_destroy( mongo *conn ) { - mongo_disconnect( conn ); - - if( conn->replset ) { - mongo_replset_free_list( &conn->replset->seeds ); - mongo_replset_free_list( &conn->replset->hosts ); - bson_free( conn->replset->name ); - bson_free( conn->replset ); - conn->replset = NULL; - } - - bson_free( conn->primary ); - - mongo_clear_errors( conn ); -} - -/* Determine whether this BSON object is valid for the given operation. */ -static int mongo_bson_valid( mongo *conn, const bson *bson, int write ) { - int size; - - size = bson_size( bson ); - if( size > conn->max_bson_size ) { - conn->err = MONGO_BSON_TOO_LARGE; - return MONGO_ERROR; - } - - if( ! bson->finished ) { - conn->err = MONGO_BSON_NOT_FINISHED; - return MONGO_ERROR; - } - - if( bson->err & BSON_NOT_UTF8 ) { - conn->err = MONGO_BSON_INVALID; - return MONGO_ERROR; - } - - if( write ) { - if( ( bson->err & BSON_FIELD_HAS_DOT ) || - ( bson->err & BSON_FIELD_INIT_DOLLAR ) ) { - - conn->err = MONGO_BSON_INVALID; - return MONGO_ERROR; - - } - } - - conn->err = 0; - - return MONGO_OK; -} - -/* Determine whether this BSON object is valid for the given operation. */ -static int mongo_cursor_bson_valid( mongo_cursor *cursor, const bson *bson ) { - if( ! bson->finished ) { - cursor->err = MONGO_CURSOR_BSON_ERROR; - cursor->conn->err = MONGO_BSON_NOT_FINISHED; - return MONGO_ERROR; - } - - if( bson->err & BSON_NOT_UTF8 ) { - cursor->err = MONGO_CURSOR_BSON_ERROR; - cursor->conn->err = MONGO_BSON_INVALID; - return MONGO_ERROR; - } - - return MONGO_OK; -} - -static int mongo_check_last_error( mongo *conn, const char *ns, - mongo_write_concern *write_concern ) { - - bson response = {NULL, 0}; - bson fields; - bson_iterator it; - int res = 0; - char *cmd_ns = mongo_ns_to_cmd_db( ns ); - - res = mongo_find_one( conn, cmd_ns, write_concern->cmd, bson_empty( &fields ), &response ); - bson_free( cmd_ns ); - - if( res != MONGO_OK ) - return MONGO_ERROR; - else { - if( ( bson_find( &it, &response, "$err" ) == BSON_STRING ) || - ( bson_find( &it, &response, "err" ) == BSON_STRING ) ) { - - __mongo_set_error( conn, MONGO_WRITE_ERROR, - "See conn->lasterrstr for details.", 0 ); - mongo_set_last_error( conn, &it, &response ); - return MONGO_ERROR; - } else - return MONGO_OK; - } -} - -static int mongo_choose_write_concern( mongo *conn, - mongo_write_concern *custom_write_concern, - mongo_write_concern **write_concern ) { - - if( custom_write_concern ) { - *write_concern = custom_write_concern; - } - else if( conn->write_concern ) { - *write_concern = conn->write_concern; - } - - if( *write_concern && !((*write_concern)->cmd) ) { - __mongo_set_error( conn, MONGO_WRITE_CONCERN_INVALID, - "Must call mongo_write_concern_finish() before using *write_concern.", 0 ); - return MONGO_ERROR; - } - else - return MONGO_OK; -} - - -/********************************************************************* -CRUD API -**********************************************************************/ - -MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns, - const bson *bson, mongo_write_concern *custom_write_concern ) { - - char *data; - mongo_message *mm; - mongo_write_concern *write_concern = NULL; - - if( mongo_validate_ns( conn, ns ) != MONGO_OK ) - return MONGO_ERROR; - - if( mongo_bson_valid( conn, bson, 1 ) != MONGO_OK ) { - return MONGO_ERROR; - } - - if( mongo_choose_write_concern( conn, custom_write_concern, - &write_concern ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - mm = mongo_message_create( 16 /* header */ - + 4 /* ZERO */ - + strlen( ns ) - + 1 + bson_size( bson ) - , 0, 0, MONGO_OP_INSERT ); - - data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, ns, strlen( ns ) + 1 ); - data = mongo_data_append( data, bson->data, bson_size( bson ) ); - - - /* TODO: refactor so that we can send the insert message - and the getlasterror messages together. */ - if( write_concern ) { - if( mongo_message_send( conn, mm ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - return mongo_check_last_error( conn, ns, write_concern ); - } - else { - return mongo_message_send( conn, mm ); - } -} - -MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns, - const bson **bsons, int count, mongo_write_concern *custom_write_concern, - int flags ) { - - mongo_message *mm; - mongo_write_concern *write_concern = NULL; - int i; - char *data; - int overhead = 16 + 4 + strlen( ns ) + 1; - int size = overhead; - - if( mongo_validate_ns( conn, ns ) != MONGO_OK ) - return MONGO_ERROR; - - for( i=0; i conn->max_bson_size ) { - conn->err = MONGO_BSON_TOO_LARGE; - return MONGO_ERROR; - } - - if( mongo_choose_write_concern( conn, custom_write_concern, - &write_concern ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - mm = mongo_message_create( size , 0 , 0 , MONGO_OP_INSERT ); - - data = &mm->data; - if( flags & MONGO_CONTINUE_ON_ERROR ) - data = mongo_data_append32( data, &ONE ); - else - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, ns, strlen( ns ) + 1 ); - - for( i=0; idata, bson_size( bsons[i] ) ); - } - - /* TODO: refactor so that we can send the insert message - * and the getlasterror messages together. */ - if( write_concern ) { - if( mongo_message_send( conn, mm ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - return mongo_check_last_error( conn, ns, write_concern ); - } - else { - return mongo_message_send( conn, mm ); - } -} - -MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond, - const bson *op, int flags, mongo_write_concern *custom_write_concern ) { - - char *data; - mongo_message *mm; - mongo_write_concern *write_concern = NULL; - - /* Make sure that the op BSON is valid UTF-8. - * TODO: decide whether to check cond as well. - * */ - if( mongo_bson_valid( conn, ( bson * )op, 0 ) != MONGO_OK ) { - return MONGO_ERROR; - } - - if( mongo_choose_write_concern( conn, custom_write_concern, - &write_concern ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - mm = mongo_message_create( 16 /* header */ - + 4 /* ZERO */ - + strlen( ns ) + 1 - + 4 /* flags */ - + bson_size( cond ) - + bson_size( op ) - , 0 , 0 , MONGO_OP_UPDATE ); - - data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, ns, strlen( ns ) + 1 ); - data = mongo_data_append32( data, &flags ); - data = mongo_data_append( data, cond->data, bson_size( cond ) ); - data = mongo_data_append( data, op->data, bson_size( op ) ); - - /* TODO: refactor so that we can send the insert message - * and the getlasterror messages together. */ - if( write_concern ) { - if( mongo_message_send( conn, mm ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - return mongo_check_last_error( conn, ns, write_concern ); - } - else { - return mongo_message_send( conn, mm ); - } -} - -MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond, - mongo_write_concern *custom_write_concern ) { - - char *data; - mongo_message *mm; - mongo_write_concern *write_concern = NULL; - - /* Make sure that the BSON is valid UTF-8. - * TODO: decide whether to check cond as well. - * */ - if( mongo_bson_valid( conn, ( bson * )cond, 0 ) != MONGO_OK ) { - return MONGO_ERROR; - } - - if( mongo_choose_write_concern( conn, custom_write_concern, - &write_concern ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - mm = mongo_message_create( 16 /* header */ - + 4 /* ZERO */ - + strlen( ns ) + 1 - + 4 /* ZERO */ - + bson_size( cond ) - , 0 , 0 , MONGO_OP_DELETE ); - - data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, ns, strlen( ns ) + 1 ); - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, cond->data, bson_size( cond ) ); - - /* TODO: refactor so that we can send the insert message - * and the getlasterror messages together. */ - if( write_concern ) { - if( mongo_message_send( conn, mm ) == MONGO_ERROR ) { - return MONGO_ERROR; - } - - return mongo_check_last_error( conn, ns, write_concern ); - } - else { - return mongo_message_send( conn, mm ); - } -} - - -/********************************************************************* -Write Concern API -**********************************************************************/ - -MONGO_EXPORT void mongo_write_concern_init( mongo_write_concern *write_concern ) { - memset( write_concern, 0, sizeof( mongo_write_concern ) ); -} - -MONGO_EXPORT int mongo_write_concern_finish( mongo_write_concern *write_concern ) { - bson *command; - - /* Destory any existing serialized write concern object and reuse it. */ - if( write_concern->cmd ) { - bson_destroy( write_concern->cmd ); - command = write_concern->cmd; - } - else - command = (bson *)bson_malloc( sizeof( bson ) ); - - if( !command ) { - return MONGO_ERROR; - } - - bson_init( command ); - - bson_append_int( command, "getlasterror", 1 ); - - if( write_concern->mode ) { - bson_append_string( command, "w", write_concern->mode ); - } - - else if( write_concern->w ) { - bson_append_int( command, "w", write_concern->w ); - } - - if( write_concern->wtimeout ) { - bson_append_int( command, "wtimeout", write_concern->wtimeout ); - } - - if( write_concern->j ) { - bson_append_int( command, "j", write_concern->j ); - } - - if( write_concern->fsync ) { - bson_append_int( command, "fsync", write_concern->fsync ); - } - - bson_finish( command ); - - /* write_concern now owns the BSON command object. - * This is freed in mongo_write_concern_destroy(). */ - write_concern->cmd = command; - - return MONGO_OK; -} - -MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern ) { - if( !write_concern ) - return; - - if( write_concern->cmd ) - bson_destroy( write_concern->cmd ); - - bson_free( write_concern->cmd ); -} - -MONGO_EXPORT void mongo_set_write_concern( mongo *conn, - mongo_write_concern *write_concern ) { - - conn->write_concern = write_concern; -} - -/** - * Free the write_concern object (specifically, the BSON object that it holds). - */ -MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern ); - - -static int mongo_cursor_op_query( mongo_cursor *cursor ) { - int res; - bson empty; - char *data; - mongo_message *mm; - bson temp; - bson_iterator it; - - /* Clear any errors. */ - mongo_clear_errors( cursor->conn ); - - /* Set up default values for query and fields, if necessary. */ - if( ! cursor->query ) - cursor->query = bson_empty( &empty ); - else if( mongo_cursor_bson_valid( cursor, cursor->query ) != MONGO_OK ) - return MONGO_ERROR; - - if( ! cursor->fields ) - cursor->fields = bson_empty( &empty ); - else if( mongo_cursor_bson_valid( cursor, cursor->fields ) != MONGO_OK ) - return MONGO_ERROR; - - mm = mongo_message_create( 16 + /* header */ - 4 + /* options */ - strlen( cursor->ns ) + 1 + /* ns */ - 4 + 4 + /* skip,return */ - bson_size( cursor->query ) + - bson_size( cursor->fields ) , - 0 , 0 , MONGO_OP_QUERY ); - - data = &mm->data; - data = mongo_data_append32( data , &cursor->options ); - data = mongo_data_append( data , cursor->ns , strlen( cursor->ns ) + 1 ); - data = mongo_data_append32( data , &cursor->skip ); - data = mongo_data_append32( data , &cursor->limit ); - data = mongo_data_append( data , cursor->query->data , bson_size( cursor->query ) ); - if ( cursor->fields ) - data = mongo_data_append( data , cursor->fields->data , bson_size( cursor->fields ) ); - - bson_fatal_msg( ( data == ( ( char * )mm ) + mm->head.len ), "query building fail!" ); - - res = mongo_message_send( cursor->conn , mm ); - if( res != MONGO_OK ) { - return MONGO_ERROR; - } - - res = mongo_read_response( cursor->conn, ( mongo_reply ** )&( cursor->reply ) ); - if( res != MONGO_OK ) { - return MONGO_ERROR; - } - - if( cursor->reply->fields.num == 1 ) { - bson_init_data( &temp, &cursor->reply->objs ); - if( bson_find( &it, &temp, "$err" ) ) { - mongo_set_last_error( cursor->conn, &it, &temp ); - cursor->err = MONGO_CURSOR_QUERY_FAIL; - return MONGO_ERROR; - } - } - - cursor->seen += cursor->reply->fields.num; - cursor->flags |= MONGO_CURSOR_QUERY_SENT; - return MONGO_OK; -} - -static int mongo_cursor_get_more( mongo_cursor *cursor ) { - int res; - - if( cursor->limit > 0 && cursor->seen >= cursor->limit ) { - cursor->err = MONGO_CURSOR_EXHAUSTED; - return MONGO_ERROR; - } else if( ! cursor->reply ) { - cursor->err = MONGO_CURSOR_INVALID; - return MONGO_ERROR; - } else if( ! cursor->reply->fields.cursorID ) { - cursor->err = MONGO_CURSOR_EXHAUSTED; - return MONGO_ERROR; - } else { - char *data; - int sl = strlen( cursor->ns )+1; - int limit = 0; - mongo_message *mm; - - if( cursor->limit > 0 ) - limit = cursor->limit - cursor->seen; - - mm = mongo_message_create( 16 /*header*/ - +4 /*ZERO*/ - +sl - +4 /*numToReturn*/ - +8 /*cursorID*/ - , 0, 0, MONGO_OP_GET_MORE ); - data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append( data, cursor->ns, sl ); - data = mongo_data_append32( data, &limit ); - data = mongo_data_append64( data, &cursor->reply->fields.cursorID ); - - bson_free( cursor->reply ); - res = mongo_message_send( cursor->conn, mm ); - if( res != MONGO_OK ) { - mongo_cursor_destroy( cursor ); - return MONGO_ERROR; - } - - res = mongo_read_response( cursor->conn, &( cursor->reply ) ); - if( res != MONGO_OK ) { - mongo_cursor_destroy( cursor ); - return MONGO_ERROR; - } - cursor->current.data = NULL; - cursor->seen += cursor->reply->fields.num; - - return MONGO_OK; - } -} - -MONGO_EXPORT mongo_cursor *mongo_find( mongo *conn, const char *ns, const bson *query, - const bson *fields, int limit, int skip, int options ) { - - mongo_cursor *cursor = ( mongo_cursor * )bson_malloc( sizeof( mongo_cursor ) ); - mongo_cursor_init( cursor, conn, ns ); - cursor->flags |= MONGO_CURSOR_MUST_FREE; - - mongo_cursor_set_query( cursor, query ); - mongo_cursor_set_fields( cursor, fields ); - mongo_cursor_set_limit( cursor, limit ); - mongo_cursor_set_skip( cursor, skip ); - mongo_cursor_set_options( cursor, options ); - - if( mongo_cursor_op_query( cursor ) == MONGO_OK ) - return cursor; - else { - mongo_cursor_destroy( cursor ); - return NULL; - } -} - -MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, const bson *query, - const bson *fields, bson *out ) { - - mongo_cursor cursor[1]; - mongo_cursor_init( cursor, conn, ns ); - mongo_cursor_set_query( cursor, query ); - mongo_cursor_set_fields( cursor, fields ); - mongo_cursor_set_limit( cursor, 1 ); - - if ( mongo_cursor_next( cursor ) == MONGO_OK ) { - if( out ) { - bson_init_size( out, bson_size( (bson *)&cursor->current ) ); - memcpy( out->data, cursor->current.data, - bson_size( (bson *)&cursor->current ) ); - out->finished = 1; - } - mongo_cursor_destroy( cursor ); - return MONGO_OK; - } else { - mongo_cursor_destroy( cursor ); - return MONGO_ERROR; - } -} - -MONGO_EXPORT void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ) { - memset( cursor, 0, sizeof( mongo_cursor ) ); - cursor->conn = conn; - cursor->ns = ( const char * )bson_malloc( strlen( ns ) + 1 ); - strncpy( ( char * )cursor->ns, ns, strlen( ns ) + 1 ); - cursor->current.data = NULL; -} - -MONGO_EXPORT void mongo_cursor_set_query( mongo_cursor *cursor, const bson *query ) { - cursor->query = query; -} - -MONGO_EXPORT void mongo_cursor_set_fields( mongo_cursor *cursor, const bson *fields ) { - cursor->fields = fields; -} - -MONGO_EXPORT void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ) { - cursor->skip = skip; -} - -MONGO_EXPORT void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ) { - cursor->limit = limit; -} - -MONGO_EXPORT void mongo_cursor_set_options( mongo_cursor *cursor, int options ) { - cursor->options = options; -} - -MONGO_EXPORT const char *mongo_cursor_data( mongo_cursor *cursor ) { - return cursor->current.data; -} - -MONGO_EXPORT const bson *mongo_cursor_bson( mongo_cursor *cursor ) { - return (const bson *)&(cursor->current); -} - -MONGO_EXPORT int mongo_cursor_next( mongo_cursor *cursor ) { - char *next_object; - char *message_end; - - if( ! ( cursor->flags & MONGO_CURSOR_QUERY_SENT ) ) - if( mongo_cursor_op_query( cursor ) != MONGO_OK ) - return MONGO_ERROR; - - if( !cursor->reply ) - return MONGO_ERROR; - - /* no data */ - if ( cursor->reply->fields.num == 0 ) { - - /* Special case for tailable cursors. */ - if( cursor->reply->fields.cursorID ) { - if( ( mongo_cursor_get_more( cursor ) != MONGO_OK ) || - cursor->reply->fields.num == 0 ) { - return MONGO_ERROR; - } - } - - else - return MONGO_ERROR; - } - - /* first */ - if ( cursor->current.data == NULL ) { - bson_init_finished_data( &cursor->current, &cursor->reply->objs ); - return MONGO_OK; - } - - next_object = cursor->current.data + bson_size( &cursor->current ); - message_end = ( char * )cursor->reply + cursor->reply->head.len; - - if ( next_object >= message_end ) { - if( mongo_cursor_get_more( cursor ) != MONGO_OK ) - return MONGO_ERROR; - - /* If there's still a cursor id, then the message should be pending. */ - if( cursor->reply->fields.num == 0 && cursor->reply->fields.cursorID ) { - cursor->err = MONGO_CURSOR_PENDING; - return MONGO_ERROR; - } - - bson_init_finished_data( &cursor->current, &cursor->reply->objs ); - } else { - bson_init_finished_data( &cursor->current, next_object ); - } - - return MONGO_OK; -} - -MONGO_EXPORT int mongo_cursor_destroy( mongo_cursor *cursor ) { - int result = MONGO_OK; - - if ( !cursor ) return result; - - /* Kill cursor if live. */ - if ( cursor->reply && cursor->reply->fields.cursorID ) { - mongo *conn = cursor->conn; - mongo_message *mm = mongo_message_create( 16 /*header*/ - +4 /*ZERO*/ - +4 /*numCursors*/ - +8 /*cursorID*/ - , 0, 0, MONGO_OP_KILL_CURSORS ); - char *data = &mm->data; - data = mongo_data_append32( data, &ZERO ); - data = mongo_data_append32( data, &ONE ); - data = mongo_data_append64( data, &cursor->reply->fields.cursorID ); - - result = mongo_message_send( conn, mm ); - } - - bson_free( cursor->reply ); - bson_free( ( void * )cursor->ns ); - - if( cursor->flags & MONGO_CURSOR_MUST_FREE ) - bson_free( cursor ); - - return result; -} - -/* MongoDB Helper Functions */ - -MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, const bson *key, int options, bson *out ) { - bson b; - bson_iterator it; - char name[255] = {'_'}; - int i = 1; - char idxns[1024]; - - bson_iterator_init( &it, key ); - while( i < 255 && bson_iterator_next( &it ) ) { - strncpy( name + i, bson_iterator_key( &it ), 255 - i ); - i += strlen( bson_iterator_key( &it ) ); - } - name[254] = '\0'; - - bson_init( &b ); - bson_append_bson( &b, "key", key ); - bson_append_string( &b, "ns", ns ); - bson_append_string( &b, "name", name ); - if ( options & MONGO_INDEX_UNIQUE ) - bson_append_bool( &b, "unique", 1 ); - if ( options & MONGO_INDEX_DROP_DUPS ) - bson_append_bool( &b, "dropDups", 1 ); - if ( options & MONGO_INDEX_BACKGROUND ) - bson_append_bool( &b, "background", 1 ); - if ( options & MONGO_INDEX_SPARSE ) - bson_append_bool( &b, "sparse", 1 ); - bson_finish( &b ); - - strncpy( idxns, ns, 1024-16 ); - strcpy( strchr( idxns, '.' ), ".system.indexes" ); - mongo_insert( conn, idxns, &b, NULL ); - bson_destroy( &b ); - - *strchr( idxns, '.' ) = '\0'; /* just db not ns */ - return mongo_cmd_get_last_error( conn, idxns, out ); -} - -MONGO_EXPORT bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) { - bson b; - bson_bool_t success; - - bson_init( &b ); - bson_append_int( &b, field, 1 ); - bson_finish( &b ); - - success = mongo_create_index( conn, ns, &b, options, out ); - bson_destroy( &b ); - return success; -} - -MONGO_EXPORT int mongo_create_capped_collection( mongo *conn, const char *db, - const char *collection, int size, int max, bson *out ) { - - bson b; - int result; - - bson_init( &b ); - bson_append_string( &b, "create", collection ); - bson_append_bool( &b, "capped", 1 ); - bson_append_int( &b, "size", size ); - if( max > 0 ) - bson_append_int( &b, "max", size ); - bson_finish( &b ); - - result = mongo_run_command( conn, db, &b, out ); - - bson_destroy( &b ); - - return result; -} - -MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *ns, const bson *query ) { - bson cmd; - bson out = {NULL, 0}; - double count = -1; - - bson_init( &cmd ); - bson_append_string( &cmd, "count", ns ); - if ( query && bson_size( query ) > 5 ) /* not empty */ - bson_append_bson( &cmd, "query", query ); - bson_finish( &cmd ); - - if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) { - bson_iterator it; - if( bson_find( &it, &out, "n" ) ) - count = bson_iterator_double( &it ); - bson_destroy( &cmd ); - bson_destroy( &out ); - return count; - } else { - bson_destroy( &out ); - bson_destroy( &cmd ); - return MONGO_ERROR; - } -} - -MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db, const bson *command, - bson *out ) { - - bson response = {NULL, 0}; - bson fields; - int sl = strlen( db ); - char *ns = bson_malloc( sl + 5 + 1 ); /* ".$cmd" + nul */ - int res, success = 0; - - strcpy( ns, db ); - strcpy( ns+sl, ".$cmd" ); - - res = mongo_find_one( conn, ns, command, bson_empty( &fields ), &response ); - bson_free( ns ); - - if( res != MONGO_OK ) - return MONGO_ERROR; - else { - bson_iterator it; - if( bson_find( &it, &response, "ok" ) ) - success = bson_iterator_bool( &it ); - - if( !success ) { - conn->err = MONGO_COMMAND_FAILED; - return MONGO_ERROR; - } else { - if( out ) - *out = response; - return MONGO_OK; - } - } -} - -MONGO_EXPORT int mongo_simple_int_command( mongo *conn, const char *db, - const char *cmdstr, int arg, bson *realout ) { - - bson out = {NULL, 0}; - bson cmd; - int result; - - bson_init( &cmd ); - bson_append_int( &cmd, cmdstr, arg ); - bson_finish( &cmd ); - - result = mongo_run_command( conn, db, &cmd, &out ); - - bson_destroy( &cmd ); - - if ( realout ) - *realout = out; - else - bson_destroy( &out ); - - return result; -} - -MONGO_EXPORT int mongo_simple_str_command( mongo *conn, const char *db, - const char *cmdstr, const char *arg, bson *realout ) { - - bson out = {NULL, 0}; - int result; - - bson cmd; - bson_init( &cmd ); - bson_append_string( &cmd, cmdstr, arg ); - bson_finish( &cmd ); - - result = mongo_run_command( conn, db, &cmd, &out ); - - bson_destroy( &cmd ); - - if ( realout ) - *realout = out; - else - bson_destroy( &out ); - - return result; -} - -MONGO_EXPORT int mongo_cmd_drop_db( mongo *conn, const char *db ) { - return mongo_simple_int_command( conn, db, "dropDatabase", 1, NULL ); -} - -MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out ) { - return mongo_simple_str_command( conn, db, "drop", collection, out ); -} - -MONGO_EXPORT void mongo_cmd_reset_error( mongo *conn, const char *db ) { - mongo_simple_int_command( conn, db, "reseterror", 1, NULL ); -} - -static int mongo_cmd_get_error_helper( mongo *conn, const char *db, - bson *realout, const char *cmdtype ) { - - bson out = {NULL,0}; - bson_bool_t haserror = 0; - - /* Reset last error codes. */ - mongo_clear_errors( conn ); - - /* If there's an error, store its code and string in the connection object. */ - if( mongo_simple_int_command( conn, db, cmdtype, 1, &out ) == MONGO_OK ) { - bson_iterator it; - haserror = ( bson_find( &it, &out, "err" ) != BSON_NULL ); - if( haserror ) mongo_set_last_error( conn, &it, &out ); - } - - if( realout ) - *realout = out; /* transfer of ownership */ - else - bson_destroy( &out ); - - if( haserror ) - return MONGO_ERROR; - else - return MONGO_OK; -} - -MONGO_EXPORT int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ) { - return mongo_cmd_get_error_helper( conn, db, out, "getpreverror" ); -} - -MONGO_EXPORT int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ) { - return mongo_cmd_get_error_helper( conn, db, out, "getlasterror" ); -} - -MONGO_EXPORT bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *realout ) { - bson out = {NULL,0}; - bson_bool_t ismaster = 0; - - if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { - bson_iterator it; - bson_find( &it, &out, "ismaster" ); - ismaster = bson_iterator_bool( &it ); - } - - if( realout ) - *realout = out; /* transfer of ownership */ - else - bson_destroy( &out ); - - return ismaster; -} - -static void digest2hex( mongo_md5_byte_t digest[16], char hex_digest[33] ) { - static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - int i; - for ( i=0; i<16; i++ ) { - hex_digest[2*i] = hex[( digest[i] & 0xf0 ) >> 4]; - hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ]; - } - hex_digest[32] = '\0'; -} - -static void mongo_pass_digest( const char *user, const char *pass, char hex_digest[33] ) { - mongo_md5_state_t st; - mongo_md5_byte_t digest[16]; - - mongo_md5_init( &st ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )":mongo:", 7 ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )pass, strlen( pass ) ); - mongo_md5_finish( &st, digest ); - digest2hex( digest, hex_digest ); -} - -MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass ) { - bson user_obj; - bson pass_obj; - char hex_digest[33]; - char *ns = bson_malloc( strlen( db ) + strlen( ".system.users" ) + 1 ); - int res; - - strcpy( ns, db ); - strcpy( ns+strlen( db ), ".system.users" ); - - mongo_pass_digest( user, pass, hex_digest ); - - bson_init( &user_obj ); - bson_append_string( &user_obj, "user", user ); - bson_finish( &user_obj ); - - bson_init( &pass_obj ); - bson_append_start_object( &pass_obj, "$set" ); - bson_append_string( &pass_obj, "pwd", hex_digest ); - bson_append_finish_object( &pass_obj ); - bson_finish( &pass_obj ); - - res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT, NULL ); - - bson_free( ns ); - bson_destroy( &user_obj ); - bson_destroy( &pass_obj ); - - return res; -} - -MONGO_EXPORT bson_bool_t mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass ) { - bson from_db; - bson cmd; - bson out; - const char *nonce; - int result; - - mongo_md5_state_t st; - mongo_md5_byte_t digest[16]; - char hex_digest[33]; - - if( mongo_simple_int_command( conn, db, "getnonce", 1, &from_db ) == MONGO_OK ) { - bson_iterator it; - bson_find( &it, &from_db, "nonce" ); - nonce = bson_iterator_string( &it ); - } else { - return MONGO_ERROR; - } - - mongo_pass_digest( user, pass, hex_digest ); - - mongo_md5_init( &st ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )nonce, strlen( nonce ) ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) ); - mongo_md5_append( &st, ( const mongo_md5_byte_t * )hex_digest, 32 ); - mongo_md5_finish( &st, digest ); - digest2hex( digest, hex_digest ); - - bson_init( &cmd ); - bson_append_int( &cmd, "authenticate", 1 ); - bson_append_string( &cmd, "user", user ); - bson_append_string( &cmd, "nonce", nonce ); - bson_append_string( &cmd, "key", hex_digest ); - bson_finish( &cmd ); - - bson_destroy( &from_db ); - - result = mongo_run_command( conn, db, &cmd, &out ); - - bson_destroy( &from_db ); - bson_destroy( &cmd ); - - return result; -} diff --git a/mongo-c-driver-v0.6/src/mongo.h b/mongo-c-driver-v0.6/src/mongo.h deleted file mode 100644 index 57ba921..0000000 --- a/mongo-c-driver-v0.6/src/mongo.h +++ /dev/null @@ -1,824 +0,0 @@ -/** - * @file mongo.h - * @brief Main MongoDB Declarations - */ - -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MONGO_H_ -#define MONGO_H_ - -#include "bson.h" - -MONGO_EXTERN_C_START - -#define MONGO_MAJOR 0 -#define MONGO_MINOR 6 -#define MONGO_PATCH 0 - -#define MONGO_OK 0 -#define MONGO_ERROR -1 - -#define MONGO_DEFAULT_PORT 27017 - -#define MONGO_DEFAULT_MAX_BSON_SIZE 4 * 1024 * 1024 - -#define MONGO_ERR_LEN 128 - -typedef enum mongo_error_t { - MONGO_CONN_SUCCESS = 0, /**< Connection success! */ - MONGO_CONN_NO_SOCKET, /**< Could not create a socket. */ - MONGO_CONN_FAIL, /**< An error occured while calling connect(). */ - MONGO_CONN_ADDR_FAIL, /**< An error occured while calling getaddrinfo(). */ - MONGO_CONN_NOT_MASTER, /**< Warning: connected to a non-master node (read-only). */ - MONGO_CONN_BAD_SET_NAME, /**< Given rs name doesn't match this replica set. */ - MONGO_CONN_NO_PRIMARY, /**< Can't find primary in replica set. Connection closed. */ - - MONGO_IO_ERROR, /**< An error occurred while reading or writing on the socket. */ - MONGO_SOCKET_ERROR, /**< Other socket error. */ - MONGO_READ_SIZE_ERROR, /**< The response is not the expected length. */ - MONGO_COMMAND_FAILED, /**< The command returned with 'ok' value of 0. */ - MONGO_WRITE_ERROR, /**< Write with given write_concern returned an error. */ - MONGO_NS_INVALID, /**< The name for the ns (database or collection) is invalid. */ - MONGO_BSON_INVALID, /**< BSON not valid for the specified op. */ - MONGO_BSON_NOT_FINISHED, /**< BSON object has not been finished. */ - MONGO_BSON_TOO_LARGE, /**< BSON object exceeds max BSON size. */ - MONGO_WRITE_CONCERN_INVALID /**< Supplied write concern object is invalid. */ -} mongo_error_t; - -typedef enum mongo_cursor_error_t { - MONGO_CURSOR_EXHAUSTED, /**< The cursor has no more results. */ - MONGO_CURSOR_INVALID, /**< The cursor has timed out or is not recognized. */ - MONGO_CURSOR_PENDING, /**< Tailable cursor still alive but no data. */ - MONGO_CURSOR_QUERY_FAIL, /**< The server returned an '$err' object, indicating query failure. - See conn->lasterrcode and conn->lasterrstr for details. */ - MONGO_CURSOR_BSON_ERROR /**< Something is wrong with the BSON provided. See conn->err - for details. */ -} mongo_cursor_error_t; - -enum mongo_cursor_flags { - MONGO_CURSOR_MUST_FREE = 1, /**< mongo_cursor_destroy should free cursor. */ - MONGO_CURSOR_QUERY_SENT = ( 1<<1 ) /**< Initial query has been sent. */ -}; - -enum mongo_index_opts { - MONGO_INDEX_UNIQUE = ( 1<<0 ), - MONGO_INDEX_DROP_DUPS = ( 1<<2 ), - MONGO_INDEX_BACKGROUND = ( 1<<3 ), - MONGO_INDEX_SPARSE = ( 1<<4 ) -}; - -enum mongo_update_opts { - MONGO_UPDATE_UPSERT = 0x1, - MONGO_UPDATE_MULTI = 0x2, - MONGO_UPDATE_BASIC = 0x4 -}; - -enum mongo_insert_opts { - MONGO_CONTINUE_ON_ERROR = 0x1 -}; - -enum mongo_cursor_opts { - MONGO_TAILABLE = ( 1<<1 ), /**< Create a tailable cursor. */ - MONGO_SLAVE_OK = ( 1<<2 ), /**< Allow queries on a non-primary node. */ - MONGO_NO_CURSOR_TIMEOUT = ( 1<<4 ), /**< Disable cursor timeouts. */ - MONGO_AWAIT_DATA = ( 1<<5 ), /**< Momentarily block for more data. */ - MONGO_EXHAUST = ( 1<<6 ), /**< Stream in multiple 'more' packages. */ - MONGO_PARTIAL = ( 1<<7 ) /**< Allow reads even if a shard is down. */ -}; - -enum mongo_operations { - MONGO_OP_MSG = 1000, - MONGO_OP_UPDATE = 2001, - MONGO_OP_INSERT = 2002, - MONGO_OP_QUERY = 2004, - MONGO_OP_GET_MORE = 2005, - MONGO_OP_DELETE = 2006, - MONGO_OP_KILL_CURSORS = 2007 -}; - -#pragma pack(1) -typedef struct { - int len; - int id; - int responseTo; - int op; -} mongo_header; - -typedef struct { - mongo_header head; - char data; -} mongo_message; - -typedef struct { - int flag; /* FIX THIS COMMENT non-zero on failure */ - int64_t cursorID; - int start; - int num; -} mongo_reply_fields; - -typedef struct { - mongo_header head; - mongo_reply_fields fields; - char objs; -} mongo_reply; -#pragma pack() - -typedef struct mongo_host_port { - char host[255]; - int port; - struct mongo_host_port *next; -} mongo_host_port; - -typedef struct mongo_write_concern { - int w; /**< Number of nodes this write should be replicated to. */ - int wtimeout; /**< Number of milliseconds before replication timeout. */ - int j; /**< If non-zero, block until the journal sync. */ - int fsync; /**< Same a j with journaling enabled; otherwise, call fsync. */ - const char *mode; /**< Either "majority" or a getlasterrormode. Overrides w value. */ - - bson *cmd; /**< The BSON object representing the getlasterror command. */ -} mongo_write_concern; - -typedef struct { - mongo_host_port *seeds; /**< List of seeds provided by the user. */ - mongo_host_port *hosts; /**< List of host/ports given by the replica set */ - char *name; /**< Name of the replica set. */ - bson_bool_t primary_connected; /**< Primary node connection status. */ -} mongo_replset; - -typedef struct mongo { - mongo_host_port *primary; /**< Primary connection info. */ - mongo_replset *replset; /**< replset object if connected to a replica set. */ - int sock; /**< Socket file descriptor. */ - int flags; /**< Flags on this connection object. */ - int conn_timeout_ms; /**< Connection timeout in milliseconds. */ - int op_timeout_ms; /**< Read and write timeout in milliseconds. */ - int max_bson_size; /**< Largest BSON object allowed on this connection. */ - bson_bool_t connected; /**< Connection status. */ - mongo_write_concern *write_concern; /**< The default write concern. */ - - mongo_error_t err; /**< Most recent driver error code. */ - int errcode; /**< Most recent errno or WSAGetLastError(). */ - char errstr[MONGO_ERR_LEN]; /**< String version of error. */ - int lasterrcode; /**< getlasterror code from the server. */ - char lasterrstr[MONGO_ERR_LEN]; /**< getlasterror string from the server. */ -} mongo; - -typedef struct { - mongo_reply *reply; /**< reply is owned by cursor */ - mongo *conn; /**< connection is *not* owned by cursor */ - const char *ns; /**< owned by cursor */ - int flags; /**< Flags used internally by this drivers. */ - int seen; /**< Number returned so far. */ - bson current; /**< This cursor's current bson object. */ - mongo_cursor_error_t err; /**< Errors on this cursor. */ - const bson *query; /**< Bitfield containing cursor options. */ - const bson *fields;/**< Bitfield containing cursor options. */ - int options; /**< Bitfield containing cursor options. */ - int limit; /**< Bitfield containing cursor options. */ - int skip; /**< Bitfield containing cursor options. */ -} mongo_cursor; - -/********************************************************************* -Connection API -**********************************************************************/ - -/** Initialize sockets for Windows. - */ -MONGO_EXPORT void mongo_init_sockets(); - -/** - * Initialize a new mongo connection object. You must initialize each mongo - * object using this function. - * - * @note When finished, you must pass this object to - * mongo_destroy( ). - * - * @param conn a mongo connection object allocated on the stack - * or heap. - */ -MONGO_EXPORT void mongo_init( mongo *conn ); - -/** - * Connect to a single MongoDB server. - * - * @param conn a mongo object. - * @param host a numerical network address or a network hostname. - * @param port the port to connect to. - * - * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type - * mongo_error_t will be set on the conn->err field. - */ -MONGO_EXPORT int mongo_connect( mongo *conn , const char *host, int port ); - -/** - * Set up this connection object for connecting to a replica set. - * To connect, pass the object to mongo_replset_connect(). - * - * @param conn a mongo object. - * @param name the name of the replica set to connect to. - * */ -MONGO_EXPORT void mongo_replset_init( mongo *conn, const char *name ); - -/** - * Add a seed node to the replica set connection object. - * - * You must specify at least one seed node before connecting to a replica set. - * - * @param conn a mongo object. - * @param host a numerical network address or a network hostname. - * @param port the port to connect to. - */ -MONGO_EXPORT void mongo_replset_add_seed( mongo *conn, const char *host, int port ); - -/** - * Utility function for converting a host-port string to a mongo_host_port. - * - * @param host_string a string containing either a host or a host and port separated - * by a colon. - * @param host_port the mongo_host_port object to write the result to. - */ -void mongo_parse_host( const char *host_string, mongo_host_port *host_port ); - -/** - * Utility function for validation database and collection names. - * - * @param conn a mongo object. - * - * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type - * mongo_conn_return_t will be set on the conn->err field. - * - */ -MONGO_EXPORT int mongo_validate_ns( mongo *conn, const char *ns ); - -/** - * Connect to a replica set. - * - * Before passing a connection object to this function, you must already have called - * mongo_set_replset and mongo_replset_add_seed. - * - * @param conn a mongo object. - * - * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type - * mongo_conn_return_t will be set on the conn->err field. - */ -MONGO_EXPORT int mongo_replset_connect( mongo *conn ); - -/** Set a timeout for operations on this connection. This - * is a platform-specific feature, and only work on *nix - * system. You must also compile for linux to support this. - * - * @param conn a mongo object. - * @param millis timeout time in milliseconds. - * - * @return MONGO_OK. On error, return MONGO_ERROR and - * set the conn->err field. - */ -MONGO_EXPORT int mongo_set_op_timeout( mongo *conn, int millis ); - -/** - * Ensure that this connection is healthy by performing - * a round-trip to the server. - * - * @param conn a mongo connection - * - * @return MONGO_OK if connected; otherwise, MONGO_ERROR. - */ -MONGO_EXPORT int mongo_check_connection( mongo *conn ); - -/** - * Try reconnecting to the server using the existing connection settings. - * - * This function will disconnect the current socket. If you've authenticated, - * you'll need to re-authenticate after calling this function. - * - * @param conn a mongo object. - * - * @return MONGO_OK or MONGO_ERROR and - * set the conn->err field. - */ -MONGO_EXPORT int mongo_reconnect( mongo *conn ); - -/** - * Close the current connection to the server. After calling - * this function, you may call mongo_reconnect with the same - * connection object. - * - * @param conn a mongo object. - */ -MONGO_EXPORT void mongo_disconnect( mongo *conn ); - -/** - * Close any existing connection to the server and free all allocated - * memory associated with the conn object. - * - * You must always call this function when finished with the connection object. - * - * @param conn a mongo object. - */ -MONGO_EXPORT void mongo_destroy( mongo *conn ); - -/** - * Specify the write concern object that this connection should use - * by default for all writes (inserts, updates, and deletes). This value - * can be overridden by passing a write_concern object to any write function. - * - * @param conn a mongo object. - * @param write_concern pointer to a write concern object. - * - */ -MONGO_EXPORT void mongo_set_write_concern( mongo *conn, - mongo_write_concern *write_concern ); - - -/********************************************************************* -CRUD API -**********************************************************************/ - -/** - * Insert a BSON document into a MongoDB server. This function - * will fail if the supplied BSON struct is not UTF-8 or if - * the keys are invalid for insert (contain '.' or start with '$'). - * - * The default write concern set on the conn object will be used. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param data the bson data. - * @param custom_write_concern a write concern object that will - * override any write concern set on the conn object. - * - * @return MONGO_OK or MONGO_ERROR. If the conn->err - * field is MONGO_BSON_INVALID, check the err field - * on the bson struct for the reason. - */ -MONGO_EXPORT int mongo_insert( mongo *conn, const char *ns, const bson *data, - mongo_write_concern *custom_write_concern ); - -/** - * Insert a batch of BSON documents into a MongoDB server. This function - * will fail if any of the documents to be inserted is invalid. - * - * The default write concern set on the conn object will be used. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param data the bson data. - * @param num the number of documents in data. - * @param custom_write_concern a write concern object that will - * override any write concern set on the conn object. - * @param flags flags on this batch insert. Currently, this value - * may be 0 or MONGO_CONTINUE_ON_ERROR, which will cause the - * batch insert to continue even if a given insert in the batch fails. - * - * @return MONGO_OK or MONGO_ERROR. - * - */ -MONGO_EXPORT int mongo_insert_batch( mongo *conn, const char *ns, - const bson **data, int num, mongo_write_concern *custom_write_concern, - int flags ); - -/** - * Update a document in a MongoDB server. - * - * The default write concern set on the conn object will be used. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param cond the bson update query. - * @param op the bson update data. - * @param flags flags for the update. - * @param custom_write_concern a write concern object that will - * override any write concern set on the conn object. - * - * @return MONGO_OK or MONGO_ERROR with error stored in conn object. - * - */ -MONGO_EXPORT int mongo_update( mongo *conn, const char *ns, const bson *cond, - const bson *op, int flags, mongo_write_concern *custom_write_concern ); - -/** - * Remove a document from a MongoDB server. - * - * The default write concern set on the conn object will be used. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param cond the bson query. - * @param custom_write_concern a write concern object that will - * override any write concern set on the conn object. - * - * @return MONGO_OK or MONGO_ERROR with error stored in conn object. - */ -MONGO_EXPORT int mongo_remove( mongo *conn, const char *ns, const bson *cond, - mongo_write_concern *custom_write_concern ); - - -/********************************************************************* -Write Concern API -**********************************************************************/ - -/** - * Initialize a mongo_write_concern object. Effectively zeroes out the struct. - * - */ -MONGO_EXPORT void mongo_write_concern_init( mongo_write_concern *write_concern ); - -/** - * Finish this write concern object by serializing the literal getlasterror - * command that will be sent to the server. - * - * You must call mongo_write_concern_destroy() to free the serialized BSON. - * - */ -MONGO_EXPORT int mongo_write_concern_finish( mongo_write_concern *write_concern ); - -/** - * Free the write_concern object (specifically, the BSON that it owns). - * - */ -MONGO_EXPORT void mongo_write_concern_destroy( mongo_write_concern *write_concern ); - -/********************************************************************* -Cursor API -**********************************************************************/ - -/** - * Find documents in a MongoDB server. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param query the bson query. - * @param fields a bson document of fields to be returned. - * @param limit the maximum number of documents to retrun. - * @param skip the number of documents to skip. - * @param options A bitfield containing cursor options. - * - * @return A cursor object allocated on the heap or NULL if - * an error has occurred. For finer-grained error checking, - * use the cursor builder API instead. - */ -MONGO_EXPORT mongo_cursor *mongo_find( mongo *conn, const char *ns, const bson *query, - const bson *fields, int limit, int skip, int options ); - -/** - * Initalize a new cursor object. - * - * @param cursor - * @param ns the namespace, represented as the the database - * name and collection name separated by a dot. e.g., "test.users" - */ -MONGO_EXPORT void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ); - -/** - * Set the bson object specifying this cursor's query spec. If - * your query is the empty bson object "{}", then you need not - * set this value. - * - * @param cursor - * @param query a bson object representing the query spec. This may - * be either a simple query spec or a complex spec storing values for - * $query, $orderby, $hint, and/or $explain. See - * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details. - */ -MONGO_EXPORT void mongo_cursor_set_query( mongo_cursor *cursor, const bson *query ); - -/** - * Set the fields to return for this cursor. If you want to return - * all fields, you need not set this value. - * - * @param cursor - * @param fields a bson object representing the fields to return. - * See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields. - */ -MONGO_EXPORT void mongo_cursor_set_fields( mongo_cursor *cursor, const bson *fields ); - -/** - * Set the number of documents to skip. - * - * @param cursor - * @param skip - */ -MONGO_EXPORT void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ); - -/** - * Set the number of documents to return. - * - * @param cursor - * @param limit - */ -MONGO_EXPORT void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ); - -/** - * Set any of the available query options (e.g., MONGO_TAILABLE). - * - * @param cursor - * @param options a bitfield storing query options. See - * mongo_cursor_bitfield_t for available constants. - */ -MONGO_EXPORT void mongo_cursor_set_options( mongo_cursor *cursor, int options ); - -/** - * Return the current BSON object data as a const char*. This is useful - * for creating bson iterators with bson_iterator_init. - * - * @param cursor - */ -MONGO_EXPORT const char *mongo_cursor_data( mongo_cursor *cursor ); - -/** - * Return the current BSON object data as a const char*. This is useful - * for creating bson iterators with bson_iterator_init. - * - * @param cursor - */ -MONGO_EXPORT const bson *mongo_cursor_bson( mongo_cursor *cursor ); - -/** - * Iterate the cursor, returning the next item. When successful, - * the returned object will be stored in cursor->current; - * - * @param cursor - * - * @return MONGO_OK. On error, returns MONGO_ERROR and sets - * cursor->err with a value of mongo_error_t. - */ -MONGO_EXPORT int mongo_cursor_next( mongo_cursor *cursor ); - -/** - * Destroy a cursor object. When finished with a cursor, you - * must pass it to this function. - * - * @param cursor the cursor to destroy. - * - * @return MONGO_OK or an error code. On error, check cursor->conn->err - * for errors. - */ -MONGO_EXPORT int mongo_cursor_destroy( mongo_cursor *cursor ); - -/** - * Find a single document in a MongoDB server. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param query the bson query. - * @param fields a bson document of the fields to be returned. - * @param out a bson document in which to put the query result. - * - */ -/* out can be NULL if you don't care about results. useful for commands */ -MONGO_EXPORT int mongo_find_one( mongo *conn, const char *ns, const bson *query, - const bson *fields, bson *out ); - - -/********************************************************************* -Command API and Helpers -**********************************************************************/ - -/** - * Count the number of documents in a collection matching a query. - * - * @param conn a mongo object. - * @param db the db name. - * @param coll the collection name. - * @param query the BSON query. - * - * @return the number of matching documents. If the command fails, - * MONGO_ERROR is returned. - */ -MONGO_EXPORT double mongo_count( mongo *conn, const char *db, const char *coll, - const bson *query ); - -/** - * Create a compound index. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param data the bson index data. - * @param options a bitfield for setting index options. Possibilities include - * MONGO_INDEX_UNIQUE, MONGO_INDEX_DROP_DUPS, MONGO_INDEX_BACKGROUND, - * and MONGO_INDEX_SPARSE. - * @param out a bson document containing errors, if any. - * - * @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR. - */ -MONGO_EXPORT int mongo_create_index( mongo *conn, const char *ns, - const bson *key, int options, bson *out ); - -/** - * Create a capped collection. - * - * @param conn a mongo object. - * @param ns the namespace (e.g., "dbname.collectioname") - * @param size the size of the capped collection in bytes. - * @param max the max number of documents this collection is - * allowed to contain. If zero, this argument will be ignored - * and the server will use the collection's size to age document out. - * If using this option, ensure that the total size can contain this - * number of documents. - */ -MONGO_EXPORT int mongo_create_capped_collection( mongo *conn, const char *db, - const char *collection, int size, int max, bson *out ); - -/** - * Create an index with a single key. - * - * @param conn a mongo object. - * @param ns the namespace. - * @param field the index key. - * @param options index options. - * @param out a BSON document containing errors, if any. - * - * @return true if the index was created. - */ -MONGO_EXPORT bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, - const char *field, int options, bson *out ); - -/** - * Run a command on a MongoDB server. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param command the BSON command to run. - * @param out the BSON result of the command. - * - * @return MONGO_OK if the command ran without error. - */ -MONGO_EXPORT int mongo_run_command( mongo *conn, const char *db, - const bson *command, bson *out ); - -/** - * Run a command that accepts a simple string key and integer value. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param cmd the command to run. - * @param arg the integer argument to the command. - * @param out the BSON result of the command. - * - * @return MONGO_OK or an error code. - * - */ -MONGO_EXPORT int mongo_simple_int_command( mongo *conn, const char *db, - const char *cmd, int arg, bson *out ); - -/** - * Run a command that accepts a simple string key and value. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param cmd the command to run. - * @param arg the string argument to the command. - * @param out the BSON result of the command. - * - * @return true if the command ran without error. - * - */ -MONGO_EXPORT int mongo_simple_str_command( mongo *conn, const char *db, - const char *cmd, const char *arg, bson *out ); - -/** - * Drop a database. - * - * @param conn a mongo object. - * @param db the name of the database to drop. - * - * @return MONGO_OK or an error code. - */ -MONGO_EXPORT int mongo_cmd_drop_db( mongo *conn, const char *db ); - -/** - * Drop a collection. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param collection the name of the collection to drop. - * @param out a BSON document containing the result of the command. - * - * @return true if the collection drop was successful. - */ -MONGO_EXPORT int mongo_cmd_drop_collection( mongo *conn, const char *db, - const char *collection, bson *out ); - -/** - * Add a database user. - * - * @param conn a mongo object. - * @param db the database in which to add the user. - * @param user the user name - * @param pass the user password - * - * @return MONGO_OK or MONGO_ERROR. - */ -MONGO_EXPORT int mongo_cmd_add_user( mongo *conn, const char *db, - const char *user, const char *pass ); - -/** - * Authenticate a user. - * - * @param conn a mongo object. - * @param db the database to authenticate against. - * @param user the user name to authenticate. - * @param pass the user's password. - * - * @return MONGO_OK on sucess and MONGO_ERROR on failure. - */ -MONGO_EXPORT int mongo_cmd_authenticate( mongo *conn, const char *db, - const char *user, const char *pass ); - -/** - * Check if the current server is a master. - * - * @param conn a mongo object. - * @param out a BSON result of the command. - * - * @return true if the server is a master. - */ -/* return value is master status */ -MONGO_EXPORT bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *out ); - -/** - * Get the error for the last command with the current connection. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param out a BSON object containing the error details. - * - * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values - * of conn->lasterrcode and conn->lasterrstr for the error status. - */ -MONGO_EXPORT int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ); - -/** - * Get the most recent error with the current connection. - * - * @param conn a mongo object. - * @param db the name of the database. - * @param out a BSON object containing the error details. - * - * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values - * of conn->lasterrcode and conn->lasterrstr for the error status. - */ -MONGO_EXPORT int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ); - -/** - * Reset the error state for the connection. - * - * @param conn a mongo object. - * @param db the name of the database. - */ -MONGO_EXPORT void mongo_cmd_reset_error( mongo *conn, const char *db ); - - -/********************************************************************* -Utility API -**********************************************************************/ - -MONGO_EXPORT mongo* mongo_create(); -MONGO_EXPORT void mongo_dispose(mongo* conn); -MONGO_EXPORT int mongo_get_err(mongo* conn); -MONGO_EXPORT int mongo_is_connected(mongo* conn); -MONGO_EXPORT int mongo_get_op_timeout(mongo* conn); -MONGO_EXPORT const char* mongo_get_primary(mongo* conn); -MONGO_EXPORT int mongo_get_socket(mongo* conn) ; -MONGO_EXPORT int mongo_get_host_count(mongo* conn); -MONGO_EXPORT const char* mongo_get_host(mongo* conn, int i); -MONGO_EXPORT mongo_cursor* mongo_cursor_create(); -MONGO_EXPORT void mongo_cursor_dispose(mongo_cursor* cursor); -MONGO_EXPORT int mongo_get_server_err(mongo* conn); -MONGO_EXPORT const char* mongo_get_server_err_string(mongo* conn); - -/** - * Set an error on a mongo connection object. Mostly for internal use. - * - * @param conn a mongo connection object. - * @param err a driver error code of mongo_error_t. - * @param errstr a string version of the error. - * @param errorcode Currently errno or WSAGetLastError(). - */ -MONGO_EXPORT void __mongo_set_error( mongo *conn, mongo_error_t err, - const char *errstr, int errorcode ); -/** - * Clear all errors stored on a mongo connection object. - * - * @param conn a mongo connection object. - */ -MONGO_EXPORT void mongo_clear_errors( mongo *conn ); - -MONGO_EXTERN_C_END - -#endif diff --git a/mongo-c-driver-v0.6/src/numbers.c b/mongo-c-driver-v0.6/src/numbers.c deleted file mode 100644 index b3032d5..0000000 --- a/mongo-c-driver-v0.6/src/numbers.c +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright 2009-2012 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* all the numbers that fit in a 4 byte string */ -const char bson_numstrs[1000][4] = { - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", - "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", - "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", - "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", - "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", - "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", - "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", - "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", - "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", - "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", - - "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", - "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", - "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", - "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", - "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", - "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", - "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", - "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", - "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", - "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", - - "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", - "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", - "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", - "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", - "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", - "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", - "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", - "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", - "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", - "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", - - "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", - "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", - "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", - "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", - "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", - "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", - "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", - "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", - "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", - "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", - - "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", - "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", - "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", - "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", - "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", - "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", - "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", - "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", - "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", - "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", - - "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", - "510", "511", "512", "513", "514", "515", "516", "517", "518", "519", - "520", "521", "522", "523", "524", "525", "526", "527", "528", "529", - "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", - "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", - "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", - "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", - "570", "571", "572", "573", "574", "575", "576", "577", "578", "579", - "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", - "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", - - "600", "601", "602", "603", "604", "605", "606", "607", "608", "609", - "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", - "620", "621", "622", "623", "624", "625", "626", "627", "628", "629", - "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", - "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", - "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", - "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", - "670", "671", "672", "673", "674", "675", "676", "677", "678", "679", - "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", - "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", - - "700", "701", "702", "703", "704", "705", "706", "707", "708", "709", - "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", - "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", - "730", "731", "732", "733", "734", "735", "736", "737", "738", "739", - "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", - "750", "751", "752", "753", "754", "755", "756", "757", "758", "759", - "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", - "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", - "780", "781", "782", "783", "784", "785", "786", "787", "788", "789", - "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", - - "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", - "810", "811", "812", "813", "814", "815", "816", "817", "818", "819", - "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", - "830", "831", "832", "833", "834", "835", "836", "837", "838", "839", - "840", "841", "842", "843", "844", "845", "846", "847", "848", "849", - "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", - "860", "861", "862", "863", "864", "865", "866", "867", "868", "869", - "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", - "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", - "890", "891", "892", "893", "894", "895", "896", "897", "898", "899", - - "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", - "910", "911", "912", "913", "914", "915", "916", "917", "918", "919", - "920", "921", "922", "923", "924", "925", "926", "927", "928", "929", - "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", - "940", "941", "942", "943", "944", "945", "946", "947", "948", "949", - "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", - "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", - "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", - "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", - "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", -}; diff --git a/mongo-c-driver-v0.6/test/auth_test.c b/mongo-c-driver-v0.6/test/auth_test.c deleted file mode 100644 index e356850..0000000 --- a/mongo-c-driver-v0.6/test/auth_test.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "test.h" -#include "mongo.h" -#include -#include -#include - -static const char *db = "test"; - -int main() { - - mongo conn[1]; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - mongo_cmd_drop_db( conn, db ); - - ASSERT( mongo_cmd_authenticate( conn, db, "user", "password" ) == MONGO_ERROR ); - mongo_cmd_add_user( conn, db, "user", "password" ); - ASSERT( mongo_cmd_authenticate( conn, db, "user", "password" ) == MONGO_OK ); - - mongo_cmd_drop_db( conn, db ); - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/benchmark_test.c b/mongo-c-driver-v0.6/test/benchmark_test.c deleted file mode 100644 index e83268a..0000000 --- a/mongo-c-driver-v0.6/test/benchmark_test.c +++ /dev/null @@ -1,454 +0,0 @@ -/* test.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -/* supports preprocessor concatenation */ -#define DB "benchmarks" - -/* finds without indexes */ -#define DO_SLOW_TESTS 1 - -#ifndef TEST_SERVER -#define TEST_SERVER "127.0.0.1" -#endif - -#define PER_TRIAL 5000 -#define BATCH_SIZE 100 - -static mongo conn[1]; - -static void make_small( bson *out, int i ) { - bson_init( out ); - bson_append_new_oid( out, "_id" ); - bson_append_int( out, "x", i ); - bson_finish( out ); -} - -static void make_medium( bson *out, int i ) { - bson_init( out ); - bson_append_new_oid( out, "_id" ); - bson_append_int( out, "x", i ); - bson_append_int( out, "integer", 5 ); - bson_append_double( out, "number", 5.05 ); - bson_append_bool( out, "boolean", 0 ); - - bson_append_start_array( out, "array" ); - bson_append_string( out, "0", "test" ); - bson_append_string( out, "1", "benchmark" ); - bson_append_finish_object( out ); - - bson_finish( out ); -} - -static const char *words[14] = { - "10gen","web","open","source","application","paas", - "platform-as-a-service","technology","helps", - "developers","focus","building","mongodb","mongo" -}; - -static void make_large( bson *out, int i ) { - int num; - char numstr[4]; - bson_init( out ); - - bson_append_new_oid( out, "_id" ); - bson_append_int( out, "x", i ); - bson_append_string( out, "base_url", "http://www.example.com/test-me" ); - bson_append_int( out, "total_word_count", 6743 ); - bson_append_int( out, "access_time", 999 ); /*TODO use date*/ - - bson_append_start_object( out, "meta_tags" ); - bson_append_string( out, "description", "i am a long description string" ); - bson_append_string( out, "author", "Holly Man" ); - bson_append_string( out, "dynamically_created_meta_tag", "who know\n what" ); - bson_append_finish_object( out ); - - bson_append_start_object( out, "page_structure" ); - bson_append_int( out, "counted_tags", 3450 ); - bson_append_int( out, "no_of_js_attached", 10 ); - bson_append_int( out, "no_of_images", 6 ); - bson_append_finish_object( out ); - - - bson_append_start_array( out, "harvested_words" ); - for ( num=0; num < 14*20; num++ ) { - bson_numstr( numstr, num ); - bson_append_string( out, numstr, words[num%14] ); - } - bson_append_finish_object( out ); - - bson_finish( out ); -} - -static void serialize_small_test() { - int i; - bson b; - for ( i=0; i -#include -#include - -int main() { - bson_iterator it[1], it2[1]; - bson b[1]; - bson sub[1]; - bson copy[1]; - bson_type type; - - bson_init( b ); - bson_append_string( b, "foo", "hello" ); - - { - bson_append_start_object( b, "o" ); - bson_append_string( b, "bar", "goodbye" ); - bson_append_finish_object( b ); - } - - bson_iterator_init( it, b ); - - bson_iterator_next( it ); - type = bson_iterator_next( it ); - - ASSERT( BSON_OBJECT == type ); - - bson_iterator_subobject( it, sub ); - ASSERT( sub->finished == 1 ); - - bson_iterator_init( it2, sub ); - - type = bson_iterator_next( it2 ); - ASSERT( BSON_STRING == type ); - type = bson_iterator_next( it2 ); - ASSERT( BSON_EOO == type ); - - bson_copy( copy, sub ); - - ASSERT( 1 == copy->finished ); - ASSERT( 0 == copy->stackPos ); - ASSERT( 0 == copy->err ); - - bson_destroy( b ); - - return 0; -} - diff --git a/mongo-c-driver-v0.6/test/bson_test.c b/mongo-c-driver-v0.6/test/bson_test.c deleted file mode 100644 index a75276e..0000000 --- a/mongo-c-driver-v0.6/test/bson_test.c +++ /dev/null @@ -1,274 +0,0 @@ -#include "test.h" -#include "bson.h" -#include -#include -#include - -int test_bson_generic() { - - bson_iterator it, it2, it3; - bson_oid_t oid; - bson_timestamp_t ts; - bson_timestamp_t ts_result; - bson b[1]; - bson copy[1]; - bson scope[1]; - - ts.i = 1; - ts.t = 2; - - bson_init( b ); - bson_append_double( b, "d", 3.14 ); - bson_append_string( b, "s", "hello" ); - bson_append_string_n( b, "s_n", "goodbye cruel world", 7 ); - - { - bson_append_start_object( b, "o" ); - bson_append_start_array( b, "a" ); - bson_append_binary( b, "0", 8, "w\0rld", 5 ); - bson_append_finish_object( b ); - bson_append_finish_object( b ); - } - - bson_append_undefined( b, "u" ); - - bson_oid_from_string( &oid, "010203040506070809101112" ); - ASSERT( !memcmp( oid.bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) ); - bson_append_oid( b, "oid", &oid ); - - bson_append_bool( b, "b", 1 ); - bson_append_date( b, "date", 0x0102030405060708 ); - bson_append_null( b, "n" ); - bson_append_regex( b, "r", "^asdf", "imx" ); - /* no dbref test (deprecated) */ - bson_append_code( b, "c", "function(){}" ); - bson_append_code_n( b, "c_n", "function(){}garbage", 12 ); - bson_append_symbol( b, "symbol", "symbol" ); - bson_append_symbol_n( b, "symbol_n", "symbol and garbage", 6 ); - - { - bson_init( scope ); - bson_append_int( scope, "i", 123 ); - bson_finish( scope ); - - bson_append_code_w_scope( b, "cws", "function(){return i}", scope ); - bson_destroy( scope ); - } - - bson_append_timestamp( b, "timestamp", &ts ); - bson_append_long( b, "l", 0x1122334455667788 ); - - /* Ensure that we can't copy a non-finished object. */ - ASSERT( bson_copy( copy, b ) == BSON_ERROR ); - - bson_finish( b ); - - ASSERT( b->err == BSON_VALID ); - - /* Test append after finish. */ - ASSERT( bson_append_string( b, "foo", "bar" ) == BSON_ERROR ); - ASSERT( b->err & BSON_ALREADY_FINISHED ); - - ASSERT( bson_copy( copy, b ) == BSON_OK ); - - ASSERT( 1 == copy->finished ); - ASSERT( 0 == copy->err ); - - bson_print( b ); - - bson_iterator_init( &it, b ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_DOUBLE ); - ASSERT( bson_iterator_type( &it ) == BSON_DOUBLE ); - ASSERT( !strcmp( bson_iterator_key( &it ), "d" ) ); - ASSERT( bson_iterator_double( &it ) == 3.14 ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_STRING ); - ASSERT( bson_iterator_type( &it ) == BSON_STRING ); - ASSERT( !strcmp( bson_iterator_key( &it ), "s" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "hello" ) ); - ASSERT( strcmp( bson_iterator_string( &it ), "" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_STRING ); - ASSERT( bson_iterator_type( &it ) == BSON_STRING ); - ASSERT( !strcmp( bson_iterator_key( &it ), "s_n" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "goodbye" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_OBJECT ); - ASSERT( bson_iterator_type( &it ) == BSON_OBJECT ); - ASSERT( !strcmp( bson_iterator_key( &it ), "o" ) ); - bson_iterator_subiterator( &it, &it2 ); - - ASSERT( bson_iterator_more( &it2 ) ); - ASSERT( bson_iterator_next( &it2 ) == BSON_ARRAY ); - ASSERT( bson_iterator_type( &it2 ) == BSON_ARRAY ); - ASSERT( !strcmp( bson_iterator_key( &it2 ), "a" ) ); - bson_iterator_subiterator( &it2, &it3 ); - - ASSERT( bson_iterator_more( &it3 ) ); - ASSERT( bson_iterator_next( &it3 ) == BSON_BINDATA ); - ASSERT( bson_iterator_type( &it3 ) == BSON_BINDATA ); - ASSERT( !strcmp( bson_iterator_key( &it3 ), "0" ) ); - ASSERT( bson_iterator_bin_type( &it3 ) == 8 ); - ASSERT( bson_iterator_bin_len( &it3 ) == 5 ); - ASSERT( !memcmp( bson_iterator_bin_data( &it3 ), "w\0rld", 5 ) ); - - ASSERT( bson_iterator_more( &it3 ) ); - ASSERT( bson_iterator_next( &it3 ) == BSON_EOO ); - ASSERT( bson_iterator_type( &it3 ) == BSON_EOO ); - ASSERT( !bson_iterator_more( &it3 ) ); - - ASSERT( bson_iterator_more( &it2 ) ); - ASSERT( bson_iterator_next( &it2 ) == BSON_EOO ); - ASSERT( bson_iterator_type( &it2 ) == BSON_EOO ); - ASSERT( !bson_iterator_more( &it2 ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_UNDEFINED ); - ASSERT( bson_iterator_type( &it ) == BSON_UNDEFINED ); - ASSERT( !strcmp( bson_iterator_key( &it ), "u" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_OID ); - ASSERT( bson_iterator_type( &it ) == BSON_OID ); - ASSERT( !strcmp( bson_iterator_key( &it ), "oid" ) ); - ASSERT( !memcmp( bson_iterator_oid( &it )->bytes, "\x001\x002\x003\x004\x005\x006\x007\x008\x009\x010\x011\x012", 12 ) ); - ASSERT( bson_iterator_oid( &it )->ints[0] == oid.ints[0] ); - ASSERT( bson_iterator_oid( &it )->ints[1] == oid.ints[1] ); - ASSERT( bson_iterator_oid( &it )->ints[2] == oid.ints[2] ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_BOOL ); - ASSERT( bson_iterator_type( &it ) == BSON_BOOL ); - ASSERT( !strcmp( bson_iterator_key( &it ), "b" ) ); - ASSERT( bson_iterator_bool( &it ) == 1 ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_DATE ); - ASSERT( bson_iterator_type( &it ) == BSON_DATE ); - ASSERT( !strcmp( bson_iterator_key( &it ), "date" ) ); - ASSERT( bson_iterator_date( &it ) == 0x0102030405060708 ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_NULL ); - ASSERT( bson_iterator_type( &it ) == BSON_NULL ); - ASSERT( !strcmp( bson_iterator_key( &it ), "n" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_REGEX ); - ASSERT( bson_iterator_type( &it ) == BSON_REGEX ); - ASSERT( !strcmp( bson_iterator_key( &it ), "r" ) ); - ASSERT( !strcmp( bson_iterator_regex( &it ), "^asdf" ) ); - ASSERT( !strcmp( bson_iterator_regex_opts( &it ), "imx" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_CODE ); - ASSERT( bson_iterator_type( &it ) == BSON_CODE ); - ASSERT( !strcmp( bson_iterator_code(&it), "function(){}") ); - ASSERT( !strcmp( bson_iterator_key( &it ), "c" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_CODE ); - ASSERT( bson_iterator_type( &it ) == BSON_CODE ); - ASSERT( !strcmp( bson_iterator_key( &it ), "c_n" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "" ) ); - ASSERT( !strcmp( bson_iterator_code( &it ), "function(){}" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL ); - ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL ); - ASSERT( !strcmp( bson_iterator_key( &it ), "symbol" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_SYMBOL ); - ASSERT( bson_iterator_type( &it ) == BSON_SYMBOL ); - ASSERT( !strcmp( bson_iterator_key( &it ), "symbol_n" ) ); - ASSERT( !strcmp( bson_iterator_string( &it ), "symbol" ) ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_CODEWSCOPE ); - ASSERT( bson_iterator_type( &it ) == BSON_CODEWSCOPE ); - ASSERT( !strcmp( bson_iterator_key( &it ), "cws" ) ); - ASSERT( !strcmp( bson_iterator_code( &it ), "function(){return i}" ) ); - - { - bson scope; - bson_iterator_code_scope( &it, &scope ); - bson_iterator_init( &it2, &scope ); - - ASSERT( bson_iterator_more( &it2 ) ); - ASSERT( bson_iterator_next( &it2 ) == BSON_INT ); - ASSERT( bson_iterator_type( &it2 ) == BSON_INT ); - ASSERT( !strcmp( bson_iterator_key( &it2 ), "i" ) ); - ASSERT( bson_iterator_int( &it2 ) == 123 ); - - ASSERT( bson_iterator_more( &it2 ) ); - ASSERT( bson_iterator_next( &it2 ) == BSON_EOO ); - ASSERT( bson_iterator_type( &it2 ) == BSON_EOO ); - ASSERT( !bson_iterator_more( &it2 ) ); - } - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_TIMESTAMP ); - ASSERT( bson_iterator_type( &it ) == BSON_TIMESTAMP ); - ASSERT( !strcmp( bson_iterator_key( &it ), "timestamp" ) ); - ts_result = bson_iterator_timestamp( &it ); - ASSERT( ts_result.i == 1 ); - ASSERT( ts_result.t == 2 ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_LONG ); - ASSERT( bson_iterator_type( &it ) == BSON_LONG ); - ASSERT( !strcmp( bson_iterator_key( &it ), "l" ) ); - ASSERT( bson_iterator_long( &it ) == 0x1122334455667788 ); - - ASSERT( bson_iterator_more( &it ) ); - ASSERT( bson_iterator_next( &it ) == BSON_EOO ); - ASSERT( bson_iterator_type( &it ) == BSON_EOO ); - ASSERT( !bson_iterator_more( &it ) ); - - bson_destroy( b ); - - return 0; -} - -int test_bson_iterator() { - bson b[1]; - bson_iterator i[1]; - - bson_iterator_init( i, bson_empty( b ) ); - bson_iterator_next( i ); - bson_iterator_type( i ); - - bson_find( i, bson_empty( b ), "foo" ); - - return 0; -} - -int test_bson_size( void ) { - bson bsmall[1]; - - bson_init( bsmall ); - bson_append_int( bsmall, "a", 1 ); - bson_finish( bsmall ); - - ASSERT( bson_size( bsmall ) == 12 ); - - return 0; -} - -int main() { - - test_bson_generic(); - test_bson_iterator(); - test_bson_size(); - - return 0; -} - diff --git a/mongo-c-driver-v0.6/test/commands_test.c b/mongo-c-driver-v0.6/test/commands_test.c deleted file mode 100644 index 75a894e..0000000 --- a/mongo-c-driver-v0.6/test/commands_test.c +++ /dev/null @@ -1,44 +0,0 @@ -/* commands_test.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include - -int main() { - mongo conn[1]; - bson cmd[1]; - bson out[1]; - bson_iterator it[1]; - - const char *db = "test"; - const char *col = "c.capped"; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER , 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - mongo_cmd_drop_collection( conn, db, col, NULL ); - - ASSERT( mongo_create_capped_collection( conn, db, col, - 1024, 100, NULL ) == MONGO_OK ); - - bson_init( cmd ); - bson_append_string( cmd, "collstats", col ); - bson_finish( cmd ); - - ASSERT( mongo_run_command( conn, db, cmd, out ) == MONGO_OK ); - - ASSERT( bson_find( it, out, "capped" ) == BSON_INT ); - ASSERT( bson_find( it, out, "max" ) == BSON_INT ); - - mongo_cmd_drop_collection( conn, "test", col, NULL ); - mongo_cmd_drop_db( conn, db ); - - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/count_delete_test.c b/mongo-c-driver-v0.6/test/count_delete_test.c deleted file mode 100644 index 3200947..0000000 --- a/mongo-c-driver-v0.6/test/count_delete_test.c +++ /dev/null @@ -1,64 +0,0 @@ -/* count_delete.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include - -int main() { - mongo conn[1]; - bson b; - int i; - - const char *db = "test"; - const char *col = "c.simple"; - const char *ns = "test.c.simple"; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER , 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - /* if the collection doesn't exist dropping it will fail */ - if ( !mongo_cmd_drop_collection( conn, "test", col, NULL ) - && mongo_count( conn, db, col, NULL ) != 0 ) { - printf( "failed to drop collection\n" ); - exit( 1 ); - } - - for( i=0; i< 5; i++ ) { - bson_init( &b ); - - bson_append_new_oid( &b, "_id" ); - bson_append_int( &b , "a" , i+1 ); /* 1 to 5 */ - bson_finish( &b ); - - mongo_insert( conn , ns , &b, NULL ); - bson_destroy( &b ); - } - - /* query: {a: {$gt: 3}} */ - bson_init( &b ); - { - bson_append_start_object( &b, "a" ); - bson_append_int( &b, "$gt", 3 ); - bson_append_finish_object( &b ); - } - bson_finish( &b ); - - ASSERT( mongo_count( conn, db, col, NULL ) == 5 ); - ASSERT( mongo_count( conn, db, col, &b ) == 2 ); - - mongo_remove( conn, ns, &b, NULL ); - - ASSERT( mongo_count( conn, db, col, NULL ) == 3 ); - ASSERT( mongo_count( conn, db, col, &b ) == 0 ); - - bson_destroy( &b ); - mongo_cmd_drop_db( conn, db ); - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/cpptest.cpp b/mongo-c-driver-v0.6/test/cpptest.cpp deleted file mode 100644 index 1b5000f..0000000 --- a/mongo-c-driver-v0.6/test/cpptest.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "mongo.h" -#include "test.h" -#include -#include -#include - -// this is just a simple test to make sure everything works when compiled with a c++ compiler - -using namespace std; - -int main(){ - mongo conn[1]; - bson b; - - INIT_SOCKETS_FOR_WINDOWS; - - if (mongo_connect( conn, TEST_SERVER, 27017 )){ - cout << "failed to connect" << endl; - return 1; - } - - for(int i=0; i< 5; i++){ - bson_init( &b ); - - bson_append_new_oid( &b, "_id" ); - bson_append_double( &b , "a" , 17 ); - bson_append_int( &b , "b" , 17 ); - bson_append_string( &b , "c" , "17" ); - - { - bson_append_start_object( &b , "d" ); - bson_append_int( &b, "i", 71 ); - bson_append_finish_object( &b ); - } - { - bson_append_start_array( &b , "e" ); - bson_append_int( &b, "0", 71 ); - bson_append_string( &b, "1", "71" ); - bson_append_finish_object( &b ); - } - - bson_finish(&b); - bson_destroy(&b); - } - - mongo_destroy( conn ); - - return 0; -} - diff --git a/mongo-c-driver-v0.6/test/cursors_test.c b/mongo-c-driver-v0.6/test/cursors_test.c deleted file mode 100644 index 048d06a..0000000 --- a/mongo-c-driver-v0.6/test/cursors_test.c +++ /dev/null @@ -1,204 +0,0 @@ -/* cursors.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include -#include - -void create_capped_collection( mongo *conn ) { - bson b; - - bson_init( &b ); - bson_append_string( &b, "create", "cursors" ); - bson_append_bool( &b, "capped", 1 ); - bson_append_int( &b, "size", 1000000 ); - bson_finish( &b ); - - ASSERT( mongo_run_command( conn, "test", &b, NULL ) == MONGO_OK ); - - bson_destroy( &b ); -} - -void insert_sample_data( mongo *conn, int n ) { - bson b; - int i; - - for( i=0; ierr == MONGO_CURSOR_EXHAUSTED ); - - mongo_cursor_destroy( cursor ); - remove_sample_data( conn ); - return 0; -} - -int test_tailable( mongo *conn ) { - mongo_cursor *cursor; - bson b, e; - int count; - - remove_sample_data( conn ); - create_capped_collection( conn ); - insert_sample_data( conn, 10000 ); - - bson_init( &b ); - bson_append_start_object( &b, "$query" ); - bson_append_finish_object( &b ); - bson_append_start_object( &b, "$sort" ); - bson_append_int( &b, "$natural", -1 ); - bson_append_finish_object( &b ); - bson_finish( &b ); - - cursor = mongo_find( conn, "test.cursors", &b, bson_empty( &e ), 0, 0, MONGO_TAILABLE ); - bson_destroy( &b ); - - count = 0; - while( mongo_cursor_next( cursor ) == MONGO_OK ) - count++; - - ASSERT( count == 10000 ); - - ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR ); - ASSERT( cursor->err == MONGO_CURSOR_PENDING ); - - insert_sample_data( conn, 10 ); - - count = 0; - while( mongo_cursor_next( cursor ) == MONGO_OK ) { - count++; - } - - ASSERT( count == 10 ); - - ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR ); - ASSERT( cursor->err == MONGO_CURSOR_PENDING ); - - mongo_cursor_destroy( cursor ); - remove_sample_data( conn ); - - return 0; -} - -int test_builder_api( mongo *conn ) { - int count = 0; - mongo_cursor cursor[1]; - - remove_sample_data( conn ); - insert_sample_data( conn, 10000 ); - mongo_cursor_init( cursor, conn, "test.cursors" ); - - while( mongo_cursor_next( cursor ) == MONGO_OK ) { - count++; - } - ASSERT( count == 10000 ); - - mongo_cursor_destroy( cursor ); - - mongo_cursor_init( cursor, conn, "test.cursors" ); - mongo_cursor_set_limit( cursor, 10 ); - count = 0; - while( mongo_cursor_next( cursor ) == MONGO_OK ) { - count++; - } - ASSERT( count == 10 ); - mongo_cursor_destroy( cursor ); - - return 0; -} - -int test_bad_query( mongo *conn ) { - mongo_cursor cursor[1]; - bson b[1]; - - bson_init( b ); - bson_append_start_object( b, "foo" ); - bson_append_int( b, "$bad", 1 ); - bson_append_finish_object( b ); - bson_finish( b ); - - mongo_cursor_init( cursor, conn, "test.cursors" ); - mongo_cursor_set_query( cursor, b ); - - ASSERT( mongo_cursor_next( cursor ) == MONGO_ERROR ); - ASSERT( cursor->err == MONGO_CURSOR_QUERY_FAIL ); - ASSERT( cursor->conn->lasterrcode == 10068 ); - ASSERT( strlen( cursor->conn->lasterrstr ) > 0 ); - - mongo_cursor_destroy( cursor ); - bson_destroy( b ); - return 0; -} - -int test_copy_cursor_data( mongo *conn ) { - mongo_cursor cursor[1]; - bson b[1]; - - insert_sample_data( conn, 10 ); - mongo_cursor_init( cursor, conn, "test.cursors" ); - - mongo_cursor_next( cursor ); - - ASSERT( bson_copy( b, mongo_cursor_bson( cursor ) ) == MONGO_OK ); - - ASSERT( memcmp( (void *)b->data, (void *)(cursor->current).data, - bson_size( &cursor->current ) ) == 0 ); - - mongo_cursor_destroy( cursor ); - bson_destroy( b ); - - return 0; -} - -int main() { - - mongo conn[1]; - - INIT_SOCKETS_FOR_WINDOWS; - - if( mongo_connect( conn, TEST_SERVER, 27017 ) != MONGO_OK ) { - printf( "Failed to connect" ); - exit( 1 ); - } - - test_multiple_getmore( conn ); - test_tailable( conn ); - test_builder_api( conn ); - test_bad_query( conn ); - test_copy_cursor_data( conn ); - - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/endian_swap_test.c b/mongo-c-driver-v0.6/test/endian_swap_test.c deleted file mode 100644 index a39fe80..0000000 --- a/mongo-c-driver-v0.6/test/endian_swap_test.c +++ /dev/null @@ -1,31 +0,0 @@ -/* endian_swap.c */ - -#include "test.h" -#include "bson.h" -#include - -int main() { - int small = 0x00112233; - int64_t big = 0x0011223344556677; - double d = 1.2345; - - int small_swap; - int64_t big_swap; - int64_t d_swap; - - bson_swap_endian32( &small_swap, &small ); - ASSERT( small_swap == 0x33221100 ); - bson_swap_endian32( &small, &small_swap ); - ASSERT( small == 0x00112233 ); - - bson_swap_endian64( &big_swap, &big ); - ASSERT( big_swap == 0x7766554433221100 ); - bson_swap_endian64( &big, &big_swap ); - ASSERT( big == 0x0011223344556677 ); - - bson_swap_endian64( &d_swap, &d ); - bson_swap_endian64( &d, &d_swap ); - ASSERT( d == 1.2345 ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/env_posix_test.c b/mongo-c-driver-v0.6/test/env_posix_test.c deleted file mode 100644 index 93d76a2..0000000 --- a/mongo-c-driver-v0.6/test/env_posix_test.c +++ /dev/null @@ -1,109 +0,0 @@ -/* env_posix_test.c - * Test posix-specific features. - */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include -#include - -/* Test read timeout by causing the - * server to sleep for 10s on a query. - */ -int test_read_timeout( void ) { - mongo conn[1]; - bson b, obj, out, fields; - int res; - - if ( mongo_connect( conn, TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - bson_init( &b ); - bson_append_code( &b, "$where", "sleep( 10 * 1000 );"); - bson_finish( &b ); - - bson_init( &obj ); - bson_append_string( &obj, "foo", "bar"); - bson_finish( &obj ); - - res = mongo_insert( conn, "test.foo", &obj, NULL ); - - /* Set the connection timeout here. */ - mongo_set_op_timeout( conn, 1000 ); - - res = mongo_find_one( conn, "test.foo", &b, bson_empty(&fields), &out ); - ASSERT( res == MONGO_ERROR ); - - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( strcmp( "Resource temporarily unavailable", conn->errstr ) == 0 ); - - return 0; -} - -/* Test getaddrinfo() by successfully connecting to 'localhost'. */ -int test_getaddrinfo( void ) { - mongo conn[1]; - bson b[1]; - char *ns = "test.foo"; - - if( mongo_connect( conn, "localhost", 27017 ) != MONGO_OK ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - mongo_cmd_drop_collection( conn, "test", "foo", NULL ); - - bson_init( b ); - bson_append_int( b, "foo", 17 ); - bson_finish( b ); - - mongo_insert( conn , ns , b, NULL ); - - ASSERT( mongo_count( conn, "test", "foo", NULL ) == 1 ); - - bson_destroy( b ); - mongo_destroy( conn ); - - - return 0; -} - -int test_error_messages( void ) { - mongo conn[1]; - bson b[1]; - const char *ns = "test.foo"; - - mongo_init( conn ); - - bson_init( b ); - bson_append_int( b, "foo", 17 ); - bson_finish( b ); - - ASSERT( mongo_insert( conn, ns, b, NULL ) != MONGO_OK ); - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( conn->errcode == ENOTSOCK ); - - mongo_init( conn ); - - ASSERT( mongo_count( conn, "test", "foo", NULL ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( conn->errcode == ENOTSOCK ); - - return 0; -} - -int main() { - char version[10]; - - if( mongo_get_server_version( version ) != -1 && version[0] != '1' ) { - test_read_timeout(); - } - test_getaddrinfo(); - test_error_messages(); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/env_win32_test.c b/mongo-c-driver-v0.6/test/env_win32_test.c deleted file mode 100644 index 035c16f..0000000 --- a/mongo-c-driver-v0.6/test/env_win32_test.c +++ /dev/null @@ -1,131 +0,0 @@ -/* env_win32_test.c - * Test WIN32-dependent features. - */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include - -#ifdef _MSC_VER -#include // send,recv,socklen_t etc -#include // addrinfo -#else -#include -#include -typedef int socklen_t; -#endif - -/* Test read timeout by causing the - * server to sleep for 10s on a query. - */ -int test_read_timeout( void ) { - mongo conn[1]; - bson b, obj, out, fields; - int res; - - if ( mongo_connect( conn, TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - bson_init( &b ); - bson_append_code( &b, "$where", "sleep( 10 * 1000 );"); - bson_finish( &b ); - - bson_init( &obj ); - bson_append_string( &obj, "foo", "bar"); - bson_finish( &obj ); - - res = mongo_insert( conn, "test.foo", &obj, NULL ); - - /* Set the connection timeout here. */ - - if( mongo_set_op_timeout( conn, 1000 ) != MONGO_OK ) { - printf("Could not set socket timeout!."); - exit(1); - } - - res = mongo_find_one( conn, "test.foo", &b, bson_empty(&fields), &out ); - ASSERT( res == MONGO_ERROR ); - - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( conn->errcode == WSAETIMEDOUT ); - - return 0; -} - -/* Test getaddrinfo() by successfully connecting to 'localhost'. */ -int test_getaddrinfo( void ) { - mongo conn[1]; - bson b[1]; - const char *ns = "test.foo"; - const char *errmsg = "getaddrinfo failed"; - - if( mongo_connect( conn, "badhost", 27017 ) == MONGO_OK ) { - printf( "connected to bad host!\n" ); - exit( 1 ); - } else { - ASSERT( strncmp( errmsg, conn->errstr, strlen( errmsg ) ) == 0 ); - } - - - if( mongo_connect( conn, "localhost", 27017 ) != MONGO_OK ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - mongo_cmd_drop_collection( conn, "test", "foo", NULL ); - - bson_init( b ); - bson_append_int( b, "foo", 17 ); - bson_finish( b ); - - mongo_insert( conn , ns , b, NULL ); - - ASSERT( mongo_count( conn, "test", "foo", NULL ) == 1 ); - - bson_destroy( b ); - mongo_destroy( conn ); - - - return 0; -} - -int test_error_messages( void ) { - mongo conn[1]; - bson b[1]; - const char *ns = "test.foo"; - - mongo_init( conn ); - - bson_init( b ); - bson_append_int( b, "foo", 17 ); - bson_finish( b ); - - ASSERT( mongo_insert( conn, ns, b, NULL ) != MONGO_OK ); - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( conn->errcode == WSAENOTSOCK ); - - mongo_init( conn ); - - ASSERT( mongo_count( conn, "test", "foo", NULL ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_IO_ERROR ); - ASSERT( conn->errcode == WSAENOTSOCK ); - - return 0; -} - -int main() { - char version[10]; - INIT_SOCKETS_FOR_WINDOWS; - - if( mongo_get_server_version( version ) != -1 && version[0] != '1' ) { - test_read_timeout(); - } - test_getaddrinfo(); - test_error_messages(); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/errors_test.c b/mongo-c-driver-v0.6/test/errors_test.c deleted file mode 100644 index ebbac88..0000000 --- a/mongo-c-driver-v0.6/test/errors_test.c +++ /dev/null @@ -1,259 +0,0 @@ -#include "test.h" -#include "mongo.h" -#include -#include -#include - -static const char *db = "test"; -static const char *ns = "test.c.error"; - -int test_namespace_validation() { - mongo conn[1]; - char longns[130] = "test.foo"; - int i; - - mongo_init( conn ); - - /* Test a few legal namespaces. */ - ASSERT( mongo_validate_ns( conn, "test.foo" ) == MONGO_OK ); - ASSERT( conn->err == 0 ); - - ASSERT( mongo_validate_ns( conn, "test.foo.bar" ) == MONGO_OK ); - ASSERT( conn->err == 0 ); - - /* Test illegal namespaces. */ - ASSERT( mongo_validate_ns( conn, ".test.foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "ns cannot start with", 20 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "test..foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "ns cannot start with", 20 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "test" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "ns cannot start with", 20 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "." ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "ns cannot start with", 20 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "tes t.foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Database name may not contain", 28 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "te$st.foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Database name may not contain", 28 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "te/st.foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Database name may not contain", 28 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "te\\st.foo" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Database name may not contain", 28 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "test.fo$o" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Collection may not contain '$'", 29 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "test.fo..o" ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Collection may not contain two consecutive '.'", 46 ) == 0 ); - mongo_clear_errors( conn ); - - ASSERT( mongo_validate_ns( conn, "test.fo.o." ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Collection may not end with '.'", 30 ) == 0 ); - mongo_clear_errors( conn ); - - for(i = 8; i < 129; i++ ) - longns[i] = 'a'; - longns[129] = '\0'; - - ASSERT( mongo_validate_ns( conn, longns ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Namespace too long; has 129 but must <= 128.", 32 ) == 0 ); - mongo_clear_errors( conn ); - - return 0; -} - -int test_namespace_validation_on_insert( void ) { - mongo conn[1]; - bson b[1], b2[1]; - bson *objs[2]; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - bson_init( b ); - bson_append_int( b, "foo", 1 ); - bson_finish( b ); - - ASSERT( mongo_insert( conn, "tet.fo$o", b, NULL ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Collection may not contain '$'", 29 ) == 0 ); - mongo_clear_errors( conn ); - - bson_init( b2 ); - bson_append_int( b2, "foo", 1 ); - bson_finish( b2 ); - - objs[0] = b; - objs[1] = b2; - - ASSERT( mongo_insert_batch( conn, "tet.fo$o", - (const bson **)objs, 2, NULL, 0 ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_NS_INVALID ); - ASSERT( strncmp( conn->errstr, "Collection may not contain '$'", 29 ) == 0 ); - - return 0; -} - -int test_insert_limits( void ) { - char version[10]; - mongo conn[1]; - int i; - char key[10]; - bson b[1], b2[1]; - bson *objs[2]; - - /* Test the default max BSON size. */ - mongo_init( conn ); - ASSERT( conn->max_bson_size == MONGO_DEFAULT_MAX_BSON_SIZE ); - - /* We'll perform the full test if we're running v2.0 or later. */ - if( mongo_get_server_version( version ) != -1 && version[0] <= '1' ) - return 0; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - ASSERT( conn->max_bson_size > MONGO_DEFAULT_MAX_BSON_SIZE ); - - bson_init( b ); - for(i=0; i<1200000; i++) { - sprintf( key, "%d", i + 10000000 ); - bson_append_int( b, key, i ); - } - bson_finish( b ); - - ASSERT( bson_size( b ) > conn->max_bson_size ); - - ASSERT( mongo_insert( conn, "test.foo", b, NULL ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_TOO_LARGE ); - - mongo_clear_errors( conn ); - ASSERT( conn->err == 0 ); - - bson_init( b2 ); - bson_append_int( b2, "foo", 1 ); - bson_finish( b2 ); - - objs[0] = b; - objs[1] = b2; - - ASSERT( mongo_insert_batch( conn, "test.foo", (const bson **)objs, 2, - NULL, 0 ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_TOO_LARGE ); - - return 0; -} - -int test_get_last_error_commands( void ) { - mongo conn[1]; - bson obj; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - /*********************/ - ASSERT( mongo_cmd_get_prev_error( conn, db, NULL ) == MONGO_OK ); - ASSERT( conn->lasterrcode == 0 ); - ASSERT( conn->lasterrstr[0] == 0 ); - - ASSERT( mongo_cmd_get_last_error( conn, db, NULL ) == MONGO_OK ); - ASSERT( conn->lasterrcode == 0 ); - ASSERT( conn->lasterrstr[0] == 0 ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, &obj ) == MONGO_OK ); - bson_destroy( &obj ); - - ASSERT( mongo_cmd_get_last_error( conn, db, &obj ) == MONGO_OK ); - bson_destroy( &obj ); - - /*********************/ - mongo_simple_int_command( conn, db, "forceerror", 1, NULL ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, NULL ) == MONGO_ERROR ); - ASSERT( conn->lasterrcode == 10038 ); - ASSERT( strcmp( ( const char * )conn->lasterrstr, "forced error" ) == 0 ); - - ASSERT( mongo_cmd_get_last_error( conn, db, NULL ) == MONGO_ERROR ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, &obj ) == MONGO_ERROR ); - bson_destroy( &obj ); - - ASSERT( mongo_cmd_get_last_error( conn, db, &obj ) == MONGO_ERROR ); - bson_destroy( &obj ); - - /* should clear lasterror but not preverror */ - mongo_find_one( conn, ns, bson_empty( &obj ), bson_empty( &obj ), NULL ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, NULL ) == MONGO_ERROR ); - ASSERT( mongo_cmd_get_last_error( conn, db, NULL ) == MONGO_OK ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, &obj ) == MONGO_ERROR ); - bson_destroy( &obj ); - - ASSERT( mongo_cmd_get_last_error( conn, db, &obj ) == MONGO_OK ); - bson_destroy( &obj ); - - /*********************/ - mongo_cmd_reset_error( conn, db ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, NULL ) == MONGO_OK ); - ASSERT( mongo_cmd_get_last_error( conn, db, NULL ) == MONGO_OK ); - - ASSERT( mongo_cmd_get_prev_error( conn, db, &obj ) == MONGO_OK ); - bson_destroy( &obj ); - - ASSERT( mongo_cmd_get_last_error( conn, db, &obj ) == MONGO_OK ); - bson_destroy( &obj ); - - - mongo_cmd_drop_db( conn, db ); - mongo_destroy( conn ); - - return 0; -} - -int main() { - test_get_last_error_commands(); - test_insert_limits(); - test_namespace_validation(); - test_namespace_validation_on_insert(); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/examples_test.c b/mongo-c-driver-v0.6/test/examples_test.c deleted file mode 100644 index f023c50..0000000 --- a/mongo-c-driver-v0.6/test/examples_test.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "test.h" -#include "bson.h" -#include -#include -#include - -int main() { - bson b, sub; - bson_iterator it; - - /* Create a rich document like this one: - * - * { _id: ObjectId("4d95ea712b752328eb2fc2cc"), - * user_id: ObjectId("4d95ea712b752328eb2fc2cd"), - * - * items: [ - * { sku: "col-123", - * name: "John Coltrane: Impressions", - * price: 1099, - * }, - * - * { sku: "young-456", - * name: "Larry Young: Unity", - * price: 1199 - * } - * ], - * - * address: { - * street: "59 18th St.", - * zip: 10010 - * }, - * - * total: 2298 - * } - */ - bson_init( &b ); - bson_append_new_oid( &b, "_id" ); - bson_append_new_oid( &b, "user_id" ); - - bson_append_start_array( &b, "items" ); - bson_append_start_object( &b, "0" ); - bson_append_string( &b, "name", "John Coltrane: Impressions" ); - bson_append_int( &b, "price", 1099 ); - bson_append_finish_object( &b ); - - bson_append_start_object( &b, "1" ); - bson_append_string( &b, "name", "Larry Young: Unity" ); - bson_append_int( &b, "price", 1199 ); - bson_append_finish_object( &b ); - bson_append_finish_object( &b ); - - bson_append_start_object( &b, "address" ); - bson_append_string( &b, "street", "59 18th St." ); - bson_append_int( &b, "zip", 10010 ); - bson_append_finish_object( &b ); - - bson_append_int( &b, "total", 2298 ); - - bson_finish( &b ); - - /* Advance to the 'items' array */ - bson_find( &it, &b, "items" ); - - /* Get the subobject representing items */ - bson_iterator_subobject( &it, &sub ); - - /* Now iterate that object */ - bson_print( &sub ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/functions_test.c b/mongo-c-driver-v0.6/test/functions_test.c deleted file mode 100644 index dc3a909..0000000 --- a/mongo-c-driver-v0.6/test/functions_test.c +++ /dev/null @@ -1,113 +0,0 @@ -/* functions.c */ - -#ifndef _WIN32 -#include "test.h" -#include "mongo.h" -#include -#include -#include -#include - -int test_value = 0; - -void *my_malloc( size_t size ) { - test_value = 1; - return malloc( size ); -} - -void *my_realloc( void *ptr, size_t size ) { - test_value = 2; - return realloc( ptr, size ); -} - -void my_free( void *ptr ) { - test_value = 3; - free( ptr ); -} - -int my_printf( const char *format, ... ) { - int ret = 0; - test_value = 4; - - return ret; -} - -int my_fprintf( FILE *fp, const char *format, ... ) { - int ret = 0; - test_value = 5; - - return ret; -} - -int my_sprintf( char *s, const char *format, ... ) { - int ret = 0; - test_value = 6; - - return ret; -} - -int my_errprintf( const char *format, ... ) { - int ret = 0; - test_value = 7; - - return ret; -} - -int main() { - - void *ptr; - char str[32]; - int size = 256; - - ptr = bson_malloc( size ); - ASSERT( test_value == 0 ); - ptr = bson_realloc( ptr, size + 64 ); - ASSERT( test_value == 0 ); - bson_free( ptr ); - ASSERT( test_value == 0 ); - - bson_malloc_func = my_malloc; - bson_realloc_func = my_realloc; - bson_free = my_free; - - ptr = bson_malloc( size ); - ASSERT( test_value == 1 ); - ptr = bson_realloc( ptr, size + 64 ); - ASSERT( test_value == 2 ); - bson_free( ptr ); - ASSERT( test_value == 3 ); - - test_value = 0; - - bson_printf( "Test printf %d\n", test_value ); - ASSERT( test_value == 0 ); - bson_fprintf( stdout, "Test fprintf %d\n", test_value ); - ASSERT( test_value == 0 ); - bson_sprintf( str, "Test sprintf %d\n", test_value ); - printf( "%s", str ); - ASSERT( test_value == 0 ); - bson_errprintf( "Test err %d\n", test_value ); - ASSERT( test_value == 0 ); - - bson_printf = my_printf; - bson_sprintf = my_sprintf; - bson_fprintf = my_fprintf; - bson_errprintf = my_errprintf; - - bson_printf( "Test %d\n", test_value ); - ASSERT( test_value == 4 ); - bson_fprintf( stdout, "Test %d\n", test_value ); - ASSERT( test_value == 5 ); - bson_sprintf( str, "Test %d\n", test_value ); - ASSERT( test_value == 6 ); - bson_printf( "Str: %s\n", str ); - bson_errprintf( "Test %d\n", test_value ); - ASSERT( test_value == 7 ); - - return 0; -} -#else -int main() { - return 0; -} -#endif diff --git a/mongo-c-driver-v0.6/test/gridfs_test.c b/mongo-c-driver-v0.6/test/gridfs_test.c deleted file mode 100644 index 95ed620..0000000 --- a/mongo-c-driver-v0.6/test/gridfs_test.c +++ /dev/null @@ -1,263 +0,0 @@ -#include "test.h" -#include "md5.h" -#include "mongo.h" -#include "gridfs.h" -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif - -#define LARGE 3*1024*1024 -#define UPPER 2000*1024 -#define MEDIUM 1024*512 -#define LOWER 1024*128 -#define DELTA 1024*128 - -void fill_buffer_randomly( char *data, int64_t length ) { - int64_t i; - int random; - char *letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - int nletters = strlen( letters )+1; - - for ( i = 0; i < length; i++ ) { - random = rand() % nletters; - *( data + i ) = letters[random]; - } -} - -static void digest2hex( mongo_md5_byte_t digest[16], char hex_digest[33] ) { - static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; - int i; - for ( i=0; i<16; i++ ) { - hex_digest[2*i] = hex[( digest[i] & 0xf0 ) >> 4]; - hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ]; - } - hex_digest[32] = '\0'; -} - -void test_gridfile( gridfs *gfs, char *data_before, int64_t length, char *filename, char *content_type ) { - gridfile gfile[1]; - FILE *stream; - mongo_md5_state_t pms[1]; - mongo_md5_byte_t digest[16]; - char hex_digest[33]; - int64_t i = length; - int n; - char *data_after = bson_malloc( LARGE ); - - gridfs_find_filename( gfs, filename, gfile ); - ASSERT( gridfile_exists( gfile ) ); - - stream = fopen( "output", "w+" ); - gridfile_write_file( gfile, stream ); - fseek( stream, 0, SEEK_SET ); - ASSERT( fread( data_after, length, sizeof( char ), stream ) ); - fclose( stream ); - ASSERT( strncmp( data_before, data_after, length ) == 0 ); - - gridfile_read( gfile, length, data_after ); - ASSERT( strncmp( data_before, data_after, length ) == 0 ); - - ASSERT( strcmp( gridfile_get_filename( gfile ), filename ) == 0 ); - - ASSERT( gridfile_get_contentlength( gfile ) == length ); - - ASSERT( gridfile_get_chunksize( gfile ) == DEFAULT_CHUNK_SIZE ); - - ASSERT( strcmp( gridfile_get_contenttype( gfile ), content_type ) == 0 ) ; - - ASSERT( strncmp( data_before, data_after, length ) == 0 ); - - mongo_md5_init( pms ); - - n = 0; - while( i > INT_MAX ) { - mongo_md5_append( pms, ( const mongo_md5_byte_t * )data_before + ( n * INT_MAX ), INT_MAX ); - i -= INT_MAX; - n += 1; - } - if( i > 0 ) - mongo_md5_append( pms, ( const mongo_md5_byte_t * )data_before + ( n * INT_MAX ), i ); - - mongo_md5_finish( pms, digest ); - digest2hex( digest, hex_digest ); - ASSERT( strcmp( gridfile_get_md5( gfile ), hex_digest ) == 0 ); - - gridfile_destroy( gfile ); - gridfs_remove_filename( gfs, filename ); - free( data_after ); - unlink( "output" ); -} - -void test_basic() { - mongo conn[1]; - gridfs gfs[1]; - char *data_before = bson_malloc( UPPER ); - int64_t i; - FILE *fd; - - srand( time( NULL ) ); - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn, TEST_SERVER, 27017 ) ) { - printf( "failed to connect 2\n" ); - exit( 1 ); - } - - gridfs_init( conn, "test", "fs", gfs ); - - fill_buffer_randomly( data_before, UPPER ); - for ( i = LOWER; i <= UPPER; i += DELTA ) { - - /* Input from buffer */ - gridfs_store_buffer( gfs, data_before, i, "input-buffer", "text/html" ); - test_gridfile( gfs, data_before, i, "input-buffer", "text/html" ); - - /* Input from file */ - fd = fopen( "input-file", "w" ); - fwrite( data_before, sizeof( char ), i, fd ); - fclose( fd ); - gridfs_store_file( gfs, "input-file", "input-file", "text/html" ); - test_gridfile( gfs, data_before, i, "input-file", "text/html" ); - } - - gridfs_destroy( gfs ); - mongo_disconnect( conn ); - mongo_destroy( conn ); - free( data_before ); - - /* Clean up files. */ - unlink( "input-file" ); - unlink( "output" ); -} - -void test_streaming() { - mongo conn[1]; - gridfs gfs[1]; - gridfile gfile[1]; - char *medium = bson_malloc( 2*MEDIUM ); - char *small = bson_malloc( LOWER ); - char *buf = bson_malloc( LARGE ); - int n; - - if( buf == NULL || small == NULL ) { - printf( "Failed to allocate" ); - exit( 1 ); - } - - srand( time( NULL ) ); - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect 3\n" ); - exit( 1 ); - } - - fill_buffer_randomly( medium, ( int64_t )2 * MEDIUM ); - fill_buffer_randomly( small, ( int64_t )LOWER ); - fill_buffer_randomly( buf, ( int64_t )LARGE ); - - gridfs_init( conn, "test", "fs", gfs ); - gridfile_writer_init( gfile, gfs, "medium", "text/html" ); - - gridfile_write_buffer( gfile, medium, MEDIUM ); - gridfile_write_buffer( gfile, medium + MEDIUM, MEDIUM ); - gridfile_writer_done( gfile ); - test_gridfile( gfs, medium, 2 * MEDIUM, "medium", "text/html" ); - gridfs_destroy( gfs ); - - gridfs_init( conn, "test", "fs", gfs ); - - gridfs_store_buffer( gfs, small, LOWER, "small", "text/html" ); - test_gridfile( gfs, small, LOWER, "small", "text/html" ); - gridfs_destroy( gfs ); - - gridfs_init( conn, "test", "fs", gfs ); - gridfile_writer_init( gfile, gfs, "large", "text/html" ); - for( n=0; n < ( LARGE / 1024 ); n++ ) { - gridfile_write_buffer( gfile, buf + ( n * 1024 ), 1024 ); - } - gridfile_writer_done( gfile ); - test_gridfile( gfs, buf, LARGE, "large", "text/html" ); - - gridfs_destroy( gfs ); - mongo_destroy( conn ); - free( buf ); - free( small ); -} - -void test_large() { - mongo conn[1]; - gridfs gfs[1]; - gridfile gfile[1]; - FILE *fd; - int i, n; - char *buffer = bson_malloc( LARGE ); - int64_t filesize = ( int64_t )1024 * ( int64_t )LARGE; - - srand( time( NULL ) ); - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn, TEST_SERVER, 27017 ) ) { - printf( "failed to connect 1\n" ); - exit( 1 ); - } - - gridfs_init( conn, "test", "fs", gfs ); - - /* Create a very large file */ - fill_buffer_randomly( buffer, ( int64_t )LARGE ); - fd = fopen( "bigfile", "w" ); - for( i=0; i<1024; i++ ) { - fwrite( buffer, 1, LARGE, fd ); - } - fclose( fd ); - - /* Now read the file into GridFS */ - gridfs_store_file( gfs, "bigfile", "bigfile", "text/html" ); - - gridfs_find_filename( gfs, "bigfile", gfile ); - - ASSERT( strcmp( gridfile_get_filename( gfile ), "bigfile" ) == 0 ); - ASSERT( gridfile_get_contentlength( gfile ) == filesize ); - - /* Read the file using the streaming interface */ - gridfile_writer_init( gfile, gfs, "bigfile-stream", "text/html" ); - - fd = fopen( "bigfile", "r" ); - - while( ( n = fread( buffer, 1, 1024, fd ) ) != 0 ) { - gridfile_write_buffer( gfile, buffer, n ); - } - gridfile_writer_done( gfile ); - - gridfs_find_filename( gfs, "bigfile-stream", gfile ); - - ASSERT( strcmp( gridfile_get_filename( gfile ), "bigfile-stream" ) == 0 ); - ASSERT( gridfile_get_contentlength( gfile ) == filesize ); - - gridfs_destroy( gfs ); - mongo_disconnect( conn ); - mongo_destroy( conn ); -} - -int main( void ) { -/* See https://jira.mongodb.org/browse/CDRIVER-126 - * on why we exclude this test from running on WIN32 */ -#ifndef _WIN32 - test_basic(); - test_streaming(); -#endif - - /* Normally not necessary to run test_large(), as it - * deals with very large (3GB) files and is therefore slow. - * test_large(); - */ - return 0; -} diff --git a/mongo-c-driver-v0.6/test/helpers_test.c b/mongo-c-driver-v0.6/test/helpers_test.c deleted file mode 100644 index 8c7d929..0000000 --- a/mongo-c-driver-v0.6/test/helpers_test.c +++ /dev/null @@ -1,55 +0,0 @@ -/* helpers.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include -#include - -void test_index_helper( mongo *conn ) { - - bson b, out; - bson_iterator it; - - bson_init( &b ); - bson_append_int( &b, "foo", 1 ); - bson_finish( &b ); - - mongo_create_index( conn, "test.bar", &b, MONGO_INDEX_SPARSE | MONGO_INDEX_UNIQUE, &out ); - - bson_destroy( &b ); - - bson_init( &b ); - bson_append_start_object( &b, "key" ); - bson_append_int( &b, "foo", 1 ); - bson_append_finish_object( &b ); - - bson_finish( &b ); - - mongo_find_one( conn, "test.system.indexes", &b, NULL, &out ); - - bson_print( &out ); - - bson_iterator_init( &it, &out ); - - ASSERT( bson_find( &it, &out, "unique" ) ); - ASSERT( bson_find( &it, &out, "sparse" ) ); -} - -int main() { - - mongo conn[1]; - - INIT_SOCKETS_FOR_WINDOWS; - - if( mongo_connect( conn, TEST_SERVER, 27017 ) != MONGO_OK ) { - printf( "Failed to connect" ); - exit( 1 ); - } - - - test_index_helper( conn ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/json_test.c b/mongo-c-driver-v0.6/test/json_test.c deleted file mode 100644 index 2ce0bde..0000000 --- a/mongo-c-driver-v0.6/test/json_test.c +++ /dev/null @@ -1,169 +0,0 @@ -/* testjson.c */ - -#include "test.h" -#include -#include - -#include "mongo.h" -#include "json/json.h" -#include "md5.h" - -void json_to_bson_append_element( bson_buffer *bb , const char *k , struct json_object *v ); - -/** - should already have called start_array - this will not call start/finish - */ -void json_to_bson_append_array( bson_buffer *bb , struct json_object *a ) { - int i; - char buf[10]; - for ( i=0; i -#include -#include -#include - -int increment( void ) { - static int i = 1000; - i++; - return i; -} - -int fuzz( void ) { - return 50000; -} - -/* Test custom increment and fuzz functions. */ -int main() { - - bson_oid_t o; - int res; - - bson_set_oid_inc( increment ); - bson_set_oid_fuzz( fuzz ); - - bson_oid_gen( &o ); - bson_big_endian32( &res, &( o.ints[2] ) ); - - ASSERT( o.ints[1] == 50000 ); - ASSERT( res == 1001 ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/platform/linux/timeouts.c b/mongo-c-driver-v0.6/test/platform/linux/timeouts.c deleted file mode 100644 index a54fb15..0000000 --- a/mongo-c-driver-v0.6/test/platform/linux/timeouts.c +++ /dev/null @@ -1,37 +0,0 @@ -/* timeouts.c */ - -#include "../../test.h" -#include "mongo.h" -#include -#include -#include -#include - -int main() { - - mongo conn[1]; - bson b; - int res; - - if( mongo_connect( conn, TEST_SERVER, 27017 ) != MONGO_OK ) { - printf("Failed to connect"); - exit(1); - } - - res = mongo_simple_str_command( conn, "test", "$eval", - "for(i=0; i<100000; i++) { db.foo.find() }", &b ); - - ASSERT( res == MONGO_OK ); - - /* 50ms timeout */ - mongo_set_op_timeout( conn, 50 ); - - ASSERT( conn->err == 0 ); - res = mongo_simple_str_command( conn, "test", "$eval", - "for(i=0; i<100000; i++) { db.foo.find() }", &b ); - - ASSERT( res == MONGO_ERROR ); - ASSERT( conn->err == MONGO_IO_ERROR ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/replica_set_test.c b/mongo-c-driver-v0.6/test/replica_set_test.c deleted file mode 100644 index da64679..0000000 --- a/mongo-c-driver-v0.6/test/replica_set_test.c +++ /dev/null @@ -1,154 +0,0 @@ -/* test.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include -#include - -#ifndef SEED_START_PORT -#define SEED_START_PORT 30000 -#endif - -#ifndef REPLICA_SET_NAME -#define REPLICA_SET_NAME "replica-set-foo" -#endif - -int test_connect( const char *set_name ) { - - mongo conn[1]; - int res; - - INIT_SOCKETS_FOR_WINDOWS; - - mongo_replset_init( conn, set_name ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT + 1 ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT ); - - res = mongo_replset_connect( conn ); - - if( res != MONGO_OK ) { - res = conn->err; - return res; - } - - ASSERT( conn->primary->port == SEED_START_PORT || - conn->primary->port == SEED_START_PORT + 1 || - conn->primary->port == SEED_START_PORT + 2 ); - - mongo_destroy( conn ); - return res; -} - -int test_reconnect( const char *set_name ) { - - mongo conn[1]; - int res = 0; - int e = 0; - bson b; - - INIT_SOCKETS_FOR_WINDOWS; - - mongo_replset_init( conn, set_name ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT + 1 ); - - - if( ( mongo_replset_connect( conn ) != MONGO_OK ) ) { - mongo_destroy( conn ); - return MONGO_ERROR; - } else { - fprintf( stderr, "Disconnect now:\n" ); - sleep( 10 ); - e = 1; - do { - res = mongo_find_one( conn, "foo.bar", bson_empty( &b ), bson_empty( &b ), NULL ); - if( res == MONGO_ERROR && conn->err == MONGO_IO_ERROR ) { - sleep( 2 ); - if( e++ < 30 ) { - fprintf( stderr, "Attempting reconnect %d.\n", e ); - mongo_reconnect( conn ); - } else { - fprintf( stderr, "Fail.\n" ); - return -1; - } - } - } while( 1 ); - } - - - return 0; -} - -int test_insert_limits( const char *set_name ) { - char version[10]; - mongo conn[1]; - mongo_write_concern wc[1]; - int i; - char key[10]; - int res = 0; - bson b[1], b2[1]; - bson *objs[2]; - - mongo_write_concern_init( wc ); - wc->w = 1; - mongo_write_concern_finish( wc ); - - /* We'll perform the full test if we're running v2.0 or later. */ - if( mongo_get_server_version( version ) != -1 && version[0] <= '1' ) - return 0; - - mongo_replset_init( conn, set_name ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT + 1 ); - mongo_replset_add_seed( conn, TEST_SERVER, SEED_START_PORT ); - res = mongo_replset_connect( conn ); - - if( res != MONGO_OK ) { - res = conn->err; - return res; - } - - ASSERT( conn->max_bson_size > MONGO_DEFAULT_MAX_BSON_SIZE ); - - bson_init( b ); - for(i=0; i<1200000; i++) { - sprintf( key, "%d", i + 10000000 ); - bson_append_int( b, key, i ); - } - bson_finish( b ); - - ASSERT( bson_size( b ) > conn->max_bson_size ); - - ASSERT( mongo_insert( conn, "test.foo", b, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_TOO_LARGE ); - - mongo_clear_errors( conn ); - ASSERT( conn->err == 0 ); - - bson_init( b2 ); - bson_append_int( b2, "foo", 1 ); - bson_finish( b2 ); - - objs[0] = b; - objs[1] = b2; - - ASSERT( mongo_insert_batch( conn, "test.foo", (const bson**)objs, 2, wc, 0 ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_TOO_LARGE ); - - mongo_write_concern_destroy( wc ); - - return 0; -} - -int main() { - ASSERT( test_connect( REPLICA_SET_NAME ) == MONGO_OK ); - ASSERT( test_connect( "test-foobar" ) == MONGO_CONN_BAD_SET_NAME ); - ASSERT( test_insert_limits( REPLICA_SET_NAME ) == MONGO_OK ); - - /* - ASSERT( test_reconnect( "test-rs" ) == 0 ); - */ - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/resize_test.c b/mongo-c-driver-v0.6/test/resize_test.c deleted file mode 100644 index 33d433f..0000000 --- a/mongo-c-driver-v0.6/test/resize_test.c +++ /dev/null @@ -1,34 +0,0 @@ -/* resize.c */ - -#include "test.h" -#include "bson.h" -#include - -/* 64 Xs */ -const char *bigstring = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; - -int main() { - bson b; - - bson_init( &b ); - bson_append_string( &b, "a", bigstring ); - bson_append_start_object( &b, "sub" ); - bson_append_string( &b,"a", bigstring ); - bson_append_start_object( &b, "sub" ); - bson_append_string( &b,"a", bigstring ); - bson_append_start_object( &b, "sub" ); - bson_append_string( &b,"a", bigstring ); - bson_append_string( &b,"b", bigstring ); - bson_append_string( &b,"c", bigstring ); - bson_append_string( &b,"d", bigstring ); - bson_append_string( &b,"e", bigstring ); - bson_append_string( &b,"f", bigstring ); - bson_append_finish_object( &b ); - bson_append_finish_object( &b ); - bson_append_finish_object( &b ); - bson_finish( &b ); - - /* bson_print(&b); */ - bson_destroy( &b ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/simple_test.c b/mongo-c-driver-v0.6/test/simple_test.c deleted file mode 100644 index e3e0d28..0000000 --- a/mongo-c-driver-v0.6/test/simple_test.c +++ /dev/null @@ -1,159 +0,0 @@ -/* test.c */ - -#include "test.h" -#include "mongo.h" -#include "env.h" -#include -#include -#include - -int main() { - mongo conn[1]; - mongo_cursor cursor[1]; - bson b; - int i; - char hex_oid[25]; - bson_timestamp_t ts = { 1, 2 }; - - const char *col = "c.simple"; - const char *ns = "test.c.simple"; - - /* mongo_connect( conn, TEST_SERVER, 27017 ); */ - - /* Simple connect API - mongo conn[1]; - - mongo_init( conn ); - mongo_connect( conn, TEST_SERVER, 27017 ); - mongo_destroy( conn ); - - * Advanced and replica set API - mongo conn[1]; - - mongo_replset_init( conn, "foobar" ); - mongo_set_connect_timeout( conn, 1000 ); - mongo_replset_connect( conn ); - mongo_destroy( conn ); - - * BSON API - bson obj[1]; - - bson_init( obj ); - bson_append_int( obj, "a", 1 ); - bson_finish( obj ); - mongo_insert( conn, obj ); - bson_destroy( obj ); - - * BSON Iterator API - bson_iterator i[1]; - - bson_iterator_init( i, b ); - - * Cursor API - mongo_cursor cursor[1]; - - mongo_cursor_init( cursor, "test.ns" ); - mongo_cursor_limit( cursor, 100 ); - mongo_cursor_skip( cursor, 100 ); - mongo_cursor_query( cursor, &query ); - mongo_cursor_fields( cursor, &fields ); - data = mongo_cursor_next( cursor ); - mongo_cursor_destroy( cursor ); - */ - - INIT_SOCKETS_FOR_WINDOWS; - - if( mongo_connect( conn , TEST_SERVER, 27017 ) != MONGO_OK ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - mongo_cmd_drop_collection( conn, "test", col, NULL ); - mongo_find_one( conn, ns, bson_empty( &b ), bson_empty( &b ), NULL ); - - for( i=0; i< 5; i++ ) { - bson_init( &b ); - - bson_append_new_oid( &b, "_id" ); - bson_append_timestamp( &b, "ts", &ts ); - bson_append_double( &b , "a" , 17 ); - bson_append_int( &b , "b" , 17 ); - bson_append_string( &b , "c" , "17" ); - - { - bson_append_start_object( &b , "d" ); - bson_append_int( &b, "i", 71 ); - bson_append_finish_object( &b ); - } - { - bson_append_start_array( &b , "e" ); - bson_append_int( &b, "0", 71 ); - bson_append_string( &b, "1", "71" ); - bson_append_finish_object( &b ); - } - - bson_finish( &b ); - ASSERT( mongo_insert( conn , ns , &b, NULL ) == MONGO_OK ); - bson_destroy( &b ); - } - - mongo_cursor_init( cursor, conn, ns ); - - while( mongo_cursor_next( cursor ) == MONGO_OK ) { - bson_iterator it; - bson_iterator_init( &it, mongo_cursor_bson( cursor ) ); - while( bson_iterator_next( &it ) ) { - fprintf( stderr, " %s: ", bson_iterator_key( &it ) ); - - switch( bson_iterator_type( &it ) ) { - case BSON_DOUBLE: - fprintf( stderr, "(double) %e\n", bson_iterator_double( &it ) ); - break; - case BSON_INT: - fprintf( stderr, "(int) %d\n", bson_iterator_int( &it ) ); - break; - case BSON_STRING: - fprintf( stderr, "(string) \"%s\"\n", bson_iterator_string( &it ) ); - break; - case BSON_OID: - bson_oid_to_string( bson_iterator_oid( &it ), hex_oid ); - fprintf( stderr, "(oid) \"%s\"\n", hex_oid ); - break; - case BSON_OBJECT: - fprintf( stderr, "(subobject) {...}\n" ); - break; - case BSON_ARRAY: - fprintf( stderr, "(array) [...]\n" ); - break; - case BSON_TIMESTAMP: - fprintf( stderr, "(timestamp) [...]\n" ); - break; - default: - fprintf( stderr, "(type %d)\n", bson_iterator_type( &it ) ); - break; - } - } - fprintf( stderr, "\n" ); - } - - mongo_cursor_destroy( cursor ); - ASSERT( mongo_cmd_drop_db( conn, "test" ) == MONGO_OK ); - mongo_disconnect( conn ); - - ASSERT( mongo_check_connection( conn ) == MONGO_ERROR ); - - mongo_reconnect( conn ); - - ASSERT( mongo_check_connection( conn ) == MONGO_OK ); - - mongo_env_close_socket( conn->sock ); - - ASSERT( mongo_check_connection( conn ) == MONGO_ERROR ); - - mongo_reconnect( conn ); - - ASSERT( mongo_simple_int_command( conn, "admin", "ping", 1, NULL ) == MONGO_OK ); - - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/sizes_test.c b/mongo-c-driver-v0.6/test/sizes_test.c deleted file mode 100644 index dc7cdcf..0000000 --- a/mongo-c-driver-v0.6/test/sizes_test.c +++ /dev/null @@ -1,22 +0,0 @@ -/* sizes.c */ - -#include "test.h" -#include "mongo.h" -#include - -int main() { - mongo_reply mr; - - ASSERT( sizeof( int ) == 4 ); - ASSERT( sizeof( int64_t ) == 8 ); - ASSERT( sizeof( double ) == 8 ); - ASSERT( sizeof( bson_oid_t ) == 12 ); - - ASSERT( sizeof( mongo_header ) == 4+4+4+4 ); - ASSERT( sizeof( mongo_reply_fields ) == 4+8+4+4 ); - - /* field offset of obj in mongo_reply */ - ASSERT( ( &mr.objs - ( char * )&mr ) == ( 4+4+4+4 + 4+8+4+4 ) ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/test.h b/mongo-c-driver-v0.6/test/test.h deleted file mode 100644 index eee7f93..0000000 --- a/mongo-c-driver-v0.6/test/test.h +++ /dev/null @@ -1,56 +0,0 @@ -#include "mongo.h" -#include - -#define ASSERT(x) \ - do{ \ - if(!(x)){ \ - printf("\nFailed ASSERT [%s] (%d):\n %s\n\n", __FILE__, __LINE__, #x); \ - exit(1); \ - }\ - }while(0) - -#define ASSERT_EQUAL_STRINGS(x, y) \ - do{ \ - if((strncmp( x, y, strlen( y ) ) != 0 )){ \ - printf("\nFailed ASSERT_EQUAL_STRINGS [%s] (%d):\n \"%s\" does not equal\n %s\n", __FILE__, __LINE__, x, #y); \ - exit(1); \ - }\ - }while(0) - -#ifdef _WIN32 -#define INIT_SOCKETS_FOR_WINDOWS mongo_init_sockets(); -#else -#define INIT_SOCKETS_FOR_WINDOWS do {} while(0) -#endif - -const char *TEST_DB = "test"; -const char *TEST_COL = "foo"; -const char *TEST_NS = "test.foo"; - -MONGO_EXTERN_C_START - -int mongo_get_server_version( char *version ) { - mongo conn[1]; - bson cmd[1], out[1]; - bson_iterator it[1]; - const char *result; - - mongo_connect( conn, TEST_SERVER, 27017 ); - - bson_init( cmd ); - bson_append_int( cmd, "buildinfo", 1 ); - bson_finish( cmd ); - - if( mongo_run_command( conn, "admin", cmd, out ) == MONGO_ERROR ) { - return -1; - } - - bson_iterator_init( it, out ); - result = bson_iterator_string( it ); - - memcpy( version, result, strlen( result ) ); - - return 0; -} - -MONGO_EXTERN_C_END diff --git a/mongo-c-driver-v0.6/test/update_test.c b/mongo-c-driver-v0.6/test/update_test.c deleted file mode 100644 index 0677ac1..0000000 --- a/mongo-c-driver-v0.6/test/update_test.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "test.h" -#include "mongo.h" -#include -#include -#include - -int main() { - mongo conn[1]; - bson obj; - bson cond; - int i; - bson_oid_t oid; - const char *col = "c.update_test"; - const char *ns = "test.c.update_test"; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn , TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - /* if the collection doesn't exist dropping it will fail */ - if ( mongo_cmd_drop_collection( conn, "test", col, NULL ) == MONGO_OK - && mongo_find_one( conn, ns, bson_empty( &obj ), bson_empty( &obj ), NULL ) != MONGO_OK ) { - printf( "failed to drop collection\n" ); - exit( 1 ); - } - - bson_oid_gen( &oid ); - - { - /* insert */ - bson_init( &obj ); - bson_append_oid( &obj, "_id", &oid ); - bson_append_int( &obj, "a", 3 ); - bson_finish( &obj ); - mongo_insert( conn, ns, &obj, NULL ); - bson_destroy( &obj ); - } - - { - /* insert */ - bson op; - - bson_init( &cond ); - bson_append_oid( &cond, "_id", &oid ); - bson_finish( &cond ); - - bson_init( &op ); - { - bson_append_start_object( &op, "$inc" ); - bson_append_int( &op, "a", 2 ); - bson_append_finish_object( &op ); - } - { - bson_append_start_object( &op, "$set" ); - bson_append_double( &op, "b", -1.5 ); - bson_append_finish_object( &op ); - } - bson_finish( &op ); - - for ( i=0; i<5; i++ ) - mongo_update( conn, ns, &cond, &op, 0, NULL ); - - /* cond is used later */ - bson_destroy( &op ); - } - - if( mongo_find_one( conn, ns, &cond, 0, &obj ) != MONGO_OK ) { - printf( "Failed to find object\n" ); - exit( 1 ); - } else { - int fields = 0; - bson_iterator it; - bson_iterator_init( &it, &obj ); - - bson_destroy( &cond ); - - while( bson_iterator_next( &it ) ) { - switch( bson_iterator_key( &it )[0] ) { - case '_': /* id */ - ASSERT( bson_iterator_type( &it ) == BSON_OID ); - ASSERT( !memcmp( bson_iterator_oid( &it )->bytes, oid.bytes, 12 ) ); - fields++; - break; - case 'a': - ASSERT( bson_iterator_type( &it ) == BSON_INT ); - ASSERT( bson_iterator_int( &it ) == 3 + 5*2 ); - fields++; - break; - case 'b': - ASSERT( bson_iterator_type( &it ) == BSON_DOUBLE ); - ASSERT( bson_iterator_double( &it ) == -1.5 ); - fields++; - break; - } - } - - ASSERT( fields == 3 ); - } - - bson_destroy( &obj ); - - mongo_cmd_drop_db( conn, "test" ); - mongo_destroy( conn ); - return 0; -} diff --git a/mongo-c-driver-v0.6/test/validate_test.c b/mongo-c-driver-v0.6/test/validate_test.c deleted file mode 100644 index 6e156ce..0000000 --- a/mongo-c-driver-v0.6/test/validate_test.c +++ /dev/null @@ -1,138 +0,0 @@ -/* validate.c */ - -#include "test.h" -#include "mongo.h" -#include "encoding.h" -#include -#include -#include - -#define BATCH_SIZE 10 - -static void make_small_invalid( bson *out, int i ) { - bson_init( out ); - bson_append_new_oid( out, "$_id" ); - bson_append_int( out, "x.foo", i ); - bson_finish( out ); -} - -int main() { - mongo conn[1]; - bson b, empty; - mongo_cursor cursor[1]; - unsigned char not_utf8[3]; - int result = 0; - const char *ns = "test.c.validate"; - - int i=0, j=0; - bson bs[BATCH_SIZE]; - bson *bp[BATCH_SIZE]; - - not_utf8[0] = 0xC0; - not_utf8[1] = 0xC0; - not_utf8[2] = '\0'; - - INIT_SOCKETS_FOR_WINDOWS; - - if ( mongo_connect( conn, TEST_SERVER, 27017 ) ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - /* Test checking for finished bson. */ - bson_init( &b ); - bson_append_int( &b, "foo", 1 ); - ASSERT( mongo_insert( conn, "test.foo", &b, NULL ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_NOT_FINISHED ); - - /* Test valid keys. */ - bson_init( &b ); - result = bson_append_string( &b , "a.b" , "17" ); - ASSERT( result == BSON_OK ); - - ASSERT( b.err & BSON_FIELD_HAS_DOT ); - - /* Don't set INIT dollar if deb ref fields are being used. */ - result = bson_append_string( &b , "$id" , "17" ); - ASSERT( result == BSON_OK ); - ASSERT( !(b.err & BSON_FIELD_INIT_DOLLAR) ); - - result = bson_append_string( &b , "$ref" , "17" ); - ASSERT( result == BSON_OK ); - ASSERT( !(b.err & BSON_FIELD_INIT_DOLLAR) ); - - result = bson_append_string( &b , "$db" , "17" ); - ASSERT( result == BSON_OK ); - ASSERT( !(b.err & BSON_FIELD_INIT_DOLLAR) ); - - result = bson_append_string( &b , "$ab" , "17" ); - ASSERT( result == BSON_OK ); - ASSERT( b.err & BSON_FIELD_INIT_DOLLAR ); - - result = bson_append_string( &b , "ab" , "this is valid utf8" ); - ASSERT( result == BSON_OK ); - ASSERT( ! ( b.err & BSON_NOT_UTF8 ) ); - - result = bson_append_string( &b , ( const char * )not_utf8, "valid" ); - ASSERT( result == BSON_ERROR ); - ASSERT( b.err & BSON_NOT_UTF8 ); - - ASSERT( bson_finish( &b ) == BSON_ERROR ); - ASSERT( b.err & BSON_FIELD_HAS_DOT ); - ASSERT( b.err & BSON_FIELD_INIT_DOLLAR ); - ASSERT( b.err & BSON_NOT_UTF8 ); - - result = mongo_insert( conn, ns, &b, NULL ); - ASSERT( result == MONGO_ERROR ); - ASSERT( conn->err & MONGO_BSON_NOT_FINISHED ); - - result = mongo_update( conn, ns, bson_empty( &empty ), &b, 0, NULL ); - ASSERT( result == MONGO_ERROR ); - ASSERT( conn->err & MONGO_BSON_NOT_FINISHED ); - - mongo_cursor_init( cursor, conn, "test.cursors" ); - mongo_cursor_set_query( cursor, &b ); - result = mongo_cursor_next( cursor ); - ASSERT( result == MONGO_ERROR ); - ASSERT( cursor->err & MONGO_CURSOR_BSON_ERROR ); - ASSERT( cursor->conn->err & MONGO_BSON_NOT_FINISHED ); - - bson_destroy( &b ); - - /* Test valid strings. */ - bson_init( & b ); - result = bson_append_string( &b , "foo" , "bar" ); - ASSERT( result == BSON_OK ); - ASSERT( b.err == 0 ); - - result = bson_append_string( &b , "foo" , ( const char * )not_utf8 ); - ASSERT( result == BSON_ERROR ); - ASSERT( b.err & BSON_NOT_UTF8 ); - - b.err = 0; - ASSERT( b.err == 0 ); - - result = bson_append_regex( &b , "foo" , ( const char * )not_utf8, "s" ); - ASSERT( result == BSON_ERROR ); - ASSERT( b.err & BSON_NOT_UTF8 ); - - for ( j=0; j < BATCH_SIZE; j++ ) - bp[j] = &bs[j]; - - for ( j=0; j < BATCH_SIZE; j++ ) - make_small_invalid( &bs[j], i ); - - result = mongo_insert_batch( conn, ns, (const bson **)bp, BATCH_SIZE, NULL, 0 ); - ASSERT( result == MONGO_ERROR ); - ASSERT( conn->err == MONGO_BSON_INVALID ); - - for ( j=0; j < BATCH_SIZE; j++ ) - bson_destroy( &bs[j] ); - - mongo_cmd_drop_db( conn, "test" ); - mongo_disconnect( conn ); - - mongo_destroy( conn ); - - return 0; -} diff --git a/mongo-c-driver-v0.6/test/write_concern_test.c b/mongo-c-driver-v0.6/test/write_concern_test.c deleted file mode 100644 index e7b7a2d..0000000 --- a/mongo-c-driver-v0.6/test/write_concern_test.c +++ /dev/null @@ -1,284 +0,0 @@ -/* write_concern_test.c */ - -#include "test.h" -#include "mongo.h" -#include -#include -#include - -/* TODO remove and add mongo_create_collection to the public API. */ -void create_capped_collection( mongo *conn ) { - mongo_cmd_drop_collection( conn, "test", "wc", NULL ); - mongo_create_capped_collection( conn, "test", "wc", 1000000, 0, NULL ); -} - -void test_batch_insert_with_continue( mongo *conn ) { - bson *objs[5]; - bson *objs2[5]; - bson empty; - int i; - - mongo_cmd_drop_collection( conn, TEST_DB, TEST_COL, NULL ); - mongo_create_simple_index( conn, TEST_NS, "n", MONGO_INDEX_UNIQUE, NULL ); - - for( i=0; i<5; i++ ) { - objs[i] = bson_malloc( sizeof( bson ) ); - bson_init( objs[i] ); - bson_append_int( objs[i], "n", i ); - bson_finish( objs[i] ); - } - - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs, 5, - NULL, 0 ) == MONGO_OK ); - - ASSERT( mongo_count( conn, TEST_DB, TEST_COL, - bson_empty( &empty ) ) == 5 ); - - /* Add one duplicate value for n. */ - objs2[0] = bson_malloc( sizeof( bson ) ); - bson_init( objs2[0] ); - bson_append_int( objs2[0], "n", 1 ); - bson_finish( objs2[0] ); - - /* Add n for 6 - 9. */ - for( i = 1; i < 5; i++ ) { - objs2[i] = bson_malloc( sizeof( bson ) ); - bson_init( objs2[i] ); - bson_append_int( objs2[i], "n", i + 5 ); - bson_finish( objs2[i] ); - } - - /* Without continue on error, will fail immediately. */ - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs2, 5, - NULL, 0 ) == MONGO_OK ); - ASSERT( mongo_count( conn, TEST_DB, TEST_COL, - bson_empty( &empty ) ) == 5 ); - - /* With continue on error, will insert four documents. */ - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs2, 5, - NULL, MONGO_CONTINUE_ON_ERROR ) == MONGO_OK ); - ASSERT( mongo_count( conn, TEST_DB, TEST_COL, - bson_empty( &empty ) ) == 9 ); - - for( i=0; i<5; i++ ) { - bson_destroy( objs2[i] ); - bson_free( objs2[i] ); - - bson_destroy( objs[i] ); - bson_free( objs[i] ); - } -} - -/* We can test write concern for update - * and remove by doing operations on a capped collection. */ -void test_update_and_remove( mongo *conn ) { - mongo_write_concern wc[1]; - bson *objs[5]; - bson query[1], update[1]; - bson empty; - int i; - - create_capped_collection( conn ); - - for( i=0; i<5; i++ ) { - objs[i] = bson_malloc( sizeof( bson ) ); - bson_init( objs[i] ); - bson_append_int( objs[i], "n", i ); - bson_finish( objs[i] ); - } - - ASSERT( mongo_insert_batch( conn, "test.wc", (const bson **)objs, 5, - NULL, 0 ) == MONGO_OK ); - - ASSERT( mongo_count( conn, "test", "wc", bson_empty( &empty ) ) == 5 ); - - bson_init( query ); - bson_append_int( query, "n", 2 ); - bson_finish( query ); - - ASSERT( mongo_find_one( conn, "test.wc", query, bson_empty( &empty ), NULL ) == MONGO_OK ); - - bson_init( update ); - bson_append_start_object( update, "$set" ); - bson_append_string( update, "n", "a big long string" ); - bson_append_finish_object( update ); - bson_finish( update ); - - /* Update will appear to succeed with no write concern specified, but doesn't. */ - ASSERT( mongo_find_one( conn, "test.wc", query, bson_empty( &empty ), NULL ) == MONGO_OK ); - ASSERT( mongo_update( conn, "test.wc", query, update, 0, NULL ) == MONGO_OK ); - ASSERT( mongo_find_one( conn, "test.wc", query, bson_empty( &empty ), NULL ) == MONGO_OK ); - - /* Remove will appear to succeed with no write concern specified, but doesn't. */ - ASSERT( mongo_remove( conn, "test.wc", query, NULL ) == MONGO_OK ); - ASSERT( mongo_find_one( conn, "test.wc", query, bson_empty( &empty ), NULL ) == MONGO_OK ); - - mongo_write_concern_init( wc ); - wc->w = 1; - mongo_write_concern_finish( wc ); - - mongo_clear_errors( conn ); - ASSERT( mongo_update( conn, "test.wc", query, update, 0, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "failing update: objects in a capped ns cannot grow" ); - - mongo_clear_errors( conn ); - ASSERT( mongo_remove( conn, "test.wc", query, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "can't remove from a capped collection" ); - - mongo_write_concern_destroy( wc ); - bson_destroy( query ); - bson_destroy( update ); - for( i=0; i<5; i++ ) { - bson_destroy( objs[i] ); - bson_free( objs[i] ); - } -} - -void test_write_concern_input( mongo *conn ) { - mongo_write_concern wc[1], wcbad[1]; - bson b[1]; - - mongo_cmd_drop_collection( conn, TEST_DB, TEST_COL, NULL ); - - bson_init( b ); - bson_append_new_oid( b, "_id" ); - bson_finish( b ); - - mongo_write_concern_init( wc ); - wc->w = 1; - - /* Failure to finish write concern object. */ - ASSERT( mongo_insert( conn, TEST_NS, b, wc ) != MONGO_OK ); - ASSERT( conn->err == MONGO_WRITE_CONCERN_INVALID ); - ASSERT_EQUAL_STRINGS( conn->errstr, - "Must call mongo_write_concern_finish() before using *write_concern." ); - - mongo_write_concern_finish( wc ); - - /* Use a bad write concern. */ - mongo_clear_errors( conn ); - mongo_write_concern_init( wcbad ); - wcbad->w = 2; - mongo_write_concern_finish( wcbad ); - mongo_set_write_concern( conn, wcbad ); - ASSERT( mongo_insert( conn, TEST_NS, b, NULL ) != MONGO_OK ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "norepl" ); - - /* Ensure that supplied write concern overrides default. */ - mongo_clear_errors( conn ); - ASSERT( mongo_insert( conn, TEST_NS, b, wc ) != MONGO_OK ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->errstr, "See conn->lasterrstr for details." ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "E11000 duplicate key error index" ); - ASSERT( conn->lasterrcode == 11000 ); - - conn->write_concern = NULL; - mongo_write_concern_destroy( wc ); - mongo_write_concern_destroy( wcbad ); -} - -void test_insert( mongo *conn ) { - mongo_write_concern wc[1]; - bson b[1], b2[1], b3[1], b4[1], empty[1]; - bson *objs[2]; - - mongo_cmd_drop_collection( conn, TEST_DB, TEST_COL, NULL ); - - mongo_write_concern_init( wc ); - wc->w = 1; - mongo_write_concern_finish( wc ); - - bson_init( b4 ); - bson_append_string( b4, "foo", "bar" ); - bson_finish( b4 ); - - ASSERT( mongo_insert( conn, TEST_NS, b4, wc ) == MONGO_OK ); - - ASSERT( mongo_remove( conn, TEST_NS, bson_empty( empty ), wc ) == MONGO_OK ); - - bson_init( b ); - bson_append_new_oid( b, "_id" ); - bson_finish( b ); - - ASSERT( mongo_insert( conn, TEST_NS, b, NULL ) == MONGO_OK ); - - /* This fails but returns OK because it doesn't use a write concern. */ - ASSERT( mongo_insert( conn, TEST_NS, b, NULL ) == MONGO_OK ); - - ASSERT( mongo_insert( conn, TEST_NS, b, wc ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->errstr, "See conn->lasterrstr for details." ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "E11000 duplicate key error index" ); - ASSERT( conn->lasterrcode == 11000 ); - mongo_clear_errors( conn ); - - /* Still fails but returns OK because it doesn't use a write concern. */ - ASSERT( mongo_insert( conn, TEST_NS, b, NULL ) == MONGO_OK ); - - /* But not when we set a default write concern on the conn. */ - mongo_set_write_concern( conn, wc ); - ASSERT( mongo_insert( conn, TEST_NS, b, NULL ) != MONGO_OK ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->errstr, "See conn->lasterrstr for details." ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "E11000 duplicate key error index" ); - ASSERT( conn->lasterrcode == 11000 ); - - /* Now test batch insert. */ - bson_init( b2 ); - bson_append_new_oid( b2, "_id" ); - bson_finish( b2 ); - - bson_init( b3 ); - bson_append_new_oid( b3, "_id" ); - bson_finish( b3 ); - - objs[0] = b2; - objs[1] = b3; - - /* Insert two new documents by insert_batch. */ - conn->write_concern = NULL; - ASSERT( mongo_count( conn, TEST_DB, TEST_COL, bson_empty( empty ) ) == 1 ); - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs, 2, NULL, 0 ) == MONGO_OK ); - ASSERT( mongo_count( conn, TEST_DB, TEST_COL, bson_empty( empty ) ) == 3 ); - - /* This should definitely fail if we try again with write concern. */ - mongo_clear_errors( conn ); - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs, 2, wc, 0 ) == MONGO_ERROR ); - ASSERT( conn->err == MONGO_WRITE_ERROR ); - ASSERT_EQUAL_STRINGS( conn->errstr, "See conn->lasterrstr for details." ); - ASSERT_EQUAL_STRINGS( conn->lasterrstr, "E11000 duplicate key error index" ); - ASSERT( conn->lasterrcode == 11000 ); - - /* But it will succeed without the write concern set. */ - ASSERT( mongo_insert_batch( conn, TEST_NS, (const bson **)objs, 2, NULL, 0 ) == MONGO_OK ); - - bson_destroy( b ); - bson_destroy( b2 ); - bson_destroy( b3 ); - mongo_write_concern_destroy( wc ); -} - -int main() { - mongo conn[1]; - char version[10]; - - INIT_SOCKETS_FOR_WINDOWS; - - if( mongo_connect( conn, TEST_SERVER, 27017 ) != MONGO_OK ) { - printf( "failed to connect\n" ); - exit( 1 ); - } - - test_insert( conn ); - if( mongo_get_server_version( version ) != -1 && version[0] != '1' ) { - test_write_concern_input( conn ); - test_update_and_remove( conn ); - test_batch_insert_with_continue( conn ); - } - - mongo_destroy( conn ); - return 0; -} From 5d8aee52c4ccaea32c692031a1fae174db9fd1ba Mon Sep 17 00:00:00 2001 From: Jason Petersen Date: Wed, 22 Jan 2014 18:18:58 -0700 Subject: [PATCH 002/239] Add back mongo-c-driver as submodule, lock to v0.6 Slightly better dependency management, easier to update, etc. --- .gitmodules | 3 +++ Makefile | 2 +- mongo-c-driver | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 mongo-c-driver diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4941792 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mongo-c-driver"] + path = mongo-c-driver + url = git@github.com:mongodb/mongo-c-driver.git diff --git a/Makefile b/Makefile index 40e7947..023998a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ MODULE_big = mongo_fdw # environment object file. # -MONGO_DRIVER = mongo-c-driver-v0.6 +MONGO_DRIVER = mongo-c-driver MONGO_PATH = $(MONGO_DRIVER)/src MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os diff --git a/mongo-c-driver b/mongo-c-driver new file mode 160000 index 0000000..013fe75 --- /dev/null +++ b/mongo-c-driver @@ -0,0 +1 @@ +Subproject commit 013fe75d6920afcfa016b4fd9bb7d0f2bf41da6e From 3d3d363ca37f8f9623c23257967c630e50dda49d Mon Sep 17 00:00:00 2001 From: Jason Petersen Date: Wed, 22 Jan 2014 18:47:46 -0700 Subject: [PATCH 003/239] Update README to explain submodule initialization To help transition users to the new way. --- README | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README b/README index 72c03dc..e70d05e 100644 --- a/README +++ b/README @@ -7,6 +7,18 @@ http://www.citusdata.com/blog/51-run-sql-on-mongodb. Please also note that this version of mongo_fdw only works with PostgreSQL 9.2. +Checking Out +------------ + +mongo_fdw depends on mongo-c-driver and includes it as a git submodule. If you +are cloning this repository for the first time, be sure to pass the --recursive +option to git clone in order to initialize the driver submodule to a useable +state. + +If have checked out this project before and for some reason your submodule is +not up-to-date, run git submodule update --init. + + Building -------- From 22a6f440f33314c73f3961313301a877534dc8fc Mon Sep 17 00:00:00 2001 From: Jason Petersen Date: Thu, 23 Jan 2014 11:17:38 -0700 Subject: [PATCH 004/239] Switch to relative URL for submodule This'll let people use whatever protocol (git, ssh, http, etc.) they want when checking out the project and the submodule will automatically use the same. See [this blog post][]. [this blog post]: http://blog.tremily.us/posts/Relative_submodules/ --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 4941792..8da9629 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "mongo-c-driver"] path = mongo-c-driver - url = git@github.com:mongodb/mongo-c-driver.git + url = ../../mongodb/mongo-c-driver.git From eb21c86013ba9728f53f560b52fc4d5532ecc196 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Tue, 13 May 2014 17:10:48 +0300 Subject: [PATCH 005/239] Initial code needed to output json. --- mongo_fdw.c | 54 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index daa5438..386f5bc 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -33,6 +33,8 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/memutils.h" +//#include "utils/json.h" +#include "utils/jsonapi.h" #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" @@ -75,6 +77,13 @@ static int MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, double *totalRowCount, double *totalDeadRowCount); +/* the null action object used for pure validation */ +static JsonSemAction nullSemAction = +{ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL +}; + /* declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -851,8 +860,17 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, 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_OBJECT) + if (bsonType == BSON_OBJECT && columnTypeId != JSONOID) { bson subObject; bson_iterator_subobject(&bsonIterator, &subObject); @@ -861,11 +879,6 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, continue; } - /* look up the corresponding column for this bson key */ - hashKey = (void *) bsonFullKey; - columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, - HASH_FIND, &handleFound); - /* if no corresponding column or null bson value, continue */ if (columnMapping == NULL || bsonType == BSON_NULL) { @@ -873,9 +886,6 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, } /* check if columns have compatible types */ - columnTypeId = columnMapping->columnTypeId; - columnArrayTypeId = columnMapping->columnArrayTypeId; - if (OidIsValid(columnArrayTypeId) && bsonType == BSON_ARRAY) { compatibleTypes = true; @@ -956,7 +966,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } break; } - case NAMEOID: + case NAMEOID: { /* * We currently overload the NAMEOID type to represent the BSON @@ -979,6 +989,14 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } break; } + case JSONOID: + { + if (bsonType == BSON_OBJECT || bsonType == BSON_ARRAY) + { + compatibleTypes = true; + } + break; + } default: { /* @@ -1134,7 +1152,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = CStringGetTextDatum(value); break; } - case NAMEOID: + case NAMEOID: { char value[NAMEDATALEN]; Datum valueDatum = 0; @@ -1167,6 +1185,18 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = TimestampGetDatum(timestamp); break; } + case JSONOID: + { + char *json = "{\"hello\": \"world!\"}"; // TODO + text *result = cstring_to_text(json); + JsonLexContext *lex; + + /* validate it */ + lex = makeJsonLexContext(result, false); + pg_parse_json(lex, &nullSemAction); + columnValue = PointerGetDatum(result); + break; + } default: { ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), @@ -1396,7 +1426,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, } break; - } + } /* * The first targetRowCount sample rows are simply copied into the From c86e655659fc738bb43f61ab0ac73d3124bd0c39 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Tue, 13 May 2014 19:33:14 +0300 Subject: [PATCH 006/239] First working prototype. --- mongo_fdw.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 243 insertions(+), 2 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 386f5bc..a707a07 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1068,6 +1068,232 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) return columnValueDatum; } +void debug_my_bson_print_raw( const char *data , int depth ); + +void my_bson_print( const bson *b ) { + debug_my_bson_print_raw( b->data , 0 ); +} + +void debug_my_bson_print_raw( const char *data , int depth ) { + bson_iterator i; + const char *key; + int temp; + bson_timestamp_t ts; + char oidhex[25]; + bson scope; + bson_iterator_from_buffer( &i, data ); + + //elog(NOTICE, "i->first: %d", i.first ); + //elog(NOTICE, "i->cur: %d", *i.cur ); + //bson_type bsonType = bson_iterator_type(&i); + //elog(NOTICE, "bson type: %d", bsonType); + //bson_iterator_next( &i ); + //return; + + while ( bson_iterator_next( &i ) ) { + bson_type t = bson_iterator_type( &i ); + if ( t == 0 ) + break; + key = bson_iterator_key( &i ); + + for ( temp=0; temp<=depth; temp++ ) + elog(NOTICE, "\t" ); + elog(NOTICE, "%s : %d \t " , key , t ); + + switch ( t ) { + case BSON_DOUBLE: + elog(NOTICE, "%f" , bson_iterator_double( &i ) ); + break; + case BSON_STRING: + elog(NOTICE, "%s" , bson_iterator_string( &i ) ); + break; + case BSON_SYMBOL: + elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); + break; + case BSON_OID: + bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); + elog(NOTICE, "%s" , oidhex ); + break; + case BSON_BOOL: + elog(NOTICE, "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); + break; + case BSON_DATE: + elog(NOTICE, "%ld" , ( long int )bson_iterator_date( &i ) ); + break; + case BSON_BINDATA: + elog(NOTICE, "BSON_BINDATA" ); + break; + case BSON_UNDEFINED: + elog(NOTICE, "BSON_UNDEFINED" ); + break; + case BSON_NULL: + elog(NOTICE, "BSON_NULL" ); + break; + case BSON_REGEX: + elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex( &i ) ); + break; + case BSON_CODE: + elog(NOTICE, "BSON_CODE: %s", bson_iterator_code( &i ) ); + break; + case BSON_CODEWSCOPE: + elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); + bson_init( &scope ); + bson_iterator_code_scope( &i, &scope ); + elog(NOTICE, "\n\t SCOPE: " ); + my_bson_print( &scope ); + break; + case BSON_INT: + elog(NOTICE, "%d" , bson_iterator_int( &i ) ); + break; + case BSON_LONG: + elog(NOTICE, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); + break; + case BSON_TIMESTAMP: + ts = bson_iterator_timestamp( &i ); + elog(NOTICE, "i: %d, t: %d", ts.i, ts.t ); + break; + case BSON_OBJECT: + case BSON_ARRAY: + elog(NOTICE, "\n" ); + debug_my_bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); + break; + default: + bson_errprintf( "can't print type : %d\n" , t ); + } + elog(NOTICE, "\n" ); + } +} + +int append(char **dest, char *src, int length) { + length += strlen(src); + strcpy(*dest, src); + *dest = *dest + strlen(src); + return length; +} + +int print_json(char **buffer, int length, const char *data , int depth, + bool is_array ) { + bson_iterator i; + const char *key; + int temp; + bson_timestamp_t ts; + char oidhex[25]; + bson scope; + bson_iterator_from_buffer( &i, data ); + + char * buff = malloc(512); + + bool first_elem = true; + + while ( bson_iterator_next( &i ) ) { + if (!first_elem) { + length = append(buffer, ",", length); + } + + bson_type t = bson_iterator_type( &i ); + if ( t == 0 ) + break; + key = bson_iterator_key( &i ); + + /* + for ( temp=0; temp<=depth; temp++ ) + elog(NOTICE, "\t" ); + elog(NOTICE, "%s : %d \t " , key , t ); + */ + if (!is_array) { + sprintf(buff, "\"%s\":", key); + length = append(buffer, buff, length); + //elog(NOTICE, "%s", buff); + } + + switch ( t ) { + case BSON_DOUBLE: + //elog(NOTICE, "%f" , bson_iterator_double( &i ) ); + sprintf(buff, "%f", bson_iterator_double( &i ) ); + length = append(buffer, buff, length); + break; + case BSON_STRING: + //elog(NOTICE, "\"%s\"" , bson_iterator_string( &i ) ); + sprintf(buff, "\"%s\"" , bson_iterator_string( &i ) ); + length = append(buffer, buff, length); + break; + case BSON_SYMBOL: + elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); + break; + case BSON_OID: + bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); + //elog(NOTICE, "\"%s\"" , oidhex ); + sprintf(buff, "\"%s\"" , oidhex ); + length = append(buffer, buff, length); + break; + case BSON_BOOL: + elog(NOTICE, "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); + break; + case BSON_DATE: + elog(NOTICE, "%ld" , ( long int )bson_iterator_date( &i ) ); + break; + case BSON_BINDATA: + elog(NOTICE, "BSON_BINDATA" ); + break; + case BSON_UNDEFINED: + elog(NOTICE, "BSON_UNDEFINED" ); + break; + case BSON_NULL: + elog(NOTICE, "BSON_NULL" ); + break; + case BSON_REGEX: + elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex( &i ) ); + break; + case BSON_CODE: + elog(NOTICE, "BSON_CODE: %s", bson_iterator_code( &i ) ); + break; + /*case BSON_CODEWSCOPE: + elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); + bson_init( &scope ); + bson_iterator_code_scope( &i, &scope ); + elog(NOTICE, "\n\t SCOPE: " ); + my_bson_print( &scope ); + break;*/ + case BSON_INT: + //elog(NOTICE, "%d" , bson_iterator_int( &i ) ); + sprintf(buff, "%d" , bson_iterator_int( &i ) ); + length = append(buffer, buff, length); + break; + case BSON_LONG: + //elog(NOTICE, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); + sprintf(buff, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); + length = append(buffer, buff, length); + break; + case BSON_TIMESTAMP: + ts = bson_iterator_timestamp( &i ); + elog(NOTICE, "i: %d, t: %d", ts.i, ts.t ); + break; + case BSON_OBJECT: + //elog(NOTICE, "{"); + length = append(buffer, "{", length); + length = print_json(buffer, length, bson_iterator_value( &i ) , + depth + 1, false ); + //elog(NOTICE, "}"); + length = append(buffer, "}", length); + break; + case BSON_ARRAY: + //elog(NOTICE, "["); + length = append(buffer, "[", length); + length = print_json(buffer, length, bson_iterator_value( &i ) , + depth + 1, true ); + //elog(NOTICE, "]"); + length = append(buffer, "]", length); + break; + default: + bson_errprintf( "can't print type : %d\n" , t ); + } + //elog(NOTICE, "," ); + first_elem = false; + } + //free(buff); + return length; +} + /* * ColumnValue uses column type information to read the current value pointed to @@ -1187,14 +1413,29 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case JSONOID: { - char *json = "{\"hello\": \"world!\"}"; // TODO - text *result = cstring_to_text(json); + //char *json = "{\"hello\": \"world!\"}"; // TODO + char *buffer = malloc(4096); + char *bptr = buffer + 1; + + buffer[0] = '['; + + int length = print_json(&bptr, 1, + bson_iterator_value(bsonIterator), 0, true); + //elog(NOTICE, "length: %d", length); + buffer[length] = ']'; + buffer[length + 1] = '\0'; + //elog(NOTICE, ">>%s<<", buffer); + + //my_bson_print_raw(bsonIterator->cur - 4, 0); + + text *result = cstring_to_text(buffer); JsonLexContext *lex; /* validate it */ lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); columnValue = PointerGetDatum(result); + //free(buffer); break; } default: From 1819c78efbd2ad0ff5b6e7b8c38b496fdd9fe329 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Tue, 13 May 2014 20:46:37 +0300 Subject: [PATCH 007/239] Added support for serializing objects. --- mongo_fdw.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index a707a07..2285b54 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1181,7 +1181,7 @@ int print_json(char **buffer, int length, const char *data , int depth, bson scope; bson_iterator_from_buffer( &i, data ); - char * buff = malloc(512); + char * buff = malloc(4096); bool first_elem = true; @@ -1413,20 +1413,31 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case JSONOID: { - //char *json = "{\"hello\": \"world!\"}"; // TODO - char *buffer = malloc(4096); + char *buffer = malloc(8184); char *bptr = buffer + 1; + char start_symbol, end_symbol; + bool is_array; + + bson_type type = bson_iterator_type(bsonIterator); + + if (type == BSON_ARRAY) { + start_symbol = '['; + end_symbol = ']'; + is_array = true; + } else if (type == BSON_OBJECT) { + start_symbol = '{'; + end_symbol = '}'; + is_array = false; + } else { + ereport(ERROR, (errmsg("cannot convert scolar to json"))); + } - buffer[0] = '['; + buffer[0] = start_symbol; int length = print_json(&bptr, 1, - bson_iterator_value(bsonIterator), 0, true); - //elog(NOTICE, "length: %d", length); - buffer[length] = ']'; + bson_iterator_value(bsonIterator), 0, is_array); + buffer[length] = end_symbol; buffer[length + 1] = '\0'; - //elog(NOTICE, ">>%s<<", buffer); - - //my_bson_print_raw(bsonIterator->cur - 4, 0); text *result = cstring_to_text(buffer); JsonLexContext *lex; From 2ac3c0175855581e4c1154f8902dd925c6940be5 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Tue, 13 May 2014 21:44:07 +0300 Subject: [PATCH 008/239] Updated the extension to work with mongo c driver v0.8. --- Makefile | 2 +- README.md | 2 +- mongo-c-driver | 2 +- mongo_fdw.c | 30 +++++++++++++++--------------- mongo_query.c | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 2db5765..c4bbaab 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ MODULE_big = mongo_fdw MONGO_DRIVER = mongo-c-driver MONGO_PATH = $(MONGO_DRIVER)/src MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ - $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os + $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) OBJS = mongo_fdw.o mongo_query.o $(MONGO_OBJS) diff --git a/README.md b/README.md index a8da584..027778d 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ PostgreSQL 9.2 or 9.3. Installation ------------ -The MongoDB FDW depends on the official MongoDB C Driver version 0.6 and +The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and includes it as a git submodule. If you are cloning this repository for the first time, be sure to pass the --recursive option to git clone in order to initialize the driver submodule to a useable diff --git a/mongo-c-driver b/mongo-c-driver index 013fe75..41562d5 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 013fe75d6920afcfa016b4fd9bb7d0f2bf41da6e +Subproject commit 41562d5cda9ca79af3d23a7463e5faf4f2a4e20a diff --git a/mongo_fdw.c b/mongo_fdw.c index daa5438..8dc9ece 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -340,7 +340,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, queryBuffer = SerializeDocument(queryDocument); /* only clean up the query struct, but not its data */ - bson_dispose(queryDocument); + bson_dealloc(queryDocument); /* we don't need to serialize column list as lists are copiable */ columnList = ColumnList(baserel); @@ -418,7 +418,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) addressName = mongoFdwOptions->addressName; portNumber = mongoFdwOptions->portNumber; - mongoConnection = mongo_create(); + mongoConnection = mongo_alloc(); mongo_init(mongoConnection); connectStatus = mongo_connect(mongoConnection, addressName, portNumber); @@ -427,7 +427,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) errorCode = (int32) mongoConnection->err; mongo_destroy(mongoConnection); - mongo_dispose(mongoConnection); + mongo_dealloc(mongoConnection); ereport(ERROR, (errmsg("could not connect to %s:%d", addressName, portNumber), errhint("Mongo driver connection error: %d", errorCode))); @@ -449,7 +449,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) mongoFdwOptions->collectionName); /* create cursor for collection name and set query */ - mongoCursor = mongo_cursor_create(); + mongoCursor = mongo_cursor_alloc(); mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); mongo_cursor_set_query(mongoCursor, queryDocument); @@ -561,7 +561,7 @@ MongoReScanForeignScan(ForeignScanState *scanState) /* close down the old cursor */ mongo_cursor_destroy(executionState->mongoCursor); - mongo_cursor_dispose(executionState->mongoCursor); + mongo_cursor_dealloc(executionState->mongoCursor); /* reconstruct full collection name */ foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); @@ -572,7 +572,7 @@ MongoReScanForeignScan(ForeignScanState *scanState) mongoFdwOptions->collectionName); /* reconstruct cursor for collection name and set query */ - mongoCursor = mongo_cursor_create(); + mongoCursor = mongo_cursor_alloc(); mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); mongo_cursor_set_query(mongoCursor, executionState->queryDocument); @@ -622,9 +622,9 @@ DeserializeDocument(Const *constant) Assert(constant->constlen > 0); Assert(constant->constisnull == false); - document = bson_create(); + document = bson_alloc(); bson_init_size(document, 0); - bson_init_finished_data(document, documentData); + bson_init_finished_data_with_copy(document, documentData); return document; } @@ -647,7 +647,7 @@ ForeignTableDocumentCount(Oid foreignTableId) /* resolve foreign table options; and connect to mongo server */ options = MongoGetOptions(foreignTableId); - mongoConnection = mongo_create(); + mongoConnection = mongo_alloc(); mongo_init(mongoConnection); status = mongo_connect(mongoConnection, options->addressName, options->portNumber); @@ -662,7 +662,7 @@ ForeignTableDocumentCount(Oid foreignTableId) } mongo_destroy(mongoConnection); - mongo_dispose(mongoConnection); + mongo_dealloc(mongoConnection); return documentCount; } @@ -855,7 +855,7 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, if (bsonType == BSON_OBJECT) { bson subObject; - bson_iterator_subobject(&bsonIterator, &subObject); + bson_iterator_subobject_init(&bsonIterator, &subObject, false); FillTupleSlot(&subObject, bsonFullKey, columnMappingHash, columnValues, columnNulls); continue; @@ -1193,14 +1193,14 @@ MongoFreeScanState(MongoFdwExecState *executionState) } bson_destroy(executionState->queryDocument); - bson_dispose(executionState->queryDocument); + bson_dealloc(executionState->queryDocument); mongo_cursor_destroy(executionState->mongoCursor); - mongo_cursor_dispose(executionState->mongoCursor); + mongo_cursor_dealloc(executionState->mongoCursor); /* also close the connection to mongo server */ mongo_destroy(executionState->mongoConnection); - mongo_dispose(executionState->mongoConnection); + mongo_dealloc(executionState->mongoConnection); } @@ -1322,7 +1322,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, queryBuffer = SerializeDocument(queryDocument); /* only clean up the query struct, but not its data */ - bson_dispose(queryDocument); + bson_dealloc(queryDocument); /* construct foreign plan with query document and column list */ foreignPrivateList = list_make2(queryBuffer, columnList); diff --git a/mongo_query.c b/mongo_query.c index fb2ac33..c818f6c 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -164,7 +164,7 @@ QueryDocument(Oid relationId, List *opExpressionList) bson *queryDocument = NULL; int documentStatus = BSON_OK; - queryDocument = bson_create(); + queryDocument = bson_alloc(); bson_init(queryDocument); /* @@ -241,7 +241,7 @@ QueryDocument(Oid relationId, List *opExpressionList) if (documentStatus != BSON_OK) { ereport(ERROR, (errmsg("could not create document for query"), - errhint("BSON error: %s", queryDocument->errstr))); + errhint("BSON error: %d", queryDocument->err))); } return queryDocument; From 5b430f289bfa84c2329d2a7262b095abd90e88be Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Wed, 14 May 2014 09:36:30 +0300 Subject: [PATCH 009/239] Removed some worthless code. --- mongo_fdw.c | 96 +---------------------------------------------------- 1 file changed, 1 insertion(+), 95 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 3b4f080..32b7142 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1068,101 +1068,6 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) return columnValueDatum; } -void debug_my_bson_print_raw( const char *data , int depth ); - -void my_bson_print( const bson *b ) { - debug_my_bson_print_raw( b->data , 0 ); -} - -void debug_my_bson_print_raw( const char *data , int depth ) { - bson_iterator i; - const char *key; - int temp; - bson_timestamp_t ts; - char oidhex[25]; - bson scope; - bson_iterator_from_buffer( &i, data ); - - //elog(NOTICE, "i->first: %d", i.first ); - //elog(NOTICE, "i->cur: %d", *i.cur ); - //bson_type bsonType = bson_iterator_type(&i); - //elog(NOTICE, "bson type: %d", bsonType); - //bson_iterator_next( &i ); - //return; - - while ( bson_iterator_next( &i ) ) { - bson_type t = bson_iterator_type( &i ); - if ( t == 0 ) - break; - key = bson_iterator_key( &i ); - - for ( temp=0; temp<=depth; temp++ ) - elog(NOTICE, "\t" ); - elog(NOTICE, "%s : %d \t " , key , t ); - - switch ( t ) { - case BSON_DOUBLE: - elog(NOTICE, "%f" , bson_iterator_double( &i ) ); - break; - case BSON_STRING: - elog(NOTICE, "%s" , bson_iterator_string( &i ) ); - break; - case BSON_SYMBOL: - elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); - break; - case BSON_OID: - bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); - elog(NOTICE, "%s" , oidhex ); - break; - case BSON_BOOL: - elog(NOTICE, "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); - break; - case BSON_DATE: - elog(NOTICE, "%ld" , ( long int )bson_iterator_date( &i ) ); - break; - case BSON_BINDATA: - elog(NOTICE, "BSON_BINDATA" ); - break; - case BSON_UNDEFINED: - elog(NOTICE, "BSON_UNDEFINED" ); - break; - case BSON_NULL: - elog(NOTICE, "BSON_NULL" ); - break; - case BSON_REGEX: - elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex( &i ) ); - break; - case BSON_CODE: - elog(NOTICE, "BSON_CODE: %s", bson_iterator_code( &i ) ); - break; - case BSON_CODEWSCOPE: - elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); - bson_init( &scope ); - bson_iterator_code_scope( &i, &scope ); - elog(NOTICE, "\n\t SCOPE: " ); - my_bson_print( &scope ); - break; - case BSON_INT: - elog(NOTICE, "%d" , bson_iterator_int( &i ) ); - break; - case BSON_LONG: - elog(NOTICE, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); - break; - case BSON_TIMESTAMP: - ts = bson_iterator_timestamp( &i ); - elog(NOTICE, "i: %d, t: %d", ts.i, ts.t ); - break; - case BSON_OBJECT: - case BSON_ARRAY: - elog(NOTICE, "\n" ); - debug_my_bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); - break; - default: - bson_errprintf( "can't print type : %d\n" , t ); - } - elog(NOTICE, "\n" ); - } -} int append(char **dest, char *src, int length) { length += strlen(src); @@ -1171,6 +1076,7 @@ int append(char **dest, char *src, int length) { return length; } + int print_json(char **buffer, int length, const char *data , int depth, bool is_array ) { bson_iterator i; From d3f8b83700270e3ae29df06748c35c831aae9a9b Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Wed, 14 May 2014 09:37:07 +0300 Subject: [PATCH 010/239] Updated mongo c driver to version 0.8.1. --- mongo-c-driver | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongo-c-driver b/mongo-c-driver index 41562d5..8f27c0f 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 41562d5cda9ca79af3d23a7463e5faf4f2a4e20a +Subproject commit 8f27c0fbc51ed3ab7920734b87e611bae8e13eaf From 02ceca1db1e072303e4bdc2c62f375c35b0af18a Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Wed, 14 May 2014 09:55:31 +0300 Subject: [PATCH 011/239] Changed print_json to use StringInfo. --- mongo_fdw.c | 93 +++++++++++++++-------------------------------------- 1 file changed, 26 insertions(+), 67 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 32b7142..682c971 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1069,68 +1069,45 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) } -int append(char **dest, char *src, int length) { - length += strlen(src); - strcpy(*dest, src); - *dest = *dest + strlen(src); - return length; -} - - -int print_json(char **buffer, int length, const char *data , int depth, - bool is_array ) { +void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) { bson_iterator i; const char *key; int temp; bson_timestamp_t ts; char oidhex[25]; + bool first_elem; + bson_type t; bson scope; bson_iterator_from_buffer( &i, data ); - char * buff = malloc(4096); - - bool first_elem = true; - + first_elem = true; while ( bson_iterator_next( &i ) ) { if (!first_elem) { - length = append(buffer, ",", length); + appendStringInfoChar(buffer, ','); } - bson_type t = bson_iterator_type( &i ); + t = bson_iterator_type( &i ); if ( t == 0 ) break; key = bson_iterator_key( &i ); - /* - for ( temp=0; temp<=depth; temp++ ) - elog(NOTICE, "\t" ); - elog(NOTICE, "%s : %d \t " , key , t ); - */ if (!is_array) { - sprintf(buff, "\"%s\":", key); - length = append(buffer, buff, length); - //elog(NOTICE, "%s", buff); + appendStringInfo(buffer, "\"%s\":", key); } switch ( t ) { case BSON_DOUBLE: - //elog(NOTICE, "%f" , bson_iterator_double( &i ) ); - sprintf(buff, "%f", bson_iterator_double( &i ) ); - length = append(buffer, buff, length); + appendStringInfo(buffer, "%f", bson_iterator_double(&i)); break; case BSON_STRING: - //elog(NOTICE, "\"%s\"" , bson_iterator_string( &i ) ); - sprintf(buff, "\"%s\"" , bson_iterator_string( &i ) ); - length = append(buffer, buff, length); + appendStringInfo(buffer, "\"%s\"", bson_iterator_string(&i)); break; case BSON_SYMBOL: elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); break; case BSON_OID: - bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); - //elog(NOTICE, "\"%s\"" , oidhex ); - sprintf(buff, "\"%s\"" , oidhex ); - length = append(buffer, buff, length); + bson_oid_to_string(bson_iterator_oid(&i), oidhex); + appendStringInfo(buffer, "\"%s\"", oidhex); break; case BSON_BOOL: elog(NOTICE, "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); @@ -1161,43 +1138,30 @@ int print_json(char **buffer, int length, const char *data , int depth, my_bson_print( &scope ); break;*/ case BSON_INT: - //elog(NOTICE, "%d" , bson_iterator_int( &i ) ); - sprintf(buff, "%d" , bson_iterator_int( &i ) ); - length = append(buffer, buff, length); + appendStringInfo(buffer, "%d", bson_iterator_int(&i)); break; case BSON_LONG: - //elog(NOTICE, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); - sprintf(buff, "%lld" , ( uint64_t )bson_iterator_long( &i ) ); - length = append(buffer, buff, length); + appendStringInfo(buffer, "%ld", (uint64_t)bson_iterator_long(&i)); break; case BSON_TIMESTAMP: ts = bson_iterator_timestamp( &i ); elog(NOTICE, "i: %d, t: %d", ts.i, ts.t ); break; case BSON_OBJECT: - //elog(NOTICE, "{"); - length = append(buffer, "{", length); - length = print_json(buffer, length, bson_iterator_value( &i ) , - depth + 1, false ); - //elog(NOTICE, "}"); - length = append(buffer, "}", length); + appendStringInfoChar(buffer, '{'); + print_json(buffer, bson_iterator_value(&i), depth + 1, false); + appendStringInfoChar(buffer, '}'); break; case BSON_ARRAY: - //elog(NOTICE, "["); - length = append(buffer, "[", length); - length = print_json(buffer, length, bson_iterator_value( &i ) , - depth + 1, true ); - //elog(NOTICE, "]"); - length = append(buffer, "]", length); + appendStringInfoChar(buffer, '['); + print_json(buffer, bson_iterator_value(&i), depth + 1, true); + appendStringInfoChar(buffer, ']'); break; default: bson_errprintf( "can't print type : %d\n" , t ); } - //elog(NOTICE, "," ); first_elem = false; } - //free(buff); - return length; } @@ -1319,13 +1283,13 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case JSONOID: { - char *buffer = malloc(8184); - char *bptr = buffer + 1; + JsonLexContext *lex; + text *result; char start_symbol, end_symbol; bool is_array; + StringInfo buffer = makeStringInfo(); bson_type type = bson_iterator_type(bsonIterator); - if (type == BSON_ARRAY) { start_symbol = '['; end_symbol = ']'; @@ -1338,21 +1302,16 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) ereport(ERROR, (errmsg("cannot convert scolar to json"))); } - buffer[0] = start_symbol; + appendStringInfoChar(buffer, start_symbol); + print_json(buffer, bson_iterator_value(bsonIterator), 0, is_array); + appendStringInfoChar(buffer, end_symbol); - int length = print_json(&bptr, 1, - bson_iterator_value(bsonIterator), 0, is_array); - buffer[length] = end_symbol; - buffer[length + 1] = '\0'; - - text *result = cstring_to_text(buffer); - JsonLexContext *lex; + result = cstring_to_text_with_len(buffer->data, buffer->len); /* validate it */ lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); columnValue = PointerGetDatum(result); - //free(buffer); break; } default: From e953061053a1933aa99f8be445911cff3ba5387d Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Wed, 14 May 2014 14:12:13 +0300 Subject: [PATCH 012/239] Added string escaping for json. --- mongo_fdw.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 682c971..437ce4b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1069,6 +1069,53 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) } +const char *escape_string(const char *string) { + StringInfo buff; + char *ptr; + int i, segement_start_idx, len; + bool need_escaping = false; + for (ptr = string; *ptr; ++ptr) { + if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ + || *ptr == '\\') { + need_escaping = true; + } + } + + if (!need_escaping) return string; + + //elog(NOTICE, "========================"); + //elog(NOTICE, "json to escape: %s", string); + //elog(NOTICE, "========================"); + + buff = makeStringInfo(); + len = strlen(string); + segement_start_idx = 0; + for (i = 0; i < len; ++i) { + if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' + || string[i] == '\t' || string[i] == '\\') { + if (segement_start_idx < i) { + appendBinaryStringInfo(buff, string + segement_start_idx, + i - segement_start_idx); + } + + appendStringInfoChar(buff, '\\'); + if (string[i] == '"') appendStringInfoChar(buff, '"'); + else if (string[i] == '\r') appendStringInfoChar(buff, 'r'); + else if (string[i] == '\n') appendStringInfoChar(buff, 'n'); + else if (string[i] == '\t') appendStringInfoChar(buff, 't'); + else if (string[i] == '\\') appendStringInfoChar(buff, '\\'); + + segement_start_idx = i + 1; + } + } + if (segement_start_idx < len) { + appendBinaryStringInfo(buff, string + segement_start_idx, + len - segement_start_idx); + } + return buff->data; +} + + void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) { bson_iterator i; const char *key; @@ -1100,7 +1147,8 @@ void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) appendStringInfo(buffer, "%f", bson_iterator_double(&i)); break; case BSON_STRING: - appendStringInfo(buffer, "\"%s\"", bson_iterator_string(&i)); + appendStringInfo(buffer, "\"%s\"", + escape_string(bson_iterator_string(&i))); break; case BSON_SYMBOL: elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); @@ -1110,7 +1158,8 @@ void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) appendStringInfo(buffer, "\"%s\"", oidhex); break; case BSON_BOOL: - elog(NOTICE, "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); + appendStringInfoString( + buffer, bson_iterator_bool(&i) ? "true" : "false"); break; case BSON_DATE: elog(NOTICE, "%ld" , ( long int )bson_iterator_date( &i ) ); @@ -1122,7 +1171,7 @@ void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) elog(NOTICE, "BSON_UNDEFINED" ); break; case BSON_NULL: - elog(NOTICE, "BSON_NULL" ); + appendStringInfoString(buffer, "null"); break; case BSON_REGEX: elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex( &i ) ); @@ -1306,6 +1355,8 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) print_json(buffer, bson_iterator_value(bsonIterator), 0, is_array); appendStringInfoChar(buffer, end_symbol); + //elog(NOTICE, "json to parse: %s", buffer->data); + result = cstring_to_text_with_len(buffer->data, buffer->len); /* validate it */ From 6b1afd2248113c84b23bfd581aaa2253f5c3f3f0 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Thu, 15 May 2014 12:58:19 +0300 Subject: [PATCH 013/239] Code style changes. --- mongo_fdw.c | 165 ++++++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 81 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 437ce4b..ab772d8 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1118,99 +1118,102 @@ const char *escape_string(const char *string) { void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) { bson_iterator i; - const char *key; - int temp; - bson_timestamp_t ts; - char oidhex[25]; + const char *key; + int temp; + bson_timestamp_t ts; + char oidhex[25]; bool first_elem; bson_type t; - bson scope; - bson_iterator_from_buffer( &i, data ); + bson scope; + bson_iterator_from_buffer( &i, data ); first_elem = true; - while ( bson_iterator_next( &i ) ) { - if (!first_elem) { + while (bson_iterator_next(&i)) + { + if (!first_elem) + { appendStringInfoChar(buffer, ','); } - t = bson_iterator_type( &i ); - if ( t == 0 ) - break; - key = bson_iterator_key( &i ); + t = bson_iterator_type(&i); + if (t == 0) break; + key = bson_iterator_key(&i); - if (!is_array) { + if (!is_array) + { appendStringInfo(buffer, "\"%s\":", key); } - switch ( t ) { - case BSON_DOUBLE: - appendStringInfo(buffer, "%f", bson_iterator_double(&i)); - break; - case BSON_STRING: - appendStringInfo(buffer, "\"%s\"", - escape_string(bson_iterator_string(&i))); - break; - case BSON_SYMBOL: - elog(NOTICE, "SYMBOL: %s" , bson_iterator_string( &i ) ); - break; - case BSON_OID: - bson_oid_to_string(bson_iterator_oid(&i), oidhex); - appendStringInfo(buffer, "\"%s\"", oidhex); - break; - case BSON_BOOL: - appendStringInfoString( - buffer, bson_iterator_bool(&i) ? "true" : "false"); - break; - case BSON_DATE: - elog(NOTICE, "%ld" , ( long int )bson_iterator_date( &i ) ); - break; - case BSON_BINDATA: - elog(NOTICE, "BSON_BINDATA" ); - break; - case BSON_UNDEFINED: - elog(NOTICE, "BSON_UNDEFINED" ); - break; - case BSON_NULL: - appendStringInfoString(buffer, "null"); - break; - case BSON_REGEX: - elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex( &i ) ); - break; - case BSON_CODE: - elog(NOTICE, "BSON_CODE: %s", bson_iterator_code( &i ) ); - break; - /*case BSON_CODEWSCOPE: - elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); - bson_init( &scope ); - bson_iterator_code_scope( &i, &scope ); - elog(NOTICE, "\n\t SCOPE: " ); - my_bson_print( &scope ); - break;*/ - case BSON_INT: - appendStringInfo(buffer, "%d", bson_iterator_int(&i)); - break; - case BSON_LONG: - appendStringInfo(buffer, "%ld", (uint64_t)bson_iterator_long(&i)); - break; - case BSON_TIMESTAMP: - ts = bson_iterator_timestamp( &i ); - elog(NOTICE, "i: %d, t: %d", ts.i, ts.t ); - break; - case BSON_OBJECT: - appendStringInfoChar(buffer, '{'); - print_json(buffer, bson_iterator_value(&i), depth + 1, false); - appendStringInfoChar(buffer, '}'); - break; - case BSON_ARRAY: - appendStringInfoChar(buffer, '['); - print_json(buffer, bson_iterator_value(&i), depth + 1, true); - appendStringInfoChar(buffer, ']'); - break; - default: - bson_errprintf( "can't print type : %d\n" , t ); - } + switch (t) + { + case BSON_DOUBLE: + appendStringInfo(buffer, "%f", bson_iterator_double(&i)); + break; + case BSON_STRING: + appendStringInfo(buffer, "\"%s\"", + escape_string(bson_iterator_string(&i))); + break; + case BSON_SYMBOL: + elog(NOTICE, "SYMBOL: %s", bson_iterator_string(&i)); + break; + case BSON_OID: + bson_oid_to_string(bson_iterator_oid(&i), oidhex); + appendStringInfo(buffer, "\"%s\"", oidhex); + break; + case BSON_BOOL: + appendStringInfoString( + buffer, bson_iterator_bool(&i) ? "true" : "false"); + break; + case BSON_DATE: + elog(NOTICE, "%ld" , (long int)bson_iterator_date(&i)); + break; + case BSON_BINDATA: + elog(NOTICE, "BSON_BINDATA"); + break; + case BSON_UNDEFINED: + elog(NOTICE, "BSON_UNDEFINED"); + break; + case BSON_NULL: + appendStringInfoString(buffer, "null"); + break; + case BSON_REGEX: + elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex(&i)); + break; + case BSON_CODE: + elog(NOTICE, "BSON_CODE: %s", bson_iterator_code(&i)); + break; + /*case BSON_CODEWSCOPE: + elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); + bson_init( &scope ); + bson_iterator_code_scope( &i, &scope ); + elog(NOTICE, "\n\t SCOPE: " ); + my_bson_print( &scope ); + break;*/ + case BSON_INT: + appendStringInfo(buffer, "%d", bson_iterator_int(&i)); + break; + case BSON_LONG: + appendStringInfo(buffer, "%ld", (uint64_t)bson_iterator_long(&i)); + break; + case BSON_TIMESTAMP: + ts = bson_iterator_timestamp(&i); + elog(NOTICE, "i: %d, t: %d", ts.i, ts.t); + break; + case BSON_OBJECT: + appendStringInfoChar(buffer, '{'); + print_json(buffer, bson_iterator_value(&i), depth + 1, false); + appendStringInfoChar(buffer, '}'); + break; + case BSON_ARRAY: + appendStringInfoChar(buffer, '['); + print_json(buffer, bson_iterator_value(&i), depth + 1, true); + appendStringInfoChar(buffer, ']'); + break; + default: + bson_errprintf("can't print type : %d\n" , t); + } first_elem = false; - } + } } From 7aa4145dccd497e082ec3807ce1e33f1b57598ca Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Sun, 25 May 2014 15:16:20 +0300 Subject: [PATCH 014/239] Added basic pgregress tests. --- .gitignore | 4 +++- Makefile | 4 ++++ test/expected/mongo_fdw.out | 48 +++++++++++++++++++++++++++++++++++++ test/sql/mongo_fdw.sql | 31 ++++++++++++++++++++++++ test/sql/mongo_fixture.json | 32 +++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 test/expected/mongo_fdw.out create mode 100644 test/sql/mongo_fdw.sql create mode 100644 test/sql/mongo_fixture.json diff --git a/.gitignore b/.gitignore index 6f21dc5..39fc481 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,10 @@ # Executables *.exe -*.out *.app *.i*86 *.x86_64 *.hex + +# pgregress results directory +results/ diff --git a/Makefile b/Makefile index b4543d8..ab4e23d 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,10 @@ OBJS = mongo_fdw.o mongo_query.o $(MONGO_OBJS) EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql +REGRESS = mongo_fdw +REGRESS_OPTS = --inputdir=test --outputdir=test \ + --load-extension=$(EXTENSION) + $(MONGO_DRIVER)/%.os: $(MAKE) -C $(MONGO_DRIVER) $*.os diff --git a/test/expected/mongo_fdw.out b/test/expected/mongo_fdw.out new file mode 100644 index 0000000..bc3ef38 --- /dev/null +++ b/test/expected/mongo_fdw.out @@ -0,0 +1,48 @@ +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < test/sql/mongo_fixture.json +-- +-- Basic types +CREATE FOREIGN TABLE countries ( + _id NAME, + name VARCHAR, + population INTEGER, + capital VARCHAR, + hdi FLOAT +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM countries; + _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) + +-- +-- Subfields and dates +CREATE FOREIGN TABLE country_elections ( + _id NAME, + "lastElections.type" VARCHAR, + "lastElections.date" TIMESTAMP +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections; + _id | lastElections.type | lastElections.date +--------------------------+--------------------+-------------------------- + 5381ccf9d6d81c8e8bf0434f | presedential | Sun May 25 00:00:00 2014 + 5381ccf9d6d81c8e8bf04350 | parliamentary | Sun Oct 09 00:00:00 2011 + 5381ccf9d6d81c8e8bf04351 | parliamentary | Sun Nov 28 00:00:00 2010 +(3 rows) + +-- +-- Arrays +CREATE FOREIGN TABLE main_exports ( + _id NAME, + "mainExports" TEXT[] +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM main_exports; + _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 enamelled or anodised) wire, cable","Sunflower seeds, whether or not broken"} +(3 rows) + diff --git a/test/sql/mongo_fdw.sql b/test/sql/mongo_fdw.sql new file mode 100644 index 0000000..8e435a5 --- /dev/null +++ b/test/sql/mongo_fdw.sql @@ -0,0 +1,31 @@ +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); + +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < test/sql/mongo_fixture.json + +-- +-- Basic types +CREATE FOREIGN TABLE countries ( + _id NAME, + name VARCHAR, + population INTEGER, + capital VARCHAR, + hdi FLOAT +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM countries; + +-- +-- Subfields and dates +CREATE FOREIGN TABLE country_elections ( + _id NAME, + "lastElections.type" VARCHAR, + "lastElections.date" TIMESTAMP +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections; + +-- +-- Arrays +CREATE FOREIGN TABLE main_exports ( + _id NAME, + "mainExports" TEXT[] +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM main_exports; diff --git a/test/sql/mongo_fixture.json b/test/sql/mongo_fixture.json new file mode 100644 index 0000000..e54bdca --- /dev/null +++ b/test/sql/mongo_fixture.json @@ -0,0 +1,32 @@ +[{ + "_id": {"$oid": "5381ccf9d6d81c8e8bf0434f"}, + "name": "Ukraine", + "population": 45590000, + "capital": "Kyiv", + "hdi": 0.74, + "lastElections": {"type": "presedential", "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": "presedential", "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 enamelled or anodised) wire, cable", + "Sunflower seeds, whether or not broken"] +}] From 89448c4a51f32a5ea06bd3805c5816bcefa0fe72 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Sun, 25 May 2014 15:56:19 +0300 Subject: [PATCH 015/239] Added regression test for JSON datatype. Also added code for outputing BSON dates to "MongoDB Extended JSON" [1] [1] http://docs.mongodb.org/manual/reference/mongodb-extended-json/ --- mongo_fdw.c | 3 ++- test/expected/mongo_fdw.out | 14 ++++++++++++++ test/sql/mongo_fdw.sql | 8 ++++++++ test/sql/mongo_fixture.json | 1 - 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index ab772d8..318a35a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1165,7 +1165,8 @@ void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) buffer, bson_iterator_bool(&i) ? "true" : "false"); break; case BSON_DATE: - elog(NOTICE, "%ld" , (long int)bson_iterator_date(&i)); + appendStringInfo(buffer, "{\"$date\":%ld}", + (long int)bson_iterator_date(&i)); break; case BSON_BINDATA: elog(NOTICE, "BSON_BINDATA"); diff --git a/test/expected/mongo_fdw.out b/test/expected/mongo_fdw.out index bc3ef38..35e35b2 100644 --- a/test/expected/mongo_fdw.out +++ b/test/expected/mongo_fdw.out @@ -46,3 +46,17 @@ SELECT * FROM main_exports; 5381ccf9d6d81c8e8bf04351 | {"Wine of fresh grapes, including fortified wines","Insulated (including enamelled or anodised) wire, cable","Sunflower seeds, whether or not broken"} (3 rows) +-- +-- JSON +CREATE FOREIGN TABLE country_elections_json ( + _id NAME, + "lastElections" JSON +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections_json; + _id | lastElections +--------------------------+--------------------------------------------------------- + 5381ccf9d6d81c8e8bf0434f | {"type":"presedential","date":{"$date":1400976000000}} + 5381ccf9d6d81c8e8bf04350 | {"type":"parliamentary","date":{"$date":1318118400000}} + 5381ccf9d6d81c8e8bf04351 | {"type":"parliamentary","date":{"$date":1290902400000}} +(3 rows) + diff --git a/test/sql/mongo_fdw.sql b/test/sql/mongo_fdw.sql index 8e435a5..2519b67 100644 --- a/test/sql/mongo_fdw.sql +++ b/test/sql/mongo_fdw.sql @@ -29,3 +29,11 @@ CREATE FOREIGN TABLE main_exports ( "mainExports" TEXT[] ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); SELECT * FROM main_exports; + +-- +-- JSON +CREATE FOREIGN TABLE country_elections_json ( + _id NAME, + "lastElections" JSON +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections_json; diff --git a/test/sql/mongo_fixture.json b/test/sql/mongo_fixture.json index e54bdca..c8fe62a 100644 --- a/test/sql/mongo_fixture.json +++ b/test/sql/mongo_fixture.json @@ -14,7 +14,6 @@ "population": 38540000, "capital": "Warsaw", "hdi": 0.821, - "lastElections": {"type": "presedential", "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", From c632ac480e6fffc79c32845a34488574fc5bc685 Mon Sep 17 00:00:00 2001 From: Kirill Spitsin Date: Sun, 25 May 2014 13:08:05 +0300 Subject: [PATCH 016/239] Code cleanup. --- mongo_fdw.c | 352 ++++++++++++++++++++++++++++------------------------ 1 file changed, 188 insertions(+), 164 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 318a35a..d18eb6e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -76,6 +76,8 @@ static bool MongoAnalyzeForeignTable(Relation relation, static int MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, double *totalRowCount, double *totalDeadRowCount); +const char * EscapeJsonString(const char *string); +void DumpJson(StringInfo buffer, const char *bsonData, bool isArray); /* the null action object used for pure validation */ static JsonSemAction nullSemAction = @@ -1069,155 +1071,6 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) } -const char *escape_string(const char *string) { - StringInfo buff; - char *ptr; - int i, segement_start_idx, len; - bool need_escaping = false; - for (ptr = string; *ptr; ++ptr) { - if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ - || *ptr == '\\') { - need_escaping = true; - } - } - - if (!need_escaping) return string; - - //elog(NOTICE, "========================"); - //elog(NOTICE, "json to escape: %s", string); - //elog(NOTICE, "========================"); - - buff = makeStringInfo(); - len = strlen(string); - segement_start_idx = 0; - for (i = 0; i < len; ++i) { - if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' - || string[i] == '\t' || string[i] == '\\') { - if (segement_start_idx < i) { - appendBinaryStringInfo(buff, string + segement_start_idx, - i - segement_start_idx); - } - - appendStringInfoChar(buff, '\\'); - if (string[i] == '"') appendStringInfoChar(buff, '"'); - else if (string[i] == '\r') appendStringInfoChar(buff, 'r'); - else if (string[i] == '\n') appendStringInfoChar(buff, 'n'); - else if (string[i] == '\t') appendStringInfoChar(buff, 't'); - else if (string[i] == '\\') appendStringInfoChar(buff, '\\'); - - segement_start_idx = i + 1; - } - } - if (segement_start_idx < len) { - appendBinaryStringInfo(buff, string + segement_start_idx, - len - segement_start_idx); - } - return buff->data; -} - - -void print_json(StringInfo buffer, const char *data , int depth, bool is_array ) { - bson_iterator i; - const char *key; - int temp; - bson_timestamp_t ts; - char oidhex[25]; - bool first_elem; - bson_type t; - bson scope; - bson_iterator_from_buffer( &i, data ); - - first_elem = true; - while (bson_iterator_next(&i)) - { - if (!first_elem) - { - appendStringInfoChar(buffer, ','); - } - - t = bson_iterator_type(&i); - if (t == 0) break; - key = bson_iterator_key(&i); - - if (!is_array) - { - appendStringInfo(buffer, "\"%s\":", key); - } - - switch (t) - { - case BSON_DOUBLE: - appendStringInfo(buffer, "%f", bson_iterator_double(&i)); - break; - case BSON_STRING: - appendStringInfo(buffer, "\"%s\"", - escape_string(bson_iterator_string(&i))); - break; - case BSON_SYMBOL: - elog(NOTICE, "SYMBOL: %s", bson_iterator_string(&i)); - break; - case BSON_OID: - bson_oid_to_string(bson_iterator_oid(&i), oidhex); - appendStringInfo(buffer, "\"%s\"", oidhex); - break; - case BSON_BOOL: - appendStringInfoString( - buffer, bson_iterator_bool(&i) ? "true" : "false"); - break; - case BSON_DATE: - appendStringInfo(buffer, "{\"$date\":%ld}", - (long int)bson_iterator_date(&i)); - break; - case BSON_BINDATA: - elog(NOTICE, "BSON_BINDATA"); - break; - case BSON_UNDEFINED: - elog(NOTICE, "BSON_UNDEFINED"); - break; - case BSON_NULL: - appendStringInfoString(buffer, "null"); - break; - case BSON_REGEX: - elog(NOTICE, "BSON_REGEX: %s", bson_iterator_regex(&i)); - break; - case BSON_CODE: - elog(NOTICE, "BSON_CODE: %s", bson_iterator_code(&i)); - break; - /*case BSON_CODEWSCOPE: - elog(NOTICE, "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); - bson_init( &scope ); - bson_iterator_code_scope( &i, &scope ); - elog(NOTICE, "\n\t SCOPE: " ); - my_bson_print( &scope ); - break;*/ - case BSON_INT: - appendStringInfo(buffer, "%d", bson_iterator_int(&i)); - break; - case BSON_LONG: - appendStringInfo(buffer, "%ld", (uint64_t)bson_iterator_long(&i)); - break; - case BSON_TIMESTAMP: - ts = bson_iterator_timestamp(&i); - elog(NOTICE, "i: %d, t: %d", ts.i, ts.t); - break; - case BSON_OBJECT: - appendStringInfoChar(buffer, '{'); - print_json(buffer, bson_iterator_value(&i), depth + 1, false); - appendStringInfoChar(buffer, '}'); - break; - case BSON_ARRAY: - appendStringInfoChar(buffer, '['); - print_json(buffer, bson_iterator_value(&i), depth + 1, true); - appendStringInfoChar(buffer, ']'); - break; - default: - bson_errprintf("can't print type : %d\n" , t); - } - first_elem = false; - } -} - - /* * ColumnValue uses column type information to read the current value pointed to * by the BSON iterator, and converts this value to the corresponding PostgreSQL @@ -1338,28 +1191,17 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { JsonLexContext *lex; text *result; - char start_symbol, end_symbol; bool is_array; StringInfo buffer = makeStringInfo(); bson_type type = bson_iterator_type(bsonIterator); - if (type == BSON_ARRAY) { - start_symbol = '['; - end_symbol = ']'; - is_array = true; - } else if (type == BSON_OBJECT) { - start_symbol = '{'; - end_symbol = '}'; - is_array = false; - } else { + if (type != BSON_ARRAY && type != BSON_OBJECT) + { ereport(ERROR, (errmsg("cannot convert scolar to json"))); } + is_array = (BSON_ARRAY == type); - appendStringInfoChar(buffer, start_symbol); - print_json(buffer, bson_iterator_value(bsonIterator), 0, is_array); - appendStringInfoChar(buffer, end_symbol); - - //elog(NOTICE, "json to parse: %s", buffer->data); + DumpJson(buffer, bson_iterator_value(bsonIterator), is_array); result = cstring_to_text_with_len(buffer->data, buffer->len); @@ -1382,6 +1224,188 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } +/* + * DumpJson 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 +DumpJson(StringInfo output, const char *bsonData, bool isArray) { + bson_iterator i; + const char *key; + bool isFirstElement; + char beginSymbol, endSymbol; + bson_type t; + bson_iterator_from_buffer(&i, bsonData); + + if (isArray) + { + beginSymbol = '['; + endSymbol = ']'; + } + else + { + beginSymbol = '{'; + endSymbol = '}'; + } + + appendStringInfoChar(output, beginSymbol); + + isFirstElement = true; + while (bson_iterator_next(&i)) + { + if (!isFirstElement) + { + appendStringInfoChar(output, ','); + } + + t = bson_iterator_type(&i); + if (t == 0) break; + key = bson_iterator_key(&i); + + if (!isArray) + { + appendStringInfo(output, "\"%s\":", key); + } + + switch (t) + { + case BSON_DOUBLE: + appendStringInfo(output, "%f", bson_iterator_double(&i)); + break; + case BSON_STRING: + appendStringInfo(output, "\"%s\"", + EscapeJsonString(bson_iterator_string(&i))); + break; + case BSON_SYMBOL: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("`symbol` BSON type is deprecated and " + "unsupported"), + errhint("Symbol: %s", bson_iterator_string(&i)))); + break; + case BSON_OID: + { + char oidhex[25]; + bson_oid_to_string(bson_iterator_oid(&i), oidhex); + appendStringInfo(output, "\"%s\"", oidhex); + break; + } + case BSON_BOOL: + appendStringInfoString( + output, bson_iterator_bool(&i) ? "true" : "false"); + break; + case BSON_DATE: + appendStringInfo(output, "{\"$date\":%ld}", + (long int)bson_iterator_date(&i)); + break; + case BSON_BINDATA: + /* It's possible to encode the data with base64 here. */ + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `binary data` BSON type " + "is not implemented"))); + break; + case BSON_UNDEFINED: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("`undefined` BSON type is deprecated " + "and unsupported"))); + break; + case BSON_NULL: + appendStringInfoString(output, "null"); + break; + case BSON_REGEX: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `regex` BSON type is " + "not implemented"), + errhint("Regex: %s", bson_iterator_regex(&i)))); + break; + case BSON_CODE: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `code` BSON type is " + "not implemented"), + errhint("Code: %s", bson_iterator_code(&i)))); + break; + case BSON_CODEWSCOPE: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `code with scope` BSON " + "type is not implemented"))); + break; + case BSON_INT: + appendStringInfo(output, "%d", bson_iterator_int(&i)); + break; + case BSON_LONG: + appendStringInfo(output, "%ld", (uint64_t)bson_iterator_long(&i)); + break; + case BSON_TIMESTAMP: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("internal `timestamp` BSON type is " + "and unsupported"))); + break; + case BSON_OBJECT: + DumpJson(output, bson_iterator_value(&i), false); + break; + case BSON_ARRAY: + DumpJson(output, bson_iterator_value(&i), true); + break; + default: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("unsupported BSON type: %d", t))); + } + isFirstElement = false; + } + appendStringInfoChar(output, endSymbol); +} + + +/* + * EscapeJsonString escapes a string for safe inclusion in JSON. + */ +const char * +EscapeJsonString(const char *string) { + StringInfo buffer; + const char *ptr; + int i, segmentStartIdx, len; + bool needsEscaping = false; + for (ptr = string; *ptr; ++ptr) { + if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ + || *ptr == '\\') { + needsEscaping = true; + } + } + + if (!needsEscaping) return string; + + buffer = makeStringInfo(); + len = strlen(string); + segmentStartIdx = 0; + for (i = 0; i < len; ++i) { + if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' + || string[i] == '\t' || string[i] == '\\') { + if (segmentStartIdx < i) { + appendBinaryStringInfo(buffer, string + segmentStartIdx, + i - segmentStartIdx); + } + + appendStringInfoChar(buffer, '\\'); + if (string[i] == '"') appendStringInfoChar(buffer, '"'); + else if (string[i] == '\r') appendStringInfoChar(buffer, 'r'); + else if (string[i] == '\n') appendStringInfoChar(buffer, 'n'); + else if (string[i] == '\t') appendStringInfoChar(buffer, 't'); + else if (string[i] == '\\') appendStringInfoChar(buffer, '\\'); + + segmentStartIdx = i + 1; + } + } + if (segmentStartIdx < len) { + appendBinaryStringInfo(buffer, string + segmentStartIdx, + len - segmentStartIdx); + } + return buffer->data; +} + + /* * MongoFreeScanState closes the cursor and connection to MongoDB, and reclaims * all Mongo related resources allocated for the foreign scan. From fca6bb0813f707f48c287260d4094e5a90a84d76 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:14:20 +0500 Subject: [PATCH 017/239] Connection Pooler for mongodb similar to PostgreSQL FDW. This will avoid multiple connection and disconnection to MongoDB Server. FDW's Options functions separated from "mongo_fdw.c" and placed in "option.c" file. The newly created connection's pooling functions placed into "connection.c" file. --- Makefile | 2 +- mongo_fdw.c | 245 ++++++---------------------------------------------- mongo_fdw.h | 28 +++++- 3 files changed, 56 insertions(+), 219 deletions(-) diff --git a/Makefile b/Makefile index b4543d8..82bf10c 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.o $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -OBJS = mongo_fdw.o mongo_query.o $(MONGO_OBJS) +OBJS = connection.o option.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql diff --git a/mongo_fdw.c b/mongo_fdw.c index daa5438..634e4d1 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -26,6 +26,7 @@ #include "optimizer/plancat.h" #include "optimizer/planmain.h" #include "optimizer/restrictinfo.h" +#include "storage/ipc.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" @@ -33,6 +34,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/memutils.h" +#include "storage/ipc.h" #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" @@ -40,7 +42,6 @@ /* Local functions forward declarations */ -static StringInfo OptionNamesString(Oid currentContextId); static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, @@ -57,8 +58,6 @@ static void MongoReScanForeignScan(ForeignScanState *scanState); static Const * SerializeDocument(bson *document); static bson * DeserializeDocument(Const *constant); static double ForeignTableDocumentCount(Oid foreignTableId); -static MongoFdwOptions * MongoGetOptions(Oid foreignTableId); -static char * MongoGetOptionValue(Oid foreignTableId, const char *optionName); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, @@ -74,14 +73,25 @@ static bool MongoAnalyzeForeignTable(Relation relation, static int MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, double *totalRowCount, double *totalDeadRowCount); +static void mongo_fdw_exit(int code, Datum arg); + +extern PGDLLEXPORT void _PG_init(void); /* declarations for dynamic loading */ PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(mongo_fdw_handler); -PG_FUNCTION_INFO_V1(mongo_fdw_validator); +/* + * Library load-time initalization, sets on_proc_exit() callback for + * backend shutdown. + */ +void +_PG_init(void) +{ + on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL)); +} /* * mongo_fdw_handler creates and returns a struct with pointers to foreign table @@ -91,7 +101,6 @@ Datum mongo_fdw_handler(PG_FUNCTION_ARGS) { FdwRoutine *fdwRoutine = makeNode(FdwRoutine); - fdwRoutine->GetForeignRelSize = MongoGetForeignRelSize; fdwRoutine->GetForeignPaths = MongoGetForeignPaths; fdwRoutine->GetForeignPlan = MongoGetForeignPlan; @@ -105,92 +114,13 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) PG_RETURN_POINTER(fdwRoutine); } - /* - * 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. + * Exit callback function. */ -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 = NULL; - - foreach(optionCell, optionList) - { - DefElem *optionDef = (DefElem *) lfirst(optionCell); - char *optionName = optionDef->defname; - bool optionValid = false; - - int32 optionIndex = 0; - for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) - { - const MongoValidOption *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(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 *optionValue = defGetString(optionDef); - int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); - (void) portNumber; - } - } - - PG_RETURN_VOID(); -} - - -/* - * OptionNamesString finds all options that are valid for the current context, - * and concatenates these option names in a comma separated string. - */ -static StringInfo -OptionNamesString(Oid currentContextId) +static void +mongo_fdw_exit(int code, Datum arg) { - StringInfo optionNamesString = makeStringInfo(); - bool firstOptionPrinted = false; - - int32 optionIndex = 0; - for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) - { - const MongoValidOption *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; + cleanup_connection(); } @@ -390,13 +320,11 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) { mongo *mongoConnection = NULL; mongo_cursor *mongoCursor = NULL; - int32 connectStatus = MONGO_ERROR; Oid foreignTableId = InvalidOid; List *columnList = NIL; HTAB *columnMappingHash = NULL; char *addressName = NULL; int32 portNumber = 0; - int32 errorCode = 0; StringInfo namespaceName = NULL; ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; @@ -418,22 +346,11 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) addressName = mongoFdwOptions->addressName; portNumber = mongoFdwOptions->portNumber; - mongoConnection = mongo_create(); - mongo_init(mongoConnection); - - connectStatus = mongo_connect(mongoConnection, addressName, portNumber); - if (connectStatus != MONGO_OK) - { - errorCode = (int32) mongoConnection->err; - - mongo_destroy(mongoConnection); - mongo_dispose(mongoConnection); - - ereport(ERROR, (errmsg("could not connect to %s:%d", addressName, portNumber), - errhint("Mongo driver connection error: %d", errorCode))); - } - - /* deserialize query document; and create column info hash */ + /* + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. + */ + mongoConnection = GetConnection(addressName, portNumber); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; Assert(list_length(foreignPrivateList) == 2); @@ -641,122 +558,18 @@ ForeignTableDocumentCount(Oid foreignTableId) MongoFdwOptions *options = NULL; mongo *mongoConnection = NULL; const bson *emptyQuery = NULL; - int32 status = MONGO_ERROR; double documentCount = 0.0; /* resolve foreign table options; and connect to mongo server */ options = MongoGetOptions(foreignTableId); - mongoConnection = mongo_create(); - mongo_init(mongoConnection); - - status = mongo_connect(mongoConnection, options->addressName, options->portNumber); - if (status == MONGO_OK) - { - documentCount = mongo_count(mongoConnection, options->databaseName, - options->collectionName, emptyQuery); - } - else - { - documentCount = -1.0; - } - - mongo_destroy(mongoConnection); - mongo_dispose(mongoConnection); - + mongoConnection = GetConnection(options->addressName, options->portNumber); + documentCount = mongo_count(mongoConnection, options->databaseName, + options->collectionName, emptyQuery); return documentCount; } -/* - * MongoGetOptions 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. - */ -static MongoFdwOptions * -MongoGetOptions(Oid foreignTableId) -{ - MongoFdwOptions *mongoFdwOptions = NULL; - char *addressName = NULL; - char *portName = NULL; - int32 portNumber = 0; - char *databaseName = NULL; - char *collectionName = NULL; - - addressName = MongoGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS); - if (addressName == NULL) - { - addressName = pstrdup(DEFAULT_IP_ADDRESS); - } - - portName = MongoGetOptionValue(foreignTableId, OPTION_NAME_PORT); - if (portName == NULL) - { - portNumber = DEFAULT_PORT_NUMBER; - } - else - { - portNumber = pg_atoi(portName, sizeof(int32), 0); - } - - databaseName = MongoGetOptionValue(foreignTableId, OPTION_NAME_DATABASE); - if (databaseName == NULL) - { - databaseName = pstrdup(DEFAULT_DATABASE_NAME); - } - - collectionName = MongoGetOptionValue(foreignTableId, OPTION_NAME_COLLECTION); - if (collectionName == NULL) - { - collectionName = get_rel_name(foreignTableId); - } - - mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); - mongoFdwOptions->addressName = addressName; - mongoFdwOptions->portNumber = portNumber; - mongoFdwOptions->databaseName = databaseName; - mongoFdwOptions->collectionName = collectionName; - - return mongoFdwOptions; -} - - -/* - * MongoGetOptionValue walks over foreign table and foreign server options, and - * looks for the option with the given name. If found, the function returns the - * option's value. - */ -static char * -MongoGetOptionValue(Oid foreignTableId, const char *optionName) -{ - ForeignTable *foreignTable = NULL; - ForeignServer *foreignServer = NULL; - List *optionList = NIL; - ListCell *optionCell = NULL; - char *optionValue = NULL; - - foreignTable = GetForeignTable(foreignTableId); - foreignServer = GetForeignServer(foreignTable->serverid); - - optionList = list_concat(optionList, foreignTable->options); - optionList = list_concat(optionList, foreignServer->options); - - foreach(optionCell, optionList) - { - DefElem *optionDef = (DefElem *) lfirst(optionCell); - char *optionDefName = optionDef->defname; - - if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) - { - optionValue = defGetString(optionDef); - break; - } - } - - return optionValue; -} - - /* * ColumnMappingHash creates a hash table that maps column names to column index * and types. This table helps us quickly translate BSON document key/values to @@ -1197,10 +1010,8 @@ MongoFreeScanState(MongoFdwExecState *executionState) mongo_cursor_destroy(executionState->mongoCursor); mongo_cursor_dispose(executionState->mongoCursor); - - /* also close the connection to mongo server */ - mongo_destroy(executionState->mongoConnection); - mongo_dispose(executionState->mongoConnection); + /* Release remote connection */ + ReleaseConnection(executionState->mongoConnection); } diff --git a/mongo_fdw.h b/mongo_fdw.h index 60947a2..52c5ef7 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -15,6 +15,8 @@ #include "bson.h" #include "mongo.h" +#include "postgres.h" +#include "utils/hsearch.h" #include "fmgr.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" @@ -22,6 +24,27 @@ #include "nodes/pg_list.h" #include "nodes/relation.h" #include "utils/timestamp.h" +#include "access/reloptions.h" +#include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "commands/explain.h" +#include "commands/vacuum.h" +#include "foreign/fdwapi.h" +#include "foreign/foreign.h" +#include "nodes/makefuncs.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/hsearch.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/memutils.h" + /* Defines for valid option names */ @@ -113,7 +136,6 @@ typedef struct ColumnMapping Oid columnTypeId; int32 columnTypeMod; Oid columnArrayTypeId; - } ColumnMapping; @@ -125,6 +147,10 @@ extern List * ColumnList(RelOptInfo *baserel); /* Function declarations for foreign data wrapper */ extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); +extern MongoFdwOptions * MongoGetOptions(Oid foreignTableId); +mongo* GetConnection(char *host, int32 port); +void cleanup_connection(void); +void ReleaseConnection(mongo *conn); #endif /* MONGO_FDW_H */ From 955350c200433888aa02b81b38c363685b21b830 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:15:58 +0500 Subject: [PATCH 018/239] Plemove DeserializeDocument and SerializeDocument functions which are causing the server to crash while query is used in any container, i.e in PlpgSQl function. --- mongo_fdw.c | 79 +++++++---------------------------------------------- 1 file changed, 10 insertions(+), 69 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 634e4d1..4b81a89 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -55,8 +55,6 @@ static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); static void MongoReScanForeignScan(ForeignScanState *scanState); -static Const * SerializeDocument(bson *document); -static bson * DeserializeDocument(Const *constant); static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, @@ -247,7 +245,6 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, List *foreignPrivateList = NIL; List *opExpressionList = NIL; bson *queryDocument = NULL; - Const *queryBuffer = NULL; List *columnList = NIL; /* @@ -267,22 +264,22 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, */ opExpressionList = ApplicableOpExpressionList(baserel); queryDocument = QueryDocument(foreignTableId, opExpressionList); - queryBuffer = SerializeDocument(queryDocument); - - /* only clean up the query struct, but not its data */ - bson_dispose(queryDocument); /* we don't need to serialize column list as lists are copiable */ columnList = ColumnList(baserel); /* construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(queryBuffer, columnList); + foreignPrivateList = list_make2(columnList, restrictionClauses); + + /* only clean up the query struct, but not its data */ + bson_dispose(queryDocument); /* create the foreign scan node */ foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, NIL, /* no expressions to evaluate */ foreignPrivateList); + return foreignScan; } @@ -328,7 +325,6 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) StringInfo namespaceName = NULL; ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; - Const *queryBuffer = NULL; bson *queryDocument = NULL; MongoFdwOptions *mongoFdwOptions = NULL; MongoFdwExecState *executionState = NULL; @@ -353,12 +349,12 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) mongoConnection = GetConnection(addressName, portNumber); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; - Assert(list_length(foreignPrivateList) == 2); + Assert(list_length(foreignPrivateList) == 1); + + columnList = list_nth(foreignPrivateList, 0); - queryBuffer = (Const *) linitial(foreignPrivateList); - queryDocument = DeserializeDocument(queryBuffer); + queryDocument = QueryDocument(foreignTableId, NIL); - columnList = (List *) lsecond(foreignPrivateList); columnMappingHash = ColumnMappingHash(foreignTableId, columnList); namespaceName = makeStringInfo(); @@ -496,57 +492,6 @@ MongoReScanForeignScan(ForeignScanState *scanState) executionState->mongoCursor = mongoCursor; } - -/* - * SerializeDocument serializes the document's data to a constant, as advised in - * foreign/fdwapi.h. Note that this function shallow-copies the document's data; - * and the caller should therefore not free it. - */ -static Const * -SerializeDocument(bson *document) -{ - Const *serializedDocument = NULL; - Datum documentDatum = 0; - - /* - * We access document data and wrap a datum around it. Note that even when - * we have an empty document, the document size can't be zero according to - * bson apis. - */ - const char *documentData = bson_data(document); - int32 documentSize = bson_buffer_size(document); - Assert(documentSize != 0); - - documentDatum = CStringGetDatum(documentData); - serializedDocument = makeConst(CSTRINGOID, -1, InvalidOid, documentSize, - documentDatum, false, false); - - return serializedDocument; -} - - -/* - * DeserializeDocument deserializes the constant to a bson document. For this, - * the function creates a document, and explicitly sets the document's data. - */ -static bson * -DeserializeDocument(Const *constant) -{ - bson *document = NULL; - Datum documentDatum = constant->constvalue; - char *documentData = DatumGetCString(documentDatum); - - Assert(constant->constlen > 0); - Assert(constant->constisnull == false); - - document = bson_create(); - bson_init_size(document, 0); - bson_init_finished_data(document, documentData); - - return document; -} - - /* * ForeignTableDocumentCount connects to the MongoDB server, and queries it for * the number of documents in the foreign collection. On success, the function @@ -1096,7 +1041,6 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, HTAB *columnMappingHash = NULL; mongo_cursor *mongoCursor = NULL; bson *queryDocument = NULL; - Const *queryBuffer = NULL; List *columnList = NIL; ForeignScanState *scanState = NULL; List *foreignPrivateList = NIL; @@ -1130,14 +1074,11 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, foreignTableId = RelationGetRelid(relation); queryDocument = QueryDocument(foreignTableId, NIL); - queryBuffer = SerializeDocument(queryDocument); + foreignPrivateList = list_make1(columnList); /* only clean up the query struct, but not its data */ bson_dispose(queryDocument); - /* construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(queryBuffer, columnList); - foreignScan = makeNode(ForeignScan); foreignScan->fdw_private = foreignPrivateList; From d8d8f48cb8050c36b4b755335d0880e4dc64343f Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:18:40 +0500 Subject: [PATCH 019/239] Write support (INSERT/UPDATE/DELETE). --- mongo_fdw.c | 654 ++++++++++++++++++++++++++++++++++++++++++++++---- mongo_fdw.h | 35 ++- mongo_query.c | 85 ++++--- mongo_query.h | 2 +- 4 files changed, 679 insertions(+), 97 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 4b81a89..75593eb 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -12,6 +12,12 @@ #include "postgres.h" #include "mongo_fdw.h" +#include "mongo_query.h" + +#include "postgres.h" +#include "bson.h" +#include "mongo_fdw.h" +#include "mongo_query.h" #include "access/reloptions.h" #include "catalog/pg_type.h" @@ -34,8 +40,27 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/memutils.h" -#include "storage/ipc.h" - +#include "access/sysattr.h" +#include "commands/defrem.h" +#include "commands/explain.h" +#include "commands/vacuum.h" +#include "foreign/fdwapi.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/cost.h" +#include "optimizer/pathnode.h" +#include "optimizer/paths.h" +#include "optimizer/planmain.h" +#include "optimizer/prep.h" +#include "optimizer/restrictinfo.h" +#include "optimizer/var.h" +#include "parser/parsetree.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" #endif @@ -55,16 +80,56 @@ static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); static void MongoReScanForeignScan(ForeignScanState *scanState); + +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); + +static void MongoAddForeignUpdateTargets(Query *parsetree, + RangeTblEntry *target_rte, + Relation target_relation); + +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); + +/* local functions */ static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls); static bool ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId); static Datum ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId); static Datum ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod); -static void MongoFreeScanState(MongoFdwExecState *executionState); +static void MongoFreeScanState(MongoFdwModifyState *fmstate); static bool MongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *acquireSampleRowsFunc, BlockNumber *totalPageCount); @@ -76,6 +141,20 @@ static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); +/* + * Generate new 24 character's rowid (MongoDB's "_id") + * from a string using pad character 'A'. + */ +#define UNIQUE_OID(x, z) do \ +{ \ + char y[25]; \ + memset(y, 'A', sizeof(y));\ + y[24] = 0; \ + strcpy(y, x); \ + bson_oid_from_string(&z, y);\ +} while(0); + + /* declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -102,13 +181,29 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->GetForeignRelSize = MongoGetForeignRelSize; fdwRoutine->GetForeignPaths = MongoGetForeignPaths; fdwRoutine->GetForeignPlan = MongoGetForeignPlan; - fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; fdwRoutine->BeginForeignScan = MongoBeginForeignScan; fdwRoutine->IterateForeignScan = MongoIterateForeignScan; fdwRoutine->ReScanForeignScan = MongoReScanForeignScan; fdwRoutine->EndForeignScan = MongoEndForeignScan; fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; + /* support for insert / update / delete */ + fdwRoutine->ExecForeignInsert = MongoExecForeignInsert; + fdwRoutine->BeginForeignModify = MongoBeginForeignModify; + fdwRoutine->PlanForeignModify = MongoPlanForeignModify; + fdwRoutine->AddForeignUpdateTargets = MongoAddForeignUpdateTargets; + fdwRoutine->ExecForeignUpdate = MongoExecForeignUpdate; + fdwRoutine->ExecForeignDelete = MongoExecForeignDelete; + fdwRoutine->EndForeignModify = MongoEndForeignModify; + + /* support for EXPLAIN */ + fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; + fdwRoutine->ExplainForeignModify = NULL; + fdwRoutine->ExplainForeignModify = MongoExplainForeignModify; + + /* support for ANALYSE */ + fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; + PG_RETURN_POINTER(fdwRoutine); } @@ -275,7 +370,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, bson_dispose(queryDocument); /* create the foreign scan node */ - foreignScan = make_foreignscan(targetList, restrictionClauses, + foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, NIL, /* no expressions to evaluate */ foreignPrivateList); @@ -302,9 +397,34 @@ MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, mongoFdwOptions->collectionName); + MongoFreeOptions(mongoFdwOptions); + ExplainPropertyText("Foreign Namespace", namespaceName->data, explainState); } +static void +MongoExplainForeignModify(ModifyTableState *mtstate, + ResultRelInfo *rinfo, + List *fdw_private, + int subplan_index, + ExplainState *es) +{ + MongoFdwOptions *mongoFdwOptions = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; + + foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); + mongoFdwOptions = MongoGetOptions(foreignTableId); + + /* construct fully qualified collection name */ + namespaceName = makeStringInfo(); + appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, + mongoFdwOptions->collectionName); + + MongoFreeOptions(mongoFdwOptions); + ExplainPropertyText("Foreign Namespace", namespaceName->data, es); +} + /* * MongoBeginForeignScan connects to the MongoDB server, and opens a cursor that @@ -327,7 +447,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) List *foreignPrivateList = NIL; bson *queryDocument = NULL; MongoFdwOptions *mongoFdwOptions = NULL; - MongoFdwExecState *executionState = NULL; + MongoFdwModifyState *fmstate = NULL; /* if Explain with no Analyze, do nothing */ if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) @@ -367,13 +487,14 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) mongo_cursor_set_query(mongoCursor, queryDocument); /* create and set foreign execution state */ - executionState = (MongoFdwExecState *) palloc0(sizeof(MongoFdwExecState)); - executionState->columnMappingHash = columnMappingHash; - executionState->mongoConnection = mongoConnection; - executionState->mongoCursor = mongoCursor; - executionState->queryDocument = queryDocument; - - scanState->fdw_state = (void *) executionState; + fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + fmstate->columnMappingHash = columnMappingHash; + fmstate->mongoConnection = mongoConnection; + fmstate->mongoCursor = mongoCursor; + fmstate->queryDocument = queryDocument; + fmstate->mongoFdwOptions = mongoFdwOptions; + + scanState->fdw_state = (void *) fmstate; } @@ -385,10 +506,10 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState) { - MongoFdwExecState *executionState = (MongoFdwExecState *) scanState->fdw_state; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; TupleTableSlot *tupleSlot = scanState->ss.ss_ScanTupleSlot; - mongo_cursor *mongoCursor = executionState->mongoCursor; - HTAB *columnMappingHash = executionState->columnMappingHash; + mongo_cursor *mongoCursor = fmstate->mongoCursor; + HTAB *columnMappingHash = fmstate->columnMappingHash; int32 cursorStatus = MONGO_ERROR; TupleDesc tupleDescriptor = tupleSlot->tts_tupleDescriptor; @@ -415,7 +536,7 @@ MongoIterateForeignScan(ForeignScanState *scanState) const char *bsonDocumentKey = NULL; /* top level document */ FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); ExecStoreVirtualTuple(tupleSlot); } @@ -429,7 +550,7 @@ MongoIterateForeignScan(ForeignScanState *scanState) mongo_cursor_error_t errorCode = mongoCursor->err; if (errorCode != MONGO_CURSOR_EXHAUSTED) { - MongoFreeScanState(executionState); + MongoFreeScanState(fmstate); ereport(ERROR, (errmsg("could not iterate over mongo collection"), errhint("Mongo driver cursor error code: %d", errorCode))); @@ -447,12 +568,12 @@ MongoIterateForeignScan(ForeignScanState *scanState) static void MongoEndForeignScan(ForeignScanState *scanState) { - MongoFdwExecState *executionState = (MongoFdwExecState *) scanState->fdw_state; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; /* if we executed a query, reclaim mongo related resources */ - if (executionState != NULL) + if (fmstate != NULL) { - MongoFreeScanState(executionState); + MongoFreeScanState(fmstate); } } @@ -465,16 +586,16 @@ MongoEndForeignScan(ForeignScanState *scanState) static void MongoReScanForeignScan(ForeignScanState *scanState) { - MongoFdwExecState *executionState = (MongoFdwExecState *) scanState->fdw_state; - mongo *mongoConnection = executionState->mongoConnection; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + mongo *mongoConnection = fmstate->mongoConnection; MongoFdwOptions *mongoFdwOptions = NULL; mongo_cursor *mongoCursor = NULL; StringInfo namespaceName = NULL; Oid foreignTableId = InvalidOid; /* close down the old cursor */ - mongo_cursor_destroy(executionState->mongoCursor); - mongo_cursor_dispose(executionState->mongoCursor); + mongo_cursor_destroy(fmstate->mongoCursor); + mongo_cursor_dispose(fmstate->mongoCursor); /* reconstruct full collection name */ foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); @@ -484,14 +605,448 @@ MongoReScanForeignScan(ForeignScanState *scanState) appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, mongoFdwOptions->collectionName); + MongoFreeOptions(mongoFdwOptions); + /* reconstruct cursor for collection name and set query */ mongoCursor = mongo_cursor_create(); mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); - mongo_cursor_set_query(mongoCursor, executionState->queryDocument); + mongo_cursor_set_query(mongoCursor, fmstate->queryDocument); + fmstate->mongoCursor = mongoCursor; +} + +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 = heap_open(rte->relid, NoLock); + + if (operation == CMD_INSERT) + { + TupleDesc tupdesc = RelationGetDescr(rel); + int attnum; - executionState->mongoCursor = mongoCursor; + for (attnum = 1; attnum <= tupdesc->natts; attnum++) + { + Form_pg_attribute attr = tupdesc->attrs[attnum - 1]; + + if (!attr->attisdropped) + targetAttrs = lappend_int(targetAttrs, attnum); + } + } + else if (operation == CMD_UPDATE) + { + Bitmapset *tmpset = bms_copy(rte->modifiedCols); + AttrNumber col; + + while ((col = bms_first_member(tmpset)) >= 0) + { + col += FirstLowInvalidHeapAttributeNumber; + 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"); + + heap_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 = NULL; + Relation rel = resultRelInfo->ri_RelationDesc; + AttrNumber n_params = 0; + Oid typefnoid = InvalidOid; + bool isvarlena = false; + ListCell *lc = NULL; + Oid foreignTableId = InvalidOid; + + /* + * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState + * stays NULL. + */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) + return; + + foreignTableId = RelationGetRelid(rel); + + /* Begin constructing MongoFdwModifyState. */ + fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + + fmstate->rel = rel; + fmstate->mongoFdwOptions = MongoGetOptions(foreignTableId); + + fmstate->target_attrs = (List *) list_nth(fdw_private, 0); + + n_params = list_length(fmstate->target_attrs) + 1; + fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params); + fmstate->p_nums = 0; + + /* Set up for remaining transmittable parameters */ + foreach(lc, fmstate->target_attrs) + { + int attnum = lfirst_int(lc); + Form_pg_attribute attr = RelationGetDescr(rel)->attrs[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) +{ + MongoFdwOptions *options = NULL; + mongo *mongoConnection = NULL; + Oid foreignTableId = InvalidOid; + bson *b = NULL; + bson_oid_t bsonObjectId; + char *outputString; + Oid outputFunctionId = InvalidOid; + bool typeVarLength = false; + Oid typoid = InvalidOid; + Datum value; + bool isnull = false; + char qualname[255]; + + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; + + mongoConnection = GetConnection(options->addressName, options->portNumber); + + b = bson_create(); + bson_init(b); + + typoid = get_atttype(foreignTableId, 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); + /* + * We also disallow null values to the first column which + * happens to be the row identifier in MongoDb (_id). + */ + if (attnum == 1) + { + if (isnull) + elog(ERROR, "null value for first column (row identifier column) is not supported"); + + getTypeOutputInfo(typoid, &outputFunctionId, &typeVarLength); + outputString = OidOutputFunctionCall(outputFunctionId, value); + + /* MongoDB support 24 character's _id (row identifier) */ + if (strlen(outputString) > 24) + elog(ERROR, "first column (row identifier column) should be max 24 characters"); + + UNIQUE_OID(outputString, bsonObjectId); + + if(bson_append_oid(b, "_id", &bsonObjectId) == MONGO_ERROR) + ereport(ERROR, + (errcode(ERRCODE_FDW_ERROR), + errmsg("insert failed, invalid value"))); + } + if(AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid) == MONGO_ERROR) + ereport(ERROR, (errmsg("failed to update row %d", slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid), + errhint("Mongo driver insert error: %d", mongoConnection->err))); + } + } + bson_finish(b); + + sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + + if (mongo_insert(mongoConnection, qualname, b , NULL) != MONGO_OK) + ereport(ERROR, + (errcode(ERRCODE_FDW_ERROR), + errmsg("insert failed"), + errhint("Mongo driver insert error: %d", mongoConnection->err))); + + bson_destroy(b); + bson_dispose(b); + + return slot; +} + + +/* + * MongoAddForeignUpdateTargets + * Add column(s) needed for update/delete on a foreign table + */ +static void +MongoAddForeignUpdateTargets(Query *parsetree, + RangeTblEntry *target_rte, + Relation target_relation) +{ + Var *var; + const char *attrname; + TargetEntry *tle; + + /* + * What we need is the rowid which is the first column + */ + Form_pg_attribute attr = + RelationGetDescr(target_relation)->attrs[0]; + + /* Make a Var representing the desired value */ + var = makeVar(parsetree->resultRelation, + 1, + attr->atttypid, + attr->atttypmod, + InvalidOid, + 0); + + /* Wrap it in a TLE with the right name ... */ + attrname = NameStr(attr->attname); + + 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); +} + + +/* + * postgresExecForeignUpdate + * Update one row in a foreign table + */ +static TupleTableSlot * +MongoExecForeignUpdate(EState *estate, + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) +{ + MongoFdwOptions *options = NULL; + mongo *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + bson *b = NULL; + bson *op = NULL; + char qualname[255]; + + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; + + mongoConnection = GetConnection(options->addressName, options->portNumber); + + /* Get the id that was passed up as a resjunk column */ + datum = ExecGetJunkAttribute(planSlot, 1, &isNull); + + columnName = get_relid_attribute_name(foreignTableId, 1); + + typoid = get_atttype(foreignTableId, 1); + + b = bson_create(); + bson_init(b); + + bson_append_start_object(b, "$set"); + + /* Get following parameters from slot, and append to the bson object */ + if (slot != NULL && fmstate->target_attrs != NIL) + { + ListCell *lc; + + foreach(lc, fmstate->target_attrs) + { + int attnum = lfirst_int(lc); + Datum value; + bool isnull; + + value = slot_getattr(slot, attnum, &isnull); + AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + } + } + bson_append_finish_object(b); + bson_finish(b); + + op = bson_create(); + bson_init(op); + + /* Append where clause in bson object for particular rowid */ + if (AppenMongoValue(op, columnName, datum, false, typoid) == MONGO_ERROR) + { + bson_destroy(b); + bson_dispose(b); + + bson_destroy(op); + bson_dispose(op); + + ereport(ERROR, (errmsg("failed to update row"), + errhint("Mongo driver update error: %d", mongoConnection->err))); + + return NULL; + } + bson_finish(op); + + sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + + /* We are ready to update the row into MongoDB */ + if (mongo_update(mongoConnection, qualname, op, b, MONGO_UPDATE_BASIC, 0) == MONGO_ERROR) + ereport(ERROR, (errmsg("failed to update row"), + errhint("Mongo driver update error: %d", mongoConnection->err))); + + bson_destroy(b); + bson_dispose(b); + + bson_destroy(op); + bson_dispose(op); + + /* 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) +{ + MongoFdwOptions *options = NULL; + mongo *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + bson *b = NULL; + char qualname[255]; + + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; + + mongoConnection = GetConnection(options->addressName, options->portNumber); + + /* Get the id that was passed up as a resjunk column */ + datum = ExecGetJunkAttribute(planSlot, 1, &isNull); + + columnName = get_relid_attribute_name(foreignTableId, 1); + + typoid = get_atttype(foreignTableId, 1); + + b = bson_create(); + bson_init(b); + + if (AppenMongoValue(b, columnName, datum, false, typoid) == MONGO_ERROR) + { + bson_destroy(b); + bson_dispose(b); + ereport(ERROR, (errmsg("failed to delete row"), + errhint("Mongo driver delete error: %d", mongoConnection->err))); + + return NULL; + } + bson_finish(b); + + sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + + /* Now we are ready to delete a single document from MongoDB */ + if (mongo_remove(mongoConnection, qualname, b , NULL) != MONGO_OK) + ereport(ERROR, (errmsg("failed to delete row"), + errhint("Mongo driver delete error: %d", mongoConnection->err))); + + bson_destroy(b); + bson_dispose(b); + + /* 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) +{ + +} + + /* * ForeignTableDocumentCount connects to the MongoDB server, and queries it for * the number of documents in the foreign collection. On success, the function @@ -511,6 +1066,9 @@ ForeignTableDocumentCount(Oid foreignTableId) mongoConnection = GetConnection(options->addressName, options->portNumber); documentCount = mongo_count(mongoConnection, options->databaseName, options->collectionName, emptyQuery); + + MongoFreeOptions(options); + return documentCount; } @@ -615,7 +1173,7 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, bson subObject; bson_iterator_subobject(&bsonIterator, &subObject); FillTupleSlot(&subObject, bsonFullKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); continue; } @@ -714,7 +1272,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } break; } - case NAMEOID: + case NAMEOID: { /* * We currently overload the NAMEOID type to represent the BSON @@ -943,20 +1501,20 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) * all Mongo related resources allocated for the foreign scan. */ static void -MongoFreeScanState(MongoFdwExecState *executionState) +MongoFreeScanState(MongoFdwModifyState *fmstate) { - if (executionState == NULL) + if (fmstate == NULL) { return; } - bson_destroy(executionState->queryDocument); - bson_dispose(executionState->queryDocument); + bson_destroy(fmstate->queryDocument); + bson_dispose(fmstate->queryDocument); - mongo_cursor_destroy(executionState->mongoCursor); - mongo_cursor_dispose(executionState->mongoCursor); + mongo_cursor_destroy(fmstate->mongoCursor); + mongo_cursor_dispose(fmstate->mongoCursor); /* Release remote connection */ - ReleaseConnection(executionState->mongoConnection); + ReleaseConnection(fmstate->mongoConnection); } @@ -1045,7 +1603,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, ForeignScanState *scanState = NULL; List *foreignPrivateList = NIL; ForeignScan *foreignScan = NULL; - MongoFdwExecState *executionState = NULL; + MongoFdwModifyState *fmstate = NULL; char *relationName = NULL; int executorFlags = 0; MemoryContext oldContext = CurrentMemoryContext; @@ -1086,9 +1644,9 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, MongoBeginForeignScan(scanState, executorFlags); - executionState = (MongoFdwExecState *) scanState->fdw_state; - mongoCursor = executionState->mongoCursor; - columnMappingHash = executionState->columnMappingHash; + fmstate = (MongoFdwModifyState *) scanState->fdw_state; + mongoCursor = fmstate->mongoCursor; + columnMappingHash = fmstate->columnMappingHash; /* * Use per-tuple memory context to prevent leak of memory used to read @@ -1128,7 +1686,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, MemoryContextSwitchTo(tupleContext); FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); MemoryContextSwitchTo(oldContext); } @@ -1141,14 +1699,14 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, mongo_cursor_error_t errorCode = mongoCursor->err; if (errorCode != MONGO_CURSOR_EXHAUSTED) { - MongoFreeScanState(executionState); + MongoFreeScanState(fmstate); ereport(ERROR, (errmsg("could not iterate over mongo collection"), errhint("Mongo driver cursor error code: %d", errorCode))); } break; - } + } /* * The first targetRowCount sample rows are simply copied into the @@ -1187,8 +1745,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, heap_freetuple(sampleRows[rowIndex]); sampleRows[rowIndex] = heap_form_tuple(tupleDescriptor, - columnValues, - columnNulls); + columnValues, + columnNulls); } rowCountToSkip -= 1; @@ -1199,7 +1757,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, /* clean up */ MemoryContextDelete(tupleContext); - MongoFreeScanState(executionState); + MongoFreeScanState(fmstate); pfree(columnValues); pfree(columnNulls); diff --git a/mongo_fdw.h b/mongo_fdw.h index 52c5ef7..8be4d10 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -110,17 +110,33 @@ typedef struct MongoFdwOptions /* - * MongoFdwExecState keeps foreign data wrapper specific execution state that we - * create and hold onto when executing the query. - */ -typedef struct MongoFdwExecState +* 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 { - struct HTAB *columnMappingHash; - mongo *mongoConnection; - mongo_cursor *mongoCursor; - bson *queryDocument; + 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 *mongoConnection; /* MongoDB connection */ + mongo_cursor *mongoCursor; /* MongoDB cursor */ + bson *queryDocument; /* Bson Document */ + + MongoFdwOptions *mongoFdwOptions; -} MongoFdwExecState; + /* working memory context */ + MemoryContext temp_cxt; /* context for per-tuple temporary data */ +} MongoFdwModifyState; /* @@ -148,6 +164,7 @@ extern List * ColumnList(RelOptInfo *baserel); extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); extern MongoFdwOptions * MongoGetOptions(Oid foreignTableId); +extern void MongoFreeOptions(MongoFdwOptions *mongoFdwOptions); mongo* GetConnection(char *host, int32 port); void cleanup_connection(void); diff --git a/mongo_query.c b/mongo_query.c index fb2ac33..426e801 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -13,6 +13,7 @@ #include "postgres.h" #include "mongo_fdw.h" +#include "mongo_query.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" @@ -363,59 +364,68 @@ ColumnOperatorList(Var *column, List *operatorList) static void AppendConstantValue(bson *queryDocument, const char *keyName, Const *constant) { - Datum constantValue = constant->constvalue; - Oid constantTypeId = constant->consttype; - - bool constantNull = constant->constisnull; - if (constantNull) + if (constant->constisnull) { bson_append_null(queryDocument, keyName); return; } + AppenMongoValue(queryDocument, keyName, constant->constvalue, constant->consttype, false); +} + +int32 +AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) +{ + int32 status = MONGO_ERROR; - switch(constantTypeId) + if (isnull) + { + status = bson_append_null(queryDocument, keyName); + return status; + } + + switch(id) { case INT2OID: { - int16 value = DatumGetInt16(constantValue); - bson_append_int(queryDocument, keyName, (int) value); + int16 valueInt = DatumGetInt16(value); + status = bson_append_int(queryDocument, keyName, (int) valueInt); break; } case INT4OID: { - int32 value = DatumGetInt32(constantValue); - bson_append_int(queryDocument, keyName, value); + int32 valueInt = DatumGetInt32(value); + status = bson_append_int(queryDocument, keyName, valueInt); break; } case INT8OID: { - int64 value = DatumGetInt64(constantValue); - bson_append_long(queryDocument, keyName, value); + int64 valueLong = DatumGetInt64(value); + status = bson_append_long(queryDocument, keyName, valueLong); break; } case FLOAT4OID: { - float4 value = DatumGetFloat4(constantValue); - bson_append_double(queryDocument, keyName, (double) value); + float4 valueFloat = DatumGetFloat4(value); + status = bson_append_double(queryDocument, keyName, (double) valueFloat); break; } case FLOAT8OID: { - float8 value = DatumGetFloat8(constantValue); - bson_append_double(queryDocument, keyName, value); + float8 valueFloat = DatumGetFloat8(value); + status = bson_append_double(queryDocument, keyName, valueFloat); break; } case NUMERICOID: { - Datum valueDatum = DirectFunctionCall1(numeric_float8, constantValue); - float8 value = DatumGetFloat8(valueDatum); - bson_append_double(queryDocument, keyName, value); + Datum valueDatum = DirectFunctionCall1(numeric_float8, value); + float8 valueFloat = DatumGetFloat8(valueDatum); + status = bson_append_double(queryDocument, keyName, valueFloat); break; } case BOOLOID: { - bool value = DatumGetBool(constantValue); - bson_append_int(queryDocument, keyName, (int) value); + bool valueBool = DatumGetBool(value); + status = bson_append_int(queryDocument, keyName, (int) valueBool); break; } case BPCHAROID: @@ -425,46 +435,42 @@ AppendConstantValue(bson *queryDocument, const char *keyName, Const *constant) char *outputString = NULL; Oid outputFunctionId = InvalidOid; bool typeVarLength = false; - - getTypeOutputInfo(constantTypeId, &outputFunctionId, &typeVarLength); - outputString = OidOutputFunctionCall(outputFunctionId, constantValue); - - bson_append_string(queryDocument, keyName, outputString); + getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); + outputString = OidOutputFunctionCall(outputFunctionId, value); + status = bson_append_string(queryDocument, keyName, outputString); break; } - case NAMEOID: + case NAMEOID: { char *outputString = NULL; Oid outputFunctionId = InvalidOid; bool typeVarLength = false; bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - - getTypeOutputInfo(constantTypeId, &outputFunctionId, &typeVarLength); - outputString = OidOutputFunctionCall(outputFunctionId, constantValue); + getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); + outputString = OidOutputFunctionCall(outputFunctionId, value); bson_oid_from_string(&bsonObjectId, outputString); - - bson_append_oid(queryDocument, keyName, &bsonObjectId); + status = bson_append_oid(queryDocument, keyName, &bsonObjectId); break; } case DATEOID: { - Datum valueDatum = DirectFunctionCall1(date_timestamp, constantValue); + Datum valueDatum = DirectFunctionCall1(date_timestamp, value); Timestamp valueTimestamp = DatumGetTimestamp(valueDatum); int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - bson_append_date(queryDocument, keyName, valueMilliSecs); + status = bson_append_date(queryDocument, keyName, valueMilliSecs); break; } case TIMESTAMPOID: case TIMESTAMPTZOID: { - Timestamp valueTimestamp = DatumGetTimestamp(constantValue); + Timestamp valueTimestamp = DatumGetTimestamp(value); int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - bson_append_date(queryDocument, keyName, valueMilliSecs); + status = bson_append_date(queryDocument, keyName, valueMilliSecs); break; } default: @@ -474,12 +480,13 @@ AppendConstantValue(bson *queryDocument, const char *keyName, Const *constant) * 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", constantTypeId))); + 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; } diff --git a/mongo_query.h b/mongo_query.h index 0a75e98..87b95e5 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -12,7 +12,7 @@ #ifndef MONGO_QUERY_H #define MONGO_QUERY_H - +int32 AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); #endif /* MONGO_QUERY_H */ From 5f881c9e5fb3ebf84274829eab14aadab48552e8 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:21:25 +0500 Subject: [PATCH 020/239] MongoDB's Meta Driver support added. --- Makefile | 35 ---- mongo_fdw.c | 441 +++++++++++++++++++++++--------------------------- mongo_fdw.h | 83 ++++++---- mongo_query.c | 69 ++++---- mongo_query.h | 5 +- 5 files changed, 291 insertions(+), 342 deletions(-) delete mode 100644 Makefile diff --git a/Makefile b/Makefile deleted file mode 100644 index 82bf10c..0000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# mongo_fdw/Makefile -# -# Copyright (c) 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. -# - -MONGO_DRIVER = mongo-c-driver-v0.6 -MONGO_PATH = $(MONGO_DRIVER)/src -MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ - $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os - -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -OBJS = connection.o option.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) - -EXTENSION = mongo_fdw -DATA = mongo_fdw--1.0.sql - -$(MONGO_DRIVER)/%.os: - $(MAKE) -C $(MONGO_DRIVER) $*.os - -# -# 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) diff --git a/mongo_fdw.c b/mongo_fdw.c index 75593eb..2bc67b1 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -10,12 +10,9 @@ *------------------------------------------------------------------------- */ -#include "postgres.h" -#include "mongo_fdw.h" -#include "mongo_query.h" - #include "postgres.h" #include "bson.h" +#include "mongo_wrapper.h" #include "mongo_fdw.h" #include "mongo_query.h" @@ -68,14 +65,14 @@ /* Local functions forward declarations */ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId, ForeignPath *bestPath, - List *targetList, List *restrictionClauses); + Oid foreignTableId, ForeignPath *bestPath, + List *targetList, List *restrictionClauses); static void MongoExplainForeignScan(ForeignScanState *scanState, - ExplainState *explainState); + ExplainState *explainState); static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); @@ -90,7 +87,7 @@ static TupleTableSlot *MongoExecForeignDelete(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot); static void MongoEndForeignModify(EState *estate, - ResultRelInfo *resultRelInfo); + ResultRelInfo *resultRelInfo); static void MongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, @@ -103,35 +100,33 @@ static void MongoBeginForeignModify(ModifyTableState *mtstate, int eflags); static TupleTableSlot *MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static List *MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index); + ModifyTable *plan, + Index resultRelation, + int subplan_index); static void MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, - List *fdw_private, - int subplan_index, - ExplainState *es); + ResultRelInfo *rinfo, List *fdw_private, + int subplan_index, ExplainState *es); /* local functions */ static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); -static void FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); -static bool ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId); -static Datum ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId); -static Datum ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, +static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls); +static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); +static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); +static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod); static void MongoFreeScanState(MongoFdwModifyState *fmstate); static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, + AcquireSampleRowsFunc *acquireSampleRowsFunc, BlockNumber *totalPageCount); static int MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, @@ -151,10 +146,9 @@ extern PGDLLEXPORT void _PG_init(void); memset(y, 'A', sizeof(y));\ y[24] = 0; \ strcpy(y, x); \ - bson_oid_from_string(&z, y);\ + BsonOidFromString(&z, y);\ } while(0); - /* declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -198,7 +192,6 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) /* support for EXPLAIN */ fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; - fdwRoutine->ExplainForeignModify = NULL; fdwRoutine->ExplainForeignModify = MongoExplainForeignModify; /* support for ANALYSE */ @@ -339,7 +332,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; List *opExpressionList = NIL; - bson *queryDocument = NULL; + BSON *queryDocument = NULL; List *columnList = NIL; /* @@ -364,13 +357,13 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, columnList = ColumnList(baserel); /* construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(columnList, restrictionClauses); + foreignPrivateList = list_make2(columnList, opExpressionList); - /* only clean up the query struct, but not its data */ - bson_dispose(queryDocument); + /* only clean up the query struct */ + BsonDestroy(queryDocument); /* create the foreign scan node */ - foreignScan = make_foreignscan(targetList, restrictionClauses, + foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, NIL, /* no expressions to evaluate */ foreignPrivateList); @@ -435,25 +428,23 @@ MongoExplainForeignModify(ModifyTableState *mtstate, static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) { - mongo *mongoConnection = NULL; - mongo_cursor *mongoCursor = NULL; + MONGO_CONN *mongoConnection = NULL; + MONGO_CURSOR *mongoCursor = NULL; Oid foreignTableId = InvalidOid; List *columnList = NIL; HTAB *columnMappingHash = NULL; char *addressName = NULL; int32 portNumber = 0; - StringInfo namespaceName = NULL; ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; - bson *queryDocument = NULL; + BSON *queryDocument = NULL; MongoFdwOptions *mongoFdwOptions = NULL; MongoFdwModifyState *fmstate = NULL; + List *opExpressionList = NIL; /* if Explain with no Analyze, do nothing */ if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) - { return; - } foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); mongoFdwOptions = MongoGetOptions(foreignTableId); @@ -467,24 +458,20 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) * establish new connection if necessary. */ mongoConnection = GetConnection(addressName, portNumber); + foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; - Assert(list_length(foreignPrivateList) == 1); + Assert(list_length(foreignPrivateList) == 2); columnList = list_nth(foreignPrivateList, 0); + opExpressionList = list_nth(foreignPrivateList, 1); - queryDocument = QueryDocument(foreignTableId, NIL); + queryDocument = QueryDocument(foreignTableId, opExpressionList); columnMappingHash = ColumnMappingHash(foreignTableId, columnList); - namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, - mongoFdwOptions->collectionName); - /* create cursor for collection name and set query */ - mongoCursor = mongo_cursor_create(); - mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); - mongo_cursor_set_query(mongoCursor, queryDocument); + mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->databaseName, mongoFdwOptions->collectionName, queryDocument); /* create and set foreign execution state */ fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); @@ -508,9 +495,8 @@ MongoIterateForeignScan(ForeignScanState *scanState) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; TupleTableSlot *tupleSlot = scanState->ss.ss_ScanTupleSlot; - mongo_cursor *mongoCursor = fmstate->mongoCursor; + MONGO_CURSOR *mongoCursor = fmstate->mongoCursor; HTAB *columnMappingHash = fmstate->columnMappingHash; - int32 cursorStatus = MONGO_ERROR; TupleDesc tupleDescriptor = tupleSlot->tts_tupleDescriptor; Datum *columnValues = tupleSlot->tts_values; @@ -529,32 +515,33 @@ MongoIterateForeignScan(ForeignScanState *scanState) memset(columnValues, 0, columnCount * sizeof(Datum)); memset(columnNulls, true, columnCount * sizeof(bool)); - cursorStatus = mongo_cursor_next(mongoCursor); - if (cursorStatus == MONGO_OK) + if (MongoCursorNext(mongoCursor, NULL)) { - const bson *bsonDocument = mongo_cursor_bson(mongoCursor); + const BSON *bsonDocument = MongoCursorBson(mongoCursor); const char *bsonDocumentKey = NULL; /* top level document */ FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); ExecStoreVirtualTuple(tupleSlot); } else { + #ifndef META_DRIVER /* * The following is a courtesy check. In practice when Mongo shuts down, * mongo_cursor_next() could possibly crash. This function first frees - * cursor->reply, and then references reply in mongo_cursor_destroy(). + * cursor->reply, and then references reply in mongo_cursor__destroy(). */ + mongo_cursor_error_t errorCode = mongoCursor->err; if (errorCode != MONGO_CURSOR_EXHAUSTED) { MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); + errhint("Mongo driver cursor error code: %d", errorCode))); } + #endif } return tupleSlot; @@ -573,6 +560,11 @@ MongoEndForeignScan(ForeignScanState *scanState) /* if we executed a query, reclaim mongo related resources */ if (fmstate != NULL) { + if (fmstate->mongoFdwOptions) + { + MongoFreeOptions(fmstate->mongoFdwOptions); + fmstate->mongoFdwOptions = NULL; + } MongoFreeScanState(fmstate); } } @@ -587,31 +579,23 @@ static void MongoReScanForeignScan(ForeignScanState *scanState) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - mongo *mongoConnection = fmstate->mongoConnection; + MONGO_CONN *mongoConnection = fmstate->mongoConnection; MongoFdwOptions *mongoFdwOptions = NULL; - mongo_cursor *mongoCursor = NULL; - StringInfo namespaceName = NULL; Oid foreignTableId = InvalidOid; /* close down the old cursor */ - mongo_cursor_destroy(fmstate->mongoCursor); - mongo_cursor_dispose(fmstate->mongoCursor); + MongoCursorDestroy(fmstate->mongoCursor); /* reconstruct full collection name */ foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); mongoFdwOptions = MongoGetOptions(foreignTableId); - namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, - mongoFdwOptions->collectionName); - - MongoFreeOptions(mongoFdwOptions); - /* reconstruct cursor for collection name and set query */ - mongoCursor = mongo_cursor_create(); - mongo_cursor_init(mongoCursor, mongoConnection, namespaceName->data); - mongo_cursor_set_query(mongoCursor, fmstate->queryDocument); - fmstate->mongoCursor = mongoCursor; + fmstate->mongoCursor = MongoCursorCreate(mongoConnection, + fmstate->mongoFdwOptions->databaseName, + fmstate->mongoFdwOptions->collectionName, + fmstate->queryDocument); + MongoFreeOptions(mongoFdwOptions); } static List * @@ -623,7 +607,7 @@ MongoPlanForeignModify(PlannerInfo *root, CmdType operation = plan->operation; RangeTblEntry *rte = planner_rt_fetch(resultRelation, root); Relation rel; - List *targetAttrs = NIL; + List* targetAttrs = NIL; /* * Core code already has some lock on each rel being planned, so we can @@ -646,7 +630,7 @@ MongoPlanForeignModify(PlannerInfo *root, } else if (operation == CMD_UPDATE) { - Bitmapset *tmpset = bms_copy(rte->modifiedCols); + Bitmapset *tmpset = bms_copy(rte->modifiedCols); AttrNumber col; while ((col = bms_first_member(tmpset)) >= 0) @@ -654,7 +638,6 @@ MongoPlanForeignModify(PlannerInfo *root, col += FirstLowInvalidHeapAttributeNumber; 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) @@ -684,8 +667,7 @@ MongoPlanForeignModify(PlannerInfo *root, /* - * MongoBeginForeignModify - * Begin an insert/update/delete operation on a foreign table + * Begin an insert/update/delete operation on a foreign table */ static void MongoBeginForeignModify(ModifyTableState *mtstate, @@ -742,8 +724,7 @@ MongoBeginForeignModify(ModifyTableState *mtstate, /* - * MongoExecForeignInsert - * Insert one row into a foreign table + * Insert one row into a foreign table. */ static TupleTableSlot * MongoExecForeignInsert(EState *estate, @@ -752,9 +733,9 @@ MongoExecForeignInsert(EState *estate, TupleTableSlot *planSlot) { MongoFdwOptions *options = NULL; - mongo *mongoConnection = NULL; + MONGO_CONN *mongoConnection = NULL; Oid foreignTableId = InvalidOid; - bson *b = NULL; + BSON *b = NULL; bson_oid_t bsonObjectId; char *outputString; Oid outputFunctionId = InvalidOid; @@ -762,7 +743,7 @@ MongoExecForeignInsert(EState *estate, Oid typoid = InvalidOid; Datum value; bool isnull = false; - char qualname[255]; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -773,8 +754,7 @@ MongoExecForeignInsert(EState *estate, mongoConnection = GetConnection(options->addressName, options->portNumber); - b = bson_create(); - bson_init(b); + b = BsonCreate(); typoid = get_atttype(foreignTableId, 1); @@ -787,12 +767,13 @@ MongoExecForeignInsert(EState *estate, { int attnum = lfirst_int(lc); value = slot_getattr(slot, attnum, &isnull); - /* - * We also disallow null values to the first column which - * happens to be the row identifier in MongoDb (_id). - */ if (attnum == 1) { + /* + * We also disallow null values to the first column which + * happens to be the row identifier in MongoDb (_id). + */ + if (isnull) elog(ERROR, "null value for first column (row identifier column) is not supported"); @@ -805,46 +786,40 @@ MongoExecForeignInsert(EState *estate, UNIQUE_OID(outputString, bsonObjectId); - if(bson_append_oid(b, "_id", &bsonObjectId) == MONGO_ERROR) + /* Append rowid field which is "_id" in MongoDB */ + if(!BsonAppendOid(b, "_id", &bsonObjectId)) ereport(ERROR, (errcode(ERRCODE_FDW_ERROR), errmsg("insert failed, invalid value"))); } - if(AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid) == MONGO_ERROR) - ereport(ERROR, (errmsg("failed to update row %d", slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid), - errhint("Mongo driver insert error: %d", mongoConnection->err))); + AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); } } - bson_finish(b); - - sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + BsonFinish(b); - if (mongo_insert(mongoConnection, qualname, b , NULL) != MONGO_OK) - ereport(ERROR, - (errcode(ERRCODE_FDW_ERROR), - errmsg("insert failed"), - errhint("Mongo driver insert error: %d", mongoConnection->err))); + /* Now we are ready to insert tuple / document into MongoDB */ + MongoInsert(mongoConnection, options->databaseName, options->collectionName, b); - bson_destroy(b); - bson_dispose(b); + BsonDestroy(b); return slot; } /* - * MongoAddForeignUpdateTargets - * Add column(s) needed for update/delete on a foreign table + * 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. */ static void MongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) { - Var *var; - const char *attrname; - TargetEntry *tle; + Var *var = NULL; + const char *attrname = NULL; + TargetEntry *tle = NULL; /* * What we need is the rowid which is the first column @@ -873,10 +848,6 @@ MongoAddForeignUpdateTargets(Query *parsetree, } -/* - * postgresExecForeignUpdate - * Update one row in a foreign table - */ static TupleTableSlot * MongoExecForeignUpdate(EState *estate, ResultRelInfo *resultRelInfo, @@ -884,15 +855,15 @@ MongoExecForeignUpdate(EState *estate, TupleTableSlot *planSlot) { MongoFdwOptions *options = NULL; - mongo *mongoConnection = NULL; + MONGO_CONN *mongoConnection = NULL; Datum datum = 0; bool isNull = false; Oid foreignTableId = InvalidOid; char *columnName = NULL; Oid typoid = InvalidOid; - bson *b = NULL; - bson *op = NULL; - char qualname[255]; + BSON *b = NULL; + BSON *op = NULL; + BSON set; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -910,12 +881,10 @@ MongoExecForeignUpdate(EState *estate, typoid = get_atttype(foreignTableId, 1); - b = bson_create(); - bson_init(b); - - bson_append_start_object(b, "$set"); + b = BsonCreate(); + BsonAppendStartObject(b, "$set", &set); - /* Get following parameters from slot, and append to the bson object */ + /* get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) { ListCell *lc; @@ -927,50 +896,36 @@ MongoExecForeignUpdate(EState *estate, bool isnull; value = slot_getattr(slot, attnum, &isnull); +#ifdef META_DRIVER + AppenMongoValue(&set, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); +#else AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); +#endif } } - bson_append_finish_object(b); - bson_finish(b); + BsonAppendFinishObject(b, &set); + BsonFinish(b); - op = bson_create(); - bson_init(op); - - /* Append where clause in bson object for particular rowid */ - if (AppenMongoValue(op, columnName, datum, false, typoid) == MONGO_ERROR) + op = BsonCreate(); + if (!AppenMongoValue(op, columnName, datum, false, typoid)) { - bson_destroy(b); - bson_dispose(b); - - bson_destroy(op); - bson_dispose(op); - - ereport(ERROR, (errmsg("failed to update row"), - errhint("Mongo driver update error: %d", mongoConnection->err))); - + BsonDestroy(b); return NULL; } - bson_finish(op); - - sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + BsonFinish(op); /* We are ready to update the row into MongoDB */ - if (mongo_update(mongoConnection, qualname, op, b, MONGO_UPDATE_BASIC, 0) == MONGO_ERROR) - ereport(ERROR, (errmsg("failed to update row"), - errhint("Mongo driver update error: %d", mongoConnection->err))); + MongoUpdate(mongoConnection, options->databaseName, options->collectionName, op, b); - bson_destroy(b); - bson_dispose(b); - - bson_destroy(op); - bson_dispose(op); + BsonDestroy(op); + BsonDestroy(b); /* Return NULL if nothing was updated on the remote end */ return slot; } - /* * MongoExecForeignDelete * Delete one row from a foreign table @@ -982,14 +937,13 @@ MongoExecForeignDelete(EState *estate, TupleTableSlot *planSlot) { MongoFdwOptions *options = NULL; - mongo *mongoConnection = NULL; + MONGO_CONN *mongoConnection = NULL; Datum datum = 0; bool isNull = false; Oid foreignTableId = InvalidOid; char *columnName = NULL; Oid typoid = InvalidOid; - bson *b = NULL; - char qualname[255]; + BSON *b = NULL; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -1007,35 +961,23 @@ MongoExecForeignDelete(EState *estate, typoid = get_atttype(foreignTableId, 1); - b = bson_create(); - bson_init(b); - - if (AppenMongoValue(b, columnName, datum, false, typoid) == MONGO_ERROR) + b = BsonCreate(); + if (!AppenMongoValue(b,columnName, datum, false, typoid)) { - bson_destroy(b); - bson_dispose(b); - ereport(ERROR, (errmsg("failed to delete row"), - errhint("Mongo driver delete error: %d", mongoConnection->err))); - + BsonDestroy(b); return NULL; } - bson_finish(b); - - sprintf(qualname,"%s.%s", options->databaseName, options->collectionName); + BsonFinish(b); /* Now we are ready to delete a single document from MongoDB */ - if (mongo_remove(mongoConnection, qualname, b , NULL) != MONGO_OK) - ereport(ERROR, (errmsg("failed to delete row"), - errhint("Mongo driver delete error: %d", mongoConnection->err))); + MongoDelete(mongoConnection, options->databaseName, options->collectionName, b); - bson_destroy(b); - bson_dispose(b); + BsonDestroy(b); /* Return NULL if nothing was updated on the remote end */ return slot; } - /* * MongoEndForeignModify * Finish an insert/update/delete operation on a foreign table @@ -1043,10 +985,19 @@ MongoExecForeignDelete(EState *estate, static void MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { - + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + if (fmstate) + { + if (fmstate->mongoFdwOptions) + { + MongoFreeOptions(fmstate->mongoFdwOptions); + fmstate->mongoFdwOptions = NULL; + } + MongoFreeScanState(fmstate); + pfree(fmstate); + } } - /* * ForeignTableDocumentCount connects to the MongoDB server, and queries it for * the number of documents in the foreign collection. On success, the function @@ -1056,16 +1007,16 @@ static double ForeignTableDocumentCount(Oid foreignTableId) { MongoFdwOptions *options = NULL; - mongo *mongoConnection = NULL; - const bson *emptyQuery = NULL; + MONGO_CONN *mongoConnection = NULL; + const BSON *emptyQuery = NULL; double documentCount = 0.0; /* resolve foreign table options; and connect to mongo server */ options = MongoGetOptions(foreignTableId); mongoConnection = GetConnection(options->addressName, options->portNumber); - documentCount = mongo_count(mongoConnection, options->databaseName, - options->collectionName, emptyQuery); + + MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); MongoFreeOptions(options); @@ -1133,16 +1084,16 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) * passed as NULL. */ static void -FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, +FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) { - bson_iterator bsonIterator = { NULL, 0 }; - bson_iterator_init(&bsonIterator, bsonDocument); + BSON_ITERATOR bsonIterator = { NULL, 0 }; + BsonIterInit(&bsonIterator, (BSON*)bsonDocument); - while (bson_iterator_next(&bsonIterator)) + while (BsonIterNext(&bsonIterator)) { - const char *bsonKey = bson_iterator_key(&bsonIterator); - bson_type bsonType = bson_iterator_type(&bsonIterator); + const char *bsonKey = BsonIterKey(&bsonIterator); + BSON_TYPE bsonType = BsonIterType(&bsonIterator); ColumnMapping *columnMapping = NULL; Oid columnTypeId = InvalidOid; @@ -1168,22 +1119,22 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, } /* recurse into nested objects */ - if (bsonType == BSON_OBJECT) + if (bsonType == BSON_TYPE_DOCUMENT) { - bson subObject; - bson_iterator_subobject(&bsonIterator, &subObject); + BSON subObject; + BsonIterSubObject(&bsonIterator, &subObject); FillTupleSlot(&subObject, bsonFullKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); continue; } - /* look up the corresponding column for this bson key */ + /* look up the corresponding column for this BSON key */ hashKey = (void *) bsonFullKey; columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, HASH_FIND, &handleFound); - /* if no corresponding column or null bson value, continue */ - if (columnMapping == NULL || bsonType == BSON_NULL) + /* if no corresponding column or null BSON value, continue */ + if (columnMapping == NULL || bsonType == BSON_TYPE_NULL) { continue; } @@ -1192,7 +1143,7 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, columnTypeId = columnMapping->columnTypeId; columnArrayTypeId = columnMapping->columnArrayTypeId; - if (OidIsValid(columnArrayTypeId) && bsonType == BSON_ARRAY) + if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) { compatibleTypes = true; } @@ -1235,7 +1186,7 @@ FillTupleSlot(const bson *bsonDocument, const char *bsonDocumentKey, * internal conversions applied by BSON APIs. */ static bool -ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) +ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) { bool compatibleTypes = false; @@ -1246,8 +1197,8 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) case INT8OID: case FLOAT4OID: case FLOAT8OID: case NUMERICOID: { - if (bsonType == BSON_INT || bsonType == BSON_LONG || - bsonType == BSON_DOUBLE) + if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || + bsonType == BSON_TYPE_DOUBLE) { compatibleTypes = true; } @@ -1255,8 +1206,8 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) } case BOOLOID: { - if (bsonType == BSON_INT || bsonType == BSON_LONG || - bsonType == BSON_DOUBLE || bsonType == BSON_BOOL) + if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || + bsonType == BSON_TYPE_DOUBLE || bsonType == BSON_TYPE_BOOL) { compatibleTypes = true; } @@ -1266,7 +1217,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) case VARCHAROID: case TEXTOID: { - if (bsonType == BSON_STRING) + if (bsonType == BSON_TYPE_UTF8) { compatibleTypes = true; } @@ -1279,7 +1230,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) * object identifier. We can safely overload this 64-byte data type * since it's reserved for internal use in PostgreSQL. */ - if (bsonType == BSON_OID) + if (bsonType == BSON_TYPE_OID) { compatibleTypes = true; } @@ -1289,7 +1240,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) case TIMESTAMPOID: case TIMESTAMPTZOID: { - if (bsonType == BSON_DATE) + if (bsonType == BSON_TYPE_DATE_TIME) { compatibleTypes = true; } @@ -1303,7 +1254,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) * 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"), + errmsg("cannot convert BSON type to column type"), errhint("Column type: %u", (uint32) columnTypeId))); break; } @@ -1320,7 +1271,7 @@ ColumnTypesCompatible(bson_type bsonType, Oid columnTypeId) * datum from element datums, and returns the array datum. */ static Datum -ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) +ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; @@ -1333,16 +1284,15 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) char typeAlignment = 0; int16 typeLength = 0; - bson_iterator bsonSubIterator = { NULL, 0 }; - bson_iterator_subiterator(bsonIterator, &bsonSubIterator); - - while (bson_iterator_next(&bsonSubIterator)) + BSON_ITERATOR bsonSubIterator = { NULL, 0 }; + BsonIterSubIter(bsonIterator, &bsonSubIterator); + while (BsonIterNext(&bsonSubIterator)) { - bson_type bsonType = bson_iterator_type(&bsonSubIterator); + BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); bool compatibleTypes = false; compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); - if (bsonType == BSON_NULL || !compatibleTypes) + if (bsonType == BSON_TYPE_NULL || !compatibleTypes) { continue; } @@ -1373,7 +1323,7 @@ ColumnValueArray(bson_iterator *bsonIterator, Oid valueTypeId) * datum. The function then returns this datum. */ static Datum -ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) +ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { Datum columnValue = 0; @@ -1381,37 +1331,37 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { case INT2OID: { - int16 value = (int16) bson_iterator_int(bsonIterator); + int16 value = (int16) BsonIterInt32(bsonIterator); columnValue = Int16GetDatum(value); break; } case INT4OID: { - int32 value = bson_iterator_int(bsonIterator); + int32 value = BsonIterInt32(bsonIterator); columnValue = Int32GetDatum(value); break; } case INT8OID: { - int64 value = bson_iterator_long(bsonIterator); + int64 value = BsonIterInt64(bsonIterator); columnValue = Int64GetDatum(value); break; } case FLOAT4OID: { - float4 value = (float4) bson_iterator_double(bsonIterator); + float4 value = (float4) BsonIterDouble(bsonIterator); columnValue = Float4GetDatum(value); break; } case FLOAT8OID: { - float8 value = bson_iterator_double(bsonIterator); + float8 value = BsonIterDouble(bsonIterator); columnValue = Float8GetDatum(value); break; } case NUMERICOID: { - float8 value = bson_iterator_double(bsonIterator); + float8 value = BsonIterDouble(bsonIterator); Datum valueDatum = Float8GetDatum(value); /* overlook type modifiers for numeric */ @@ -1420,13 +1370,13 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case BOOLOID: { - bool value = bson_iterator_bool(bsonIterator); + bool value = BsonIterBool(bsonIterator); columnValue = BoolGetDatum(value); break; } case BPCHAROID: { - const char *value = bson_iterator_string(bsonIterator); + const char *value = BsonIterString(bsonIterator); Datum valueDatum = CStringGetDatum(value); columnValue = DirectFunctionCall3(bpcharin, valueDatum, @@ -1436,7 +1386,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case VARCHAROID: { - const char *value = bson_iterator_string(bsonIterator); + const char *value = BsonIterString(bsonIterator); Datum valueDatum = CStringGetDatum(value); columnValue = DirectFunctionCall3(varcharin, valueDatum, @@ -1446,16 +1396,16 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case TEXTOID: { - const char *value = bson_iterator_string(bsonIterator); + const char *value = BsonIterString(bsonIterator); columnValue = CStringGetTextDatum(value); break; } - case NAMEOID: + case NAMEOID: { char value[NAMEDATALEN]; Datum valueDatum = 0; - bson_oid_t *bsonObjectId = bson_iterator_oid(bsonIterator); + bson_oid_t *bsonObjectId = (bson_oid_t*) BsonIterOid(bsonIterator); bson_oid_to_string(bsonObjectId, value); valueDatum = CStringGetDatum(value); @@ -1466,7 +1416,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } case DATEOID: { - int64 valueMillis = bson_iterator_date(bsonIterator); + int64 valueMillis = BsonIterDate(bsonIterator); int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; Datum timestampDatum = TimestampGetDatum(timestamp); @@ -1476,7 +1426,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) case TIMESTAMPOID: case TIMESTAMPTZOID: { - int64 valueMillis = bson_iterator_date(bsonIterator); + int64 valueMillis = BsonIterDate(bsonIterator); int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; /* overlook type modifiers for timestamp */ @@ -1486,7 +1436,7 @@ ColumnValue(bson_iterator *bsonIterator, Oid columnTypeId, int32 columnTypeMod) default: { ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("cannot convert bson type to column type"), + errmsg("cannot convert BSON type to column type"), errhint("Column type: %u", (uint32) columnTypeId))); break; } @@ -1504,15 +1454,20 @@ static void MongoFreeScanState(MongoFdwModifyState *fmstate) { if (fmstate == NULL) - { return; + + if (fmstate->queryDocument) + { + BsonDestroy(fmstate->queryDocument); + fmstate->queryDocument = NULL; } - bson_destroy(fmstate->queryDocument); - bson_dispose(fmstate->queryDocument); + if (fmstate->mongoCursor) + { + MongoCursorDestroy(fmstate->mongoCursor); + fmstate->mongoCursor = NULL; + } - mongo_cursor_destroy(fmstate->mongoCursor); - mongo_cursor_dispose(fmstate->mongoCursor); /* Release remote connection */ ReleaseConnection(fmstate->mongoConnection); } @@ -1535,6 +1490,7 @@ MongoAnalyzeForeignTable(Relation relation, double foreignTableSize = 0; foreignTableId = RelationGetRelid(relation); + documentCount = ForeignTableDocumentCount(foreignTableId); if (documentCount > 0.0) @@ -1597,8 +1553,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, AttrNumber columnCount = 0; AttrNumber columnId = 0; HTAB *columnMappingHash = NULL; - mongo_cursor *mongoCursor = NULL; - bson *queryDocument = NULL; + MONGO_CURSOR *mongoCursor = NULL; + BSON *queryDocument = NULL; List *columnList = NIL; ForeignScanState *scanState = NULL; List *foreignPrivateList = NIL; @@ -1635,7 +1591,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, foreignPrivateList = list_make1(columnList); /* only clean up the query struct, but not its data */ - bson_dispose(queryDocument); + BsonDestroy(queryDocument); foreignScan = makeNode(ForeignScan); foreignScan->fdw_private = foreignPrivateList; @@ -1666,8 +1622,6 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, for (;;) { - int32 cursorStatus = MONGO_ERROR; - /* check for user-requested abort or sleep */ vacuum_delay_point(); @@ -1675,10 +1629,9 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, memset(columnValues, 0, columnCount * sizeof(Datum)); memset(columnNulls, true, columnCount * sizeof(bool)); - cursorStatus = mongo_cursor_next(mongoCursor); - if (cursorStatus == MONGO_OK) + if(MongoCursorNext(mongoCursor, NULL)) { - const bson *bsonDocument = mongo_cursor_bson(mongoCursor); + const BSON *bsonDocument = MongoCursorBson(mongoCursor); const char *bsonDocumentKey = NULL; /* top level document */ /* fetch next tuple */ @@ -1686,25 +1639,27 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, MemoryContextSwitchTo(tupleContext); FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); MemoryContextSwitchTo(oldContext); } else { + #ifndef META_DRIVER /* * The following is a courtesy check. In practice when Mongo shuts down, - * mongo_cursor_next() could possibly crash. + * mongo_cursor__next() could possibly crash. */ mongo_cursor_error_t errorCode = mongoCursor->err; + if (errorCode != MONGO_CURSOR_EXHAUSTED) { MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), + ereport(ERROR, (errmsg("could not iterate over mongo 11collection"), errhint("Mongo driver cursor error code: %d", errorCode))); } - + #endif break; } @@ -1745,8 +1700,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, heap_freetuple(sampleRows[rowIndex]); sampleRows[rowIndex] = heap_form_tuple(tupleDescriptor, - columnValues, - columnNulls); + columnValues, + columnNulls); } rowCountToSkip -= 1; diff --git a/mongo_fdw.h b/mongo_fdw.h index 8be4d10..f72af94 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -12,11 +12,16 @@ #ifndef MONGO_FDW_H #define MONGO_FDW_H +#include "config.h" +#include "mongo_wrapper.h" #include "bson.h" -#include "mongo.h" -#include "postgres.h" -#include "utils/hsearch.h" +#ifdef META_DRIVER + #include "mongoc.h" +#else + #include "mongo.h" +#endif + #include "fmgr.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" @@ -45,7 +50,29 @@ #include "utils/rel.h" #include "utils/memutils.h" - +#ifdef META_DRIVER + #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 +#else + #define BSON bson + #define BSON_TYPE bson_type + #define BSON_ITERATOR bson_iterator + #define MONGO_CONN mongo + #define MONGO_CURSOR mongo_cursor + #define BSON_TYPE_DOCUMENT BSON_OBJECT + #define BSON_TYPE_NULL BSON_NULL + #define BSON_TYPE_ARRAY BSON_ARRAY + #define BSON_TYPE_INT32 BSON_INT + #define BSON_TYPE_INT64 BSON_LONG + #define BSON_TYPE_DOUBLE BSON_DOUBLE + #define BSON_TYPE_BOOL BSON_BOOL + #define BSON_TYPE_UTF8 BSON_STRING + #define BSON_TYPE_OID BSON_OID + #define BSON_TYPE_DATE_TIME BSON_DATE +#endif /* Defines for valid option names */ #define OPTION_NAME_ADDRESS "address" @@ -105,37 +132,35 @@ typedef struct MongoFdwOptions int32 portNumber; char *databaseName; char *collectionName; - } MongoFdwOptions; /* -* MongoFdwExecState keeps foreign data wrapper specific execution state that we -* create and hold onto when executing the query. -*/ + * 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. -*/ + * 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 */ + 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 */ + int p_nums; /* number of parameters to transmit */ + FmgrInfo *p_flinfo; /* output conversion functions for them */ - struct HTAB *columnMappingHash; + struct HTAB *columnMappingHash; - mongo *mongoConnection; /* MongoDB connection */ - mongo_cursor *mongoCursor; /* MongoDB cursor */ - bson *queryDocument; /* Bson Document */ + MONGO_CONN *mongoConnection; /* MongoDB connection */ + MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ + BSON *queryDocument; /* Bson Document */ - MongoFdwOptions *mongoFdwOptions; + MongoFdwOptions *mongoFdwOptions; /* working memory context */ - MemoryContext temp_cxt; /* context for per-tuple temporary data */ + MemoryContext temp_cxt; /* context for per-tuple temporary data */ } MongoFdwModifyState; @@ -155,19 +180,23 @@ typedef struct ColumnMapping } ColumnMapping; +extern MONGO_CONN *GetConnection(char *host, int32 port); +extern void cleanup_connection(void); +extern void ReleaseConnection(MONGO_CONN* conn); + +extern StringInfo OptionNamesString(Oid currentContextId); + /* Function declarations related to creating the mongo query */ extern List * ApplicableOpExpressionList(RelOptInfo *baserel); -extern bson * QueryDocument(Oid relationId, List *opExpressionList); +extern BSON * QueryDocument(Oid relationId, List *opExpressionList); extern List * ColumnList(RelOptInfo *baserel); +extern MongoFdwOptions * MongoGetOptions(Oid foreignTableId); +extern void MongoFreeOptions(MongoFdwOptions *mongoFdwOptions); + /* Function declarations for foreign data wrapper */ extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); -extern MongoFdwOptions * MongoGetOptions(Oid foreignTableId); -extern void MongoFreeOptions(MongoFdwOptions *mongoFdwOptions); -mongo* GetConnection(char *host, int32 port); -void cleanup_connection(void); -void ReleaseConnection(mongo *conn); #endif /* MONGO_FDW_H */ diff --git a/mongo_query.c b/mongo_query.c index 426e801..a559a0b 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -12,6 +12,13 @@ */ #include "postgres.h" +#include "mongo_wrapper.h" + +#ifdef META_DRIVER + #include "mongoc.h" +#else + #include "mongo.h" +#endif #include "mongo_fdw.h" #include "mongo_query.h" @@ -33,7 +40,7 @@ static char * MongoOperatorName(const char *operatorName); static List * EqualityOperatorList(List *operatorList); static List * UniqueColumnList(List *operatorList); static List * ColumnOperatorList(Var *column, List *operatorList); -static void AppendConstantValue(bson *queryDocument, const char *keyName, +static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); @@ -154,7 +161,7 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" become * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". */ -bson * +BSON * QueryDocument(Oid relationId, List *opExpressionList) { List *equalityOperatorList = NIL; @@ -162,12 +169,9 @@ QueryDocument(Oid relationId, List *opExpressionList) List *columnList = NIL; ListCell *equalityOperatorCell = NULL; ListCell *columnCell = NULL; - bson *queryDocument = NULL; - int documentStatus = BSON_OK; - - queryDocument = bson_create(); - bson_init(queryDocument); + BSON *queryDocument = NULL; + queryDocument = BsonCreate(); /* * We distinguish between equality expressions and others since we need to * insert the latter (<, >, <=, >=, <>) as separate sub-documents into the @@ -210,6 +214,7 @@ QueryDocument(Oid relationId, List *opExpressionList) char *columnName = NULL; List *columnOperatorList = NIL; ListCell *columnOperatorCell = NULL; + BSON r; columnId = column->varattno; columnName = get_relid_attribute_name(relationId, columnId); @@ -218,7 +223,7 @@ QueryDocument(Oid relationId, List *opExpressionList) columnOperatorList = ColumnOperatorList(column, comparisonOperatorList); /* for comparison expressions, start a sub-document */ - bson_append_start_object(queryDocument, columnName); + BsonAppendStartObject(queryDocument, columnName, &r); foreach(columnOperatorCell, columnOperatorList) { @@ -234,15 +239,12 @@ QueryDocument(Oid relationId, List *opExpressionList) AppendConstantValue(queryDocument, mongoOperatorName, constant); } - - bson_append_finish_object(queryDocument); + BsonAppendFinishObject(queryDocument, &r); } - - documentStatus = bson_finish(queryDocument); - if (documentStatus != BSON_OK) + if (!BsonFinish(queryDocument)) { ereport(ERROR, (errmsg("could not create document for query"), - errhint("BSON error: %s", queryDocument->errstr))); + errhint("BSON error"))); } return queryDocument; @@ -362,24 +364,23 @@ ColumnOperatorList(Var *column, List *operatorList) * its MongoDB equivalent. */ static void -AppendConstantValue(bson *queryDocument, const char *keyName, Const *constant) +AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) { if (constant->constisnull) { - bson_append_null(queryDocument, keyName); + BsonAppendNull(queryDocument, keyName); return; } - AppenMongoValue(queryDocument, keyName, constant->constvalue, constant->consttype, false); + AppenMongoValue(queryDocument, keyName, constant->constvalue, false, constant->consttype); } -int32 -AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) +bool +AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) { - int32 status = MONGO_ERROR; - + bool status; if (isnull) { - status = bson_append_null(queryDocument, keyName); + status = BsonAppendNull(queryDocument, keyName); return status; } @@ -388,44 +389,44 @@ AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnu case INT2OID: { int16 valueInt = DatumGetInt16(value); - status = bson_append_int(queryDocument, keyName, (int) valueInt); + status = BsonAppendInt32(queryDocument, keyName, (int) valueInt); break; } case INT4OID: { int32 valueInt = DatumGetInt32(value); - status = bson_append_int(queryDocument, keyName, valueInt); + status = BsonAppendInt32(queryDocument, keyName, valueInt); break; } case INT8OID: { int64 valueLong = DatumGetInt64(value); - status = bson_append_long(queryDocument, keyName, valueLong); + status = BsonAppendInt64(queryDocument, keyName, valueLong); break; } case FLOAT4OID: { float4 valueFloat = DatumGetFloat4(value); - status = bson_append_double(queryDocument, keyName, (double) valueFloat); + status = BsonAppendDouble(queryDocument, keyName, (double) valueFloat); break; } case FLOAT8OID: { float8 valueFloat = DatumGetFloat8(value); - status = bson_append_double(queryDocument, keyName, valueFloat); + status = BsonAppendDouble(queryDocument, keyName, valueFloat); break; } case NUMERICOID: { Datum valueDatum = DirectFunctionCall1(numeric_float8, value); float8 valueFloat = DatumGetFloat8(valueDatum); - status = bson_append_double(queryDocument, keyName, valueFloat); + status = BsonAppendDouble(queryDocument, keyName, valueFloat); break; } case BOOLOID: { bool valueBool = DatumGetBool(value); - status = bson_append_int(queryDocument, keyName, (int) valueBool); + status = BsonAppendBool(queryDocument, keyName, (int) valueBool); break; } case BPCHAROID: @@ -437,7 +438,7 @@ AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnu bool typeVarLength = false; getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - status = bson_append_string(queryDocument, keyName, outputString); + status = BsonAppendUTF8(queryDocument, keyName, outputString); break; } case NAMEOID: @@ -449,8 +450,8 @@ AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnu memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - bson_oid_from_string(&bsonObjectId, outputString); - status = bson_append_oid(queryDocument, keyName, &bsonObjectId); + BsonOidFromString(&bsonObjectId, outputString); + status = BsonAppendOid(queryDocument, keyName, &bsonObjectId); break; } case DATEOID: @@ -460,7 +461,7 @@ AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnu int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - status = bson_append_date(queryDocument, keyName, valueMilliSecs); + status = BsonAppendDate(queryDocument, keyName, valueMilliSecs); break; } case TIMESTAMPOID: @@ -470,7 +471,7 @@ AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnu int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - status = bson_append_date(queryDocument, keyName, valueMilliSecs); + status = BsonAppendDate(queryDocument, keyName, valueMilliSecs); break; } default: diff --git a/mongo_query.h b/mongo_query.h index 87b95e5..607bb2d 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -12,7 +12,6 @@ #ifndef MONGO_QUERY_H #define MONGO_QUERY_H -int32 AppenMongoValue(bson *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); +bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); - -#endif /* MONGO_QUERY_H */ +#endif /* MONGO_QUERY_H */ From 3dd8ef6685d7d3a5709a367367d4ea726d29003a Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:25:02 +0500 Subject: [PATCH 021/239] Added files missed during last commits. --- Makefile.legacy | 42 +++++ Makefile.meta | 44 +++++ config.h | 5 + connection.c | 141 +++++++++++++++ mongo_wrapper.c | 318 +++++++++++++++++++++++++++++++++ mongo_wrapper.h | 62 +++++++ mongo_wrapper_meta.c | 407 +++++++++++++++++++++++++++++++++++++++++++ option.c | 232 ++++++++++++++++++++++++ 8 files changed, 1251 insertions(+) create mode 100644 Makefile.legacy create mode 100644 Makefile.meta create mode 100644 config.h create mode 100644 connection.c create mode 100644 mongo_wrapper.c create mode 100644 mongo_wrapper.h create mode 100644 mongo_wrapper_meta.c create mode 100644 option.c diff --git a/Makefile.legacy b/Makefile.legacy new file mode 100644 index 0000000..88e6ce0 --- /dev/null +++ b/Makefile.legacy @@ -0,0 +1,42 @@ +# mongo_fdw/Makefile +# +# Copyright (c) 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. +# + +MONGO_DRIVER = mongo-c-driver-v0.6 +MONGO_PATH = $(MONGO_DRIVER)/src +MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os + +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) + +EXTENSION = mongo_fdw +DATA = mongo_fdw--1.0.sql + +$(MONGO_DRIVER)/%.os: + $(MAKE) -C $(MONGO_DRIVER) $*.os + +# +# 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), 9.3 9.4)) + $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) +endif diff --git a/Makefile.meta b/Makefile.meta new file mode 100644 index 0000000..10a12e4 --- /dev/null +++ b/Makefile.meta @@ -0,0 +1,44 @@ +# mongo_fdw/Makefile +# +# Copyright (c) 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. +# + +MONGO_DRIVER = mongo-c-meta-driver +MONGO_PATH = $(MONGO_DRIVER)/src/mongoc +MONGO_INCLUDE = -I$(MONGO_DRIVER)/src/libbson/src/bson/ -I$(MONGO_PATH) +PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) +SHLIB_LINK = -L$(MONGO_DRIVER)/.libs -lmongoc-1.0 +SHLIB_LINK += -L$(MONGO_DRIVER)/src/libbson/.libs -lbson-1.0 + +OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o + +EXTENSION = mongo_fdw +DATA = mongo_fdw--1.0.sql + +$(MONGO_DRIVER)/%.os: + $(MAKE) -C $(MONGO_DRIVER) $*.os + +# +# 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), 9.3 9.4)) + $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) +endif diff --git a/config.h b/config.h new file mode 100644 index 0000000..6139f3e --- /dev/null +++ b/config.h @@ -0,0 +1,5 @@ +/* + * Define if you want to compile the MongoFDW with Meta C Driver, otherwise + * it will compile using MongoDB legacy C Driver +*/ +/* #define META_DRIVER */ diff --git a/connection.c b/connection.c new file mode 100644 index 0000000..9b8660e --- /dev/null +++ b/connection.c @@ -0,0 +1,141 @@ +/*------------------------------------------------------------------------- + * + * connection.c + * Connection management functions for mongo_fdw + * + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * + *------------------------------------------------------------------------- + */ +#include + + +#include "postgres.h" +#include "mongo_wrapper.h" +#include "mongo_fdw.h" + +#include "access/xact.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/hsearch.h" +#include "utils/memutils.h" +#include "utils/resowner.h" + +/* Length of host */ +#define HOST_LEN 256 + +/* + * Connection cache hash table entry + * + * The lookup key in this hash table is the foreign Mongo server name / IP + * and the server port number. (We use just one connection per user per foreign server, + * so that we can ensure all scans use the same snapshot during a query.) + * + * The "conn" pointer can be NULL if we don't currently have a live connection. + */ +typedef struct ConnCacheKey +{ + char host[HOST_LEN]; /* MongoDB's host name / IP address */ + int32 port; /* MongoDB's port number */ +} ConnCacheKey; + +typedef struct ConnCacheEntry +{ + ConnCacheKey key; /* hash key (must be first) */ + MONGO_CONN *conn; /* connection to foreign server, or NULL */ +} ConnCacheEntry; + +/* + * Connection cache (initialized on first use) + */ +static HTAB *ConnectionHash = NULL; + +/* + * GetConnection: + * Get a mong 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* +GetConnection(char *host, int32 port) +{ + 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); + } + + /* Create hash key for the entry */ + memset(key.host, 0, HOST_LEN); + strncpy(key.host, host, HOST_LEN); + key.port = port; + + /* + * 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 (entry->conn == NULL) + { + entry->conn = MongoConnect(host, port); + elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", + entry->conn, host, port); + } + + return entry->conn; +} + +/* + * cleanup_connection: + * Delete all the cache entries on backend exists. + */ +void +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; + } +} + +/* + * Release connection created by calling GetConnection. + */ +void +ReleaseConnection(MONGO_CONN *conn) +{ + /* + * We don't close the connection indvisually here, will do all connection + * cleanup on the backend exit. + */ +} + diff --git a/mongo_wrapper.c b/mongo_wrapper.c new file mode 100644 index 0000000..9c77c41 --- /dev/null +++ b/mongo_wrapper.c @@ -0,0 +1,318 @@ +/*------------------------------------------------------------------------- + * + * mongo_wrapper.c + * + * Wrapper functions for MongoDB's old legacy Driver. + * + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "mongo_wrapper.h" + +#ifdef META_DRIVER + #include "mongoc.h" +#else + #include "mongo.h" +#endif + +#include "mongo_fdw.h" + +#define QUAL_STRING_LEN 512 + +MONGO_CONN* +MongoConnect(const char* host, const unsigned short port) +{ + MONGO_CONN *conn; + conn = mongo_create(); + mongo_init(conn); + + if (mongo_connect(conn, host, port) != MONGO_OK) + { + int err = conn->err; + mongo_destroy(conn); + mongo_dispose(conn); + ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), + errhint("Mongo driver connection error: %d", err))); + } + return conn; +} + +void +MongoDisconnect(MONGO_CONN* conn) +{ + mongo_destroy(conn); + mongo_dispose(conn); +} + +bool +MongoInsert(MONGO_CONN* conn, char* database, char *collection, bson* b) +{ + char qual[QUAL_STRING_LEN]; + + snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + if (mongo_insert(conn, qual, b, NULL) != MONGO_OK) + ereport(ERROR, (errmsg("failed to insert row"), + errhint("Mongo driver insert error: %d", conn->err))); + return true; +} + + +bool +MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* op) +{ + char qual[QUAL_STRING_LEN]; + + snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + if (mongo_update(conn, qual, b, op, MONGO_UPDATE_BASIC, 0) != MONGO_OK) + ereport(ERROR, (errmsg("failed to update row"), + errhint("Mongo driver update error: %d", conn->err))); + return true; +} + + +bool +MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) +{ + char qual[QUAL_STRING_LEN]; + + snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + if (mongo_remove(conn, qual, b , NULL) != MONGO_OK) + ereport(ERROR, (errmsg("failed to delete row"), + errhint("Mongo driver delete error: %d", conn->err))); + + return true; +} + + +MONGO_CURSOR* +MongoCursorCreate(MONGO_CONN* conn, char* database, char *collection, BSON* q) +{ + MONGO_CURSOR* c; + char qual[QUAL_STRING_LEN]; + + snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + c = mongo_cursor_create(); + mongo_cursor_init(c, conn , qual); + mongo_cursor_set_query(c, q); + return c; +} + + +const bson* +MongoCursorBson(MONGO_CURSOR* c) +{ + return mongo_cursor_bson(c); +} + + +bool +MongoCursorNext(MONGO_CURSOR* c, BSON* b) +{ + return (mongo_cursor_next(c) == MONGO_OK); +} + +void +MongoCursorDestroy(MONGO_CURSOR* c) +{ + mongo_cursor_destroy(c); + mongo_cursor_dispose(c); +} + +BSON* +BsonCreate() +{ + BSON *b = NULL; + b = bson_create(); + bson_init(b); + return b; +} + +void +BsonDestroy(BSON *b) +{ + bson_destroy(b); + bson_dispose(b); +} + +bool +BsonIterInit(BSON_ITERATOR *it, BSON *b) +{ + bson_iterator_init(it, b); + return true; +} + +bool +BsonIterSubObject(BSON_ITERATOR *it, BSON *b) +{ + bson_iterator_subobject(it, b); + return true; +} + +int32_t +BsonIterInt32(BSON_ITERATOR *it) +{ + return bson_iterator_int(it); +} + + +int64_t +BsonIterInt64(BSON_ITERATOR *it) +{ + return bson_iterator_long(it); +} + + +double +BsonIterDouble(BSON_ITERATOR *it) +{ + return bson_iterator_double(it); +} + + +bool +BsonIterBool(BSON_ITERATOR *it) +{ + return bson_iterator_bool(it); +} + + +const char* +BsonIterString(BSON_ITERATOR *it) +{ + return bson_iterator_string(it); +} + +const bson_oid_t * +BsonIterOid(BSON_ITERATOR *it) +{ + return bson_iterator_oid(it); +} + + +time_t +BsonIterDate(BSON_ITERATOR *it) +{ + return bson_iterator_date(it); +} + + +const char* +BsonIterKey(BSON_ITERATOR *it) +{ + return bson_iterator_key(it); +} + +int +BsonIterType(BSON_ITERATOR *it) +{ + return bson_iterator_type(it); +} + +int +BsonIterNext(BSON_ITERATOR *it) +{ + return bson_iterator_next(it); +} + + +bool +BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR* sub) +{ + bson_iterator_subiterator(it, sub); + return true; +} + + +void +BsonOidFromString(bson_oid_t *o, char* str) +{ + bson_oid_from_string(o, str); +} + + +bool +BsonAppendOid(BSON *b, const char* key, bson_oid_t *v) +{ + return (bson_append_oid(b, key, v) == MONGO_OK); +} + +bool +BsonAppendBool(BSON *b, const char* key, bool v) +{ + return (bson_append_int(b, key, v) == MONGO_OK); +} + +bool +BsonAppendNull(BSON *b, const char* key) +{ + return (bson_append_null(b, key) == MONGO_OK); +} + + +bool +BsonAppendInt32(BSON *b, const char* key, int v) +{ + return (bson_append_int(b, key, v) == MONGO_OK); +} + + +bool +BsonAppendInt64(BSON *b, const char* key, int64_t v) +{ + return (bson_append_long(b, key, v) == MONGO_OK); +} + +bool +BsonAppendDouble(BSON *b, const char* key, double v) +{ + return (bson_append_double(b, key, v) == MONGO_OK); +} + +bool +BsonAppendUTF8(BSON *b, const char* key, char *v) +{ + return (bson_append_string(b, key, v) == MONGO_OK); +} + +bool +BsonAppendDate(BSON *b, const char* key, time_t v) +{ + return (bson_append_date(b, key, v) == MONGO_OK); +} + + +bool +BsonAppendStartObject(BSON* b, char *key, BSON* r) +{ + return (bson_append_start_object(b, key) == MONGO_OK); +} + +bool +BsonAppendFinishObject(BSON* b, BSON* r) +{ + return (bson_append_finish_object(b) == MONGO_OK); +} + + +bool +BsonAppendBson(BSON* b, char *key, BSON* c) +{ + return (bson_append_bson(b, key, c) == MONGO_OK); +} + + +bool +BsonFinish(BSON* b) +{ + return (bson_finish(b) == MONGO_OK); +} + +int +MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) +{ + return mongo_count(conn, database, collection, b); +} + diff --git a/mongo_wrapper.h b/mongo_wrapper.h new file mode 100644 index 0000000..6cc8c21 --- /dev/null +++ b/mongo_wrapper.h @@ -0,0 +1,62 @@ +/*------------------------------------------------------------------------- + * + * mongo_wrapper.h + * + *------------------------------------------------------------------------- + */ + +#ifndef MONGO_WRAPPER_H +#define MONGO_WRAPPER_H + +#include "mongo_fdw.h" +#include "bson.h" + +#ifdef META_DRIVER + #include "mongoc.h" +#else + #include "mongo.h" +#endif + +MONGO_CONN* MongoConnect(const char* host, const unsigned short port); +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); +int 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 bson_oid_t * BsonIterOid(BSON_ITERATOR *it); +time_t BsonIterDate(BSON_ITERATOR *it); +const char* BsonIterKey(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); + +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 BsonAppendDate(BSON *b, const char* key, time_t v); +bool BsonAppendStartObject(BSON* b, char *key, BSON *r); +bool BsonAppendFinishObject(BSON* b, BSON* r); +bool BsonAppendBson(BSON* b, char *key, BSON* c); +bool BsonFinish(BSON* b); +#endif diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c new file mode 100644 index 0000000..33ec2c5 --- /dev/null +++ b/mongo_wrapper_meta.c @@ -0,0 +1,407 @@ +/*------------------------------------------------------------------------- + * + * mongo_wrapper.c + * + * Wrapper functions for MongoDB's new Meta Driver. + * + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include + +#include "mongo_wrapper.h" + +/* + * Connect to MongoDB server using Host/ip and Port number. + */ +MONGO_CONN* +MongoConnect(const char* host, const unsigned short port) +{ + MONGO_CONN *client = NULL; + char* uri = NULL; + + uri = bson_strdup_printf ("mongodb://%s:%hu/", host, port); + + client = mongoc_client_new(uri); + + bson_free(uri); + + if (client == NULL) + ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), + errhint("Mongo driver connection error"))); + return client; +} + +/* + * Disconnect from MongoDB server. + */ +void +MongoDisconnect(MONGO_CONN* conn) +{ + if (conn) + mongoc_client_destroy(conn); +} + + +/* + * Insert a document 'b' into MongoDB. + */ +bool +MongoInsert(MONGO_CONN* conn, char *database, char* collection, BSON* b) +{ + mongoc_collection_t *c = NULL; + 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; +} + + +/* + * Update a document 'b' into MongoDB. + */ +bool +MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* op) +{ + mongoc_collection_t *c = NULL; + 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; +} + + +/* + * Delete MongoDB's document. + */ +bool +MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) +{ + mongoc_collection_t *c = NULL; + bson_error_t error; + bool r = false; + + c = mongoc_client_get_collection (conn, database, collection); + + r = mongoc_collection_delete(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; +} + +/* + * 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 = NULL; + MONGO_CURSOR *cur = NULL; + bson_error_t error; + + c = mongoc_client_get_collection (conn, database, collection); + cur = mongoc_collection_find(c, MONGOC_QUERY_NONE, 0, 0, 0, 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; +} + + +/* + * Destroy cursor created by calling MongoCursorCreate function. + */ +void +MongoCursorDestroy(MONGO_CURSOR* c) +{ + mongoc_cursor_destroy(c); +} + + +/* + * Get the current document from cursor. + */ +const BSON* +MongoCursorBson(MONGO_CURSOR* c) +{ + return mongoc_cursor_current(c); +} + +/* + * Get the next document from the cursor. + */ +bool +MongoCursorNext(MONGO_CURSOR* c, BSON *b) +{ + return mongoc_cursor_next(c, (const BSON**) &b); +} + + +/* + * 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 *b = NULL; + b = bson_new(); + bson_init(b); + return b; +} + +/* + * Destroy Bson objected created by BsonCreate function. + */ +void +BsonDestroy(BSON *b) +{ + bson_destroy(b); +} + + +/* + * Initialize the bson Iterator. + */ +bool +BsonIterInit(BSON_ITERATOR *it, BSON *b) +{ + return bson_iter_init(it, b); +} + + +bool +BsonIterSubObject(BSON_ITERATOR *it, BSON *b) +{ + /* TODO: Need to see the Meta Driver equalient for "bson_iterator_subobject" */ + return true; +} + +int32_t +BsonIterInt32(BSON_ITERATOR *it) +{ + return bson_iter_int32(it); +} + + +int64_t +BsonIterInt64(BSON_ITERATOR *it) +{ + return bson_iter_int64(it); +} + + +double +BsonIterDouble(BSON_ITERATOR *it) +{ + return bson_iter_double(it); +} + + +bool +BsonIterBool(BSON_ITERATOR *it) +{ + return bson_iter_bool(it); +} + + +const char* +BsonIterString(BSON_ITERATOR *it) +{ + uint32_t len = 0; + return bson_iter_utf8(it, &len); +} + + +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 +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 +BsonFinish(BSON* b) +{ + /* + * There is no need for bson_finish in Meta Driver. + * We are doing nothing, just because of compatiblity with legacy + * driver. + */ + return true; +} + +/* + * Count the number of documents. + */ +int +MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) +{ + BSON *cmd = NULL; + BSON *out; + double count = -1; + bool r = false; + bson_error_t error; + mongoc_collection_t *c = NULL; + + c = mongoc_client_get_collection (conn, database, collection); + + cmd = BsonCreate(); + out = BsonCreate(); + BsonAppendUTF8(cmd, "count", (char*)collection); + if (b) /* not empty */ + BsonAppendBson(cmd, "query", (BSON*)b); + + BsonFinish(cmd); + r = mongoc_collection_command_simple(c, cmd, NULL, out, &error); + if (r) + { + bson_iter_t it; + if (bson_iter_init_find(&it, out, "n")) + count = BsonIterDouble(&it); + } + mongoc_collection_destroy(c); + + BsonDestroy(out); + BsonDestroy(cmd); + return (int)count; +} diff --git a/option.c b/option.c new file mode 100644 index 0000000..b7730ab --- /dev/null +++ b/option.c @@ -0,0 +1,232 @@ +/*------------------------------------------------------------------------- + * + * option.c + * + * Function definitions for MongoDB foreign data wrapper. These functions access + * data stored in MongoDB through the official C driver. + * + * Copyright (c) 2012-2014 Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "mongo_wrapper.h" +#include "mongo_fdw.h" + +#include "access/reloptions.h" +#include "catalog/pg_type.h" +#include "commands/defrem.h" +#include "commands/explain.h" +#include "commands/vacuum.h" +#include "foreign/fdwapi.h" +#include "foreign/foreign.h" +#include "nodes/makefuncs.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/hsearch.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/memutils.h" + + +static char * MongoGetOptionValue(Oid foreignTableId, const char *optionName); + +/* + * 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 = NULL; + + foreach(optionCell, optionList) + { + DefElem *optionDef = (DefElem *) lfirst(optionCell); + char *optionName = optionDef->defname; + bool optionValid = false; + + int32 optionIndex = 0; + for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) + { + const MongoValidOption *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(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 *optionValue = defGetString(optionDef); + int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); + (void) portNumber; + } + } + PG_RETURN_VOID(); +} + +/* + * OptionNamesString finds all options that are valid for the current context, + * and concatenates these option names in a comma separated string. + */ +StringInfo +OptionNamesString(Oid currentContextId) +{ + StringInfo optionNamesString = makeStringInfo(); + bool firstOptionPrinted = false; + + int32 optionIndex = 0; + for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) + { + const MongoValidOption *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; +} + + +/* + * MongoGetOptions 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 * +MongoGetOptions(Oid foreignTableId) +{ + MongoFdwOptions *mongoFdwOptions = NULL; + char *addressName = NULL; + char *portName = NULL; + int32 portNumber = 0; + char *databaseName = NULL; + char *collectionName = NULL; + + addressName = MongoGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS); + if (addressName == NULL) + { + addressName = pstrdup(DEFAULT_IP_ADDRESS); + } + + portName = MongoGetOptionValue(foreignTableId, OPTION_NAME_PORT); + if (portName == NULL) + { + portNumber = DEFAULT_PORT_NUMBER; + } + else + { + portNumber = pg_atoi(portName, sizeof(int32), 0); + } + + databaseName = MongoGetOptionValue(foreignTableId, OPTION_NAME_DATABASE); + if (databaseName == NULL) + { + databaseName = pstrdup(DEFAULT_DATABASE_NAME); + } + + collectionName = MongoGetOptionValue(foreignTableId, OPTION_NAME_COLLECTION); + if (collectionName == NULL) + { + collectionName = get_rel_name(foreignTableId); + } + + mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); + mongoFdwOptions->addressName = addressName; + mongoFdwOptions->portNumber = portNumber; + mongoFdwOptions->databaseName = databaseName; + mongoFdwOptions->collectionName = collectionName; + + return mongoFdwOptions; +} + + +void +MongoFreeOptions(MongoFdwOptions *mongoFdwOptions) +{ + if (mongoFdwOptions) + { + pfree(mongoFdwOptions->addressName); + pfree(mongoFdwOptions->databaseName); + pfree(mongoFdwOptions); + } +} + +/* + * MongoGetOptionValue walks over foreign table and foreign server options, and + * looks for the option with the given name. If found, the function returns the + * option's value. + */ +static char * +MongoGetOptionValue(Oid foreignTableId, const char *optionName) +{ + ForeignTable *foreignTable = NULL; + ForeignServer *foreignServer = NULL; + List *optionList = NIL; + ListCell *optionCell = NULL; + char *optionValue = NULL; + + foreignTable = GetForeignTable(foreignTableId); + foreignServer = GetForeignServer(foreignTable->serverid); + + optionList = list_concat(optionList, foreignTable->options); + optionList = list_concat(optionList, foreignServer->options); + + foreach(optionCell, optionList) + { + DefElem *optionDef = (DefElem *) lfirst(optionCell); + char *optionDefName = optionDef->defname; + + if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) + { + optionValue = defGetString(optionDef); + break; + } + } + + return optionValue; +} + From aacb0c80dd89c32357d9d89e7307d04a7b4002b3 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:49:15 +0500 Subject: [PATCH 022/239] Restrict first column name of MongoDB's foreign table to "_id". --- mongo_fdw.c | 45 +++++++++++---------------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 2bc67b1..899130a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -136,19 +136,6 @@ static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); -/* - * Generate new 24 character's rowid (MongoDB's "_id") - * from a string using pad character 'A'. - */ -#define UNIQUE_OID(x, z) do \ -{ \ - char y[25]; \ - memset(y, 'A', sizeof(y));\ - y[24] = 0; \ - strcpy(y, x); \ - BsonOidFromString(&z, y);\ -} while(0); - /* declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -767,33 +754,23 @@ MongoExecForeignInsert(EState *estate, { int attnum = lfirst_int(lc); value = slot_getattr(slot, attnum, &isnull); + + /* first column of MongoDB's foreign table must be _id */ + if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) + elog(ERROR, "first colum of MongoDB's foreign table must be \"_id\""); + if (attnum == 1) { /* - * We also disallow null values to the first column which - * happens to be the row identifier in MongoDb (_id). + * 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 (isnull) - elog(ERROR, "null value for first column (row identifier column) is not supported"); - - getTypeOutputInfo(typoid, &outputFunctionId, &typeVarLength); - outputString = OidOutputFunctionCall(outputFunctionId, value); - - /* MongoDB support 24 character's _id (row identifier) */ - if (strlen(outputString) > 24) - elog(ERROR, "first column (row identifier column) should be max 24 characters"); - - UNIQUE_OID(outputString, bsonObjectId); - - /* Append rowid field which is "_id" in MongoDB */ - if(!BsonAppendOid(b, "_id", &bsonObjectId)) - ereport(ERROR, - (errcode(ERRCODE_FDW_ERROR), - errmsg("insert failed, invalid value"))); } - AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + else + { + AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); + } } } BsonFinish(b); From b67102ce516e8cb8dfda36a6bda744885b0afa9a Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:53:44 +0500 Subject: [PATCH 023/239] README Changes --- CONTRIBUTING.md | 4 +- README.md | 137 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 104 insertions(+), 37 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df9d264..bd5c88b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ Using Issues `mongo_fdw`'s maintainers prefer that bug reports, feature requests, and pull requests are submitted as [GitHub Issues][1]. If you think you require personal -assistance, please **do not** open an issue: email `engage` `@` `citusdata.com` +assistance, please **do not** open an issue: email `ibrar.ahmed` `@` `enterprisedb.com` instead. @@ -81,5 +81,5 @@ 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/citusdata/mongo_fdw/issues +[1]: https://github.com/EnterpriseDB/mongo_fdw/issues [2]: LICENSE diff --git a/README.md b/README.md index 4b024d5..9065d78 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,41 @@ -MongoDB FDW for PostgreSQL -========================== +MongoDB Foreign Date Wrapper for PostgreSQL +=========================================== -This PostgreSQL extension implements a Foreign Data Wrapper (FDW) for -[MongoDB][1]. For an example demonstrating this wrapper's use, see [our blog -post][2]. Please also note that this version of `mongo_fdw` only works with -PostgreSQL 9.2 or 9.3. +This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper for. + +Please also note that this version of `mongo_fdw` only works with +PostgreSQL Version 9.3 and greater. Installation ------------ -The MongoDB FDW includes the official MongoDB C Driver version 0.6. When you -type `make`, the C driver's source code also gets automatically compiled and -linked. +This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s ""C"" drivers, [legacy driver][6] and [MongoDB][1]'s [Meta Driver][7]. This is compile time decision which driver you want to use with this FDW. + +The [MongoDB][1] FDW includes the [legacy MongoDB C Driver][6] version 0.6. When you +type `make -f Makefile.legacy`, the C driver's source code also gets automatically compiled and linked. The other option is to compile using [MongoDB's Meta Driver][7]. You need to download the Meta C driver from MongoDB site. To build on POSIX-compliant systems (like Linux and OS X), 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: +Compile using MonoDB's legacy C driver. + +```sh +PATH=/usr/local/pgsql/bin/:$PATH make -f Makefile.legacy +sudo PATH=/usr/local/pgsql/bin/:$PATH make install +``` + +Compile using MonoDB's Meta C driver. + +Add `#deine META_DRIVER` in `config.h` file, then + ```sh -PATH=/usr/local/pgsql/bin/:$PATH make +PATH=/usr/local/pgsql/bin/:$PATH make -f Makefile.meta sudo PATH=/usr/local/pgsql/bin/:$PATH make install ``` -Note that we have tested the `mongo_fdw` extension only on Fedora and Ubuntu +Note that we have tested the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3]. @@ -52,38 +64,90 @@ default value mentioned above. estimating costs for the query execution plan. To see selected execution plans for a query, just run `EXPLAIN`. -We also currently use the internal PostgreSQL `NAME` type to represent the BSON -object identifier type (the `_id` field). - ```sql +Examples with MongoDB equelent statments. + -- load extension first time after install -CREATE EXTENSION mongo_fdw; +`CREATE EXTENSION mongo_fdw;` -- create server object -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw -OPTIONS (address '127.0.0.1', port '27017'); +`CREATE SERVER mongo_server + FOREIGN DATA WRAPPER mongo_fdw + OPTIONS (address '127.0.0.1', port '27017');` -- create foreign table -CREATE FOREIGN TABLE customer_reviews +`CREATE FOREIGN TABLE warehouse( + _id NAME, + warehouse_id int, + warehouse_name text, + warehouse_created timestamptz) +SERVER mongo_server + OPTIONS (database 'db', collection 'warehouse');` + + +-- select from table +`SELECT * FROM warehouse WHERE warehouse_id = 1;` + +_id | warehouse_id | warehouse_name | warehouse_created +------------------------+----------------+--------------------------- +53720b1904864dc1f5a571a0| 1 | UPS | 12-DEC-14 12:12:10 +05:00 + + +`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 row in table +`INSERT INTO warehouse values (0, 1, 'UPS', to_date('2014-12-12T07:12:10Z'));` + +`db.warehouse.insert` +( + { + "warehouse_id" : NumberInt(1), + "warehouse_name" : "UPS", + "warehouse_created" : ISODate("2014-12-12T07:12:10Z") + } +); + +-- delete row from table +`DELETE FROM warehouse where warehouse_id = 3;` + +> db.warehouse.remove({"warehouse_id" : 2}) + + +-- update a row of table +`UPDATE warehouse set warehouse_name = 'UPS_NEW' where warehouse_id = 1;` + +db.warehouse.update ( - _id NAME, - customer_id TEXT, - review_date TIMESTAMP, - review_rating INTEGER, - product_id CHAR(10), - product_title TEXT, - product_group TEXT, - product_category TEXT, - similar_product_ids CHAR(10)[] + { + "warehouse_id" : 1 + }, + { + "warehouse_id" : 1, + "warehouse_name" : "UPS_NEW" + } ) -SERVER mongo_server -OPTIONS (database 'test', collection 'customer_reviews'); --- collect data distribution statistics -ANALYZE customer_reviews; +-- explain a table +`EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1; + QUERY PLAN + ----------------------------------------------------------------- + Foreign Scan on warehouse (cost=0.00..0.00 rows=1000 width=44) + Filter: (warehouse_id = 1) + Foreign Namespace: db.warehouse + Planning time: 0.671 ms +(4 rows)` + +-- collect data distribution statistics` +ANALYZE warehouse; ``` - Limitations ----------- @@ -103,7 +167,7 @@ Contributing Have a fix for a bug or an idea for a great new feature? Great! Check out the contribution guidelines [here][4]. For all other types of questions or comments -about the wrapper please contact us at `engage` `@` `citusdata.com`. +about the wrapper please contact us at `ibrar.ahmed` `@` `enterprisedb.com`. Support @@ -118,7 +182,8 @@ Reported bugs will be addressed by apparent severity. License ------- -Copyright © 2012–2014 Citus Data, Inc. +Portions Copyright © 2004-2014, 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 @@ -129,6 +194,8 @@ See the [`LICENSE`][5] file for full details. [1]: http://www.mongodb.com [2]: http://www.citusdata.com/blog/51-run-sql-on-mongodb -[3]: https://github.com/citusdata/mongo_fdw/issues/new +[3]: https://github.com/ibrarahmad/mongo_fdw/issues/new [4]: CONTRIBUTING.md [5]: LICENSE +[6]: https://github.com/mongodb/mongo-c-driver-legacy +[7]: https://github.com/mongodb/mongo-meta-driver From a501e6d8492c23b07385bd362b10669b435080a7 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 18:59:56 +0500 Subject: [PATCH 024/239] README file typos and cosmetic changes. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9065d78..45dbd19 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -MongoDB Foreign Date Wrapper for PostgreSQL +MongoDB Foreign Data Wrapper for PostgreSQL =========================================== This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper for. @@ -10,7 +10,7 @@ PostgreSQL Version 9.3 and greater. Installation ------------ -This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s ""C"" drivers, [legacy driver][6] and [MongoDB][1]'s [Meta Driver][7]. This is compile time decision which driver you want to use with this FDW. +This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's legacy driver][6] and [MongoDB's Meta Driver][7]. This is compile time decision which driver you want to use with this FDW. The [MongoDB][1] FDW includes the [legacy MongoDB C Driver][6] version 0.6. When you type `make -f Makefile.legacy`, the C driver's source code also gets automatically compiled and linked. The other option is to compile using [MongoDB's Meta Driver][7]. You need to download the Meta C driver from MongoDB site. @@ -183,6 +183,7 @@ License ------- Portions Copyright © 2004-2014, EnterpriseDB Corporation. + Portions Copyright © 2012–2014 Citus Data, Inc. This program is free software: you can redistribute it and/or modify it under From 4ead1e4e1941f328602ffd4e32c338034a02a497 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 19:03:13 +0500 Subject: [PATCH 025/239] Compiler warning of unused variable fixed. --- mongo_fdw.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 899130a..29ef87a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -723,10 +723,6 @@ MongoExecForeignInsert(EState *estate, MONGO_CONN *mongoConnection = NULL; Oid foreignTableId = InvalidOid; BSON *b = NULL; - bson_oid_t bsonObjectId; - char *outputString; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; Oid typoid = InvalidOid; Datum value; bool isnull = false; From 2e5a4914c06a47426dd9498b80253b49d4e540e4 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 19:05:40 +0500 Subject: [PATCH 026/239] Default Makefile to compile FDW with MongoDB's legacy Driver. --- Makefile | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..88e6ce0 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +# mongo_fdw/Makefile +# +# Copyright (c) 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. +# + +MONGO_DRIVER = mongo-c-driver-v0.6 +MONGO_PATH = $(MONGO_DRIVER)/src +MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env_posix.os + +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) + +EXTENSION = mongo_fdw +DATA = mongo_fdw--1.0.sql + +$(MONGO_DRIVER)/%.os: + $(MAKE) -C $(MONGO_DRIVER) $*.os + +# +# 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), 9.3 9.4)) + $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) +endif From e31a1936fd76feccb5030792e19c48df08ce76e3 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 19:12:43 +0500 Subject: [PATCH 027/239] Licence information update --- Makefile | 4 +++- Makefile.legacy | 6 ++++-- Makefile.meta | 6 ++++-- connection.c | 5 ++++- mongo_fdw--1.0.sql | 3 ++- mongo_fdw.c | 4 +++- mongo_fdw.control | 3 ++- mongo_fdw.h | 5 ++++- mongo_query.c | 5 ++++- mongo_query.h | 5 ++++- mongo_wrapper.c | 5 ++++- mongo_wrapper_meta.c | 7 +++++-- option.c | 5 ++++- 13 files changed, 47 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 88e6ce0..11cdf69 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ # mongo_fdw/Makefile # -# Copyright (c) 2012-2014 Citus Data, Inc. +# Portions Copyright © 2004-2014, EnterpriseDB Corporation. +# +# Portions Copyright © 2012–2014 Citus Data, Inc. # MODULE_big = mongo_fdw diff --git a/Makefile.legacy b/Makefile.legacy index 88e6ce0..a9f0ed3 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -1,6 +1,8 @@ -# mongo_fdw/Makefile +# mongo_fdw/Makefile.legacy # -# Copyright (c) 2012-2014 Citus Data, Inc. +# Portions Copyright © 2004-2014, EnterpriseDB Corporation. +# +# Portions Copyright © 2012–2014 Citus Data, Inc. # MODULE_big = mongo_fdw diff --git a/Makefile.meta b/Makefile.meta index 10a12e4..569d2f0 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -1,6 +1,8 @@ -# mongo_fdw/Makefile +# mongo_fdw/Makefile.meta # -# Copyright (c) 2012-2014 Citus Data, Inc. +# Portions Copyright © 2004-2014, EnterpriseDB Corporation. +# +# Portions Copyright © 2012–2014 Citus Data, Inc. # MODULE_big = mongo_fdw diff --git a/connection.c b/connection.c index 9b8660e..aefe924 100644 --- a/connection.c +++ b/connection.c @@ -3,7 +3,10 @@ * connection.c * Connection management functions for mongo_fdw * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index af83adf..b495ced 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,7 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Copyright (c) 2012-2014 Citus Data, Inc. +-- Portions Copyright © 2004-2014, 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 diff --git a/mongo_fdw.c b/mongo_fdw.c index 29ef87a..65d5d6c 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -5,7 +5,9 @@ * Function definitions for MongoDB foreign data wrapper. These functions access * data stored in MongoDB through the official C driver. * - * Copyright (c) 2012-2014 Citus Data, Inc. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. * *------------------------------------------------------------------------- */ diff --git a/mongo_fdw.control b/mongo_fdw.control index c0fbe86..a457792 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,7 @@ # mongo_fdw extension # -# Copyright (c) 2012-2014 Citus Data, Inc. +# Portions Copyright © 2004-2014, EnterpriseDB Corporation. +# Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' default_version = '1.0' diff --git a/mongo_fdw.h b/mongo_fdw.h index f72af94..ba40cde 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,10 @@ * * Type and function declarations for MongoDB foreign data wrapper. * - * Copyright (c) 2012-2014 Citus Data, Inc. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/mongo_query.c b/mongo_query.c index a559a0b..fae7543 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -6,7 +6,10 @@ * that queries are sent through the official MongoDB C driver, and apply query * optimizations to reduce the amount of data fetched from the driver. * - * Copyright (c) 2012-2014 Citus Data, Inc. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/mongo_query.h b/mongo_query.h index 607bb2d..66f0c9f 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,10 @@ * * Type and function declarations for constructing queries to send to MongoDB. * - * Copyright (c) 2012-2014 Citus Data, Inc. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 9c77c41..e4c3604 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,10 @@ * * Wrapper functions for MongoDB's old legacy Driver. * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 33ec2c5..70074c9 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -1,10 +1,13 @@ /*------------------------------------------------------------------------- * - * mongo_wrapper.c + * mongo_wrapper_meta.c * * Wrapper functions for MongoDB's new Meta Driver. * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ diff --git a/option.c b/option.c index b7730ab..5504373 100644 --- a/option.c +++ b/option.c @@ -5,7 +5,10 @@ * Function definitions for MongoDB foreign data wrapper. These functions access * data stored in MongoDB through the official C driver. * - * Copyright (c) 2012-2014 Citus Data, Inc. + * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright © 2012–2014 Citus Data, Inc. + * * *------------------------------------------------------------------------- */ From 0fe3122d48cd890cd6b0eb3c276d5bda62aafb8f Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 2 Jun 2014 19:27:32 +0500 Subject: [PATCH 028/239] README file changes. --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 45dbd19..a614783 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,14 @@ PostgreSQL Version 9.3 and greater. Installation ------------ -This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's legacy driver][6] and [MongoDB's Meta Driver][7]. This is compile time decision which driver you want to use with this FDW. +This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's legacy driver][6] and [MongoDB's Meta Driver][7]. This is a compile time decision which MongoDB's driver this FDW will use. The [MongoDB][1] FDW includes the [legacy MongoDB C Driver][6] version 0.6. When you -type `make -f Makefile.legacy`, the C driver's source code also gets automatically compiled and linked. The other option is to compile using [MongoDB's Meta Driver][7]. You need to download the Meta C driver from MongoDB site. +Type `make -f Makefile. legacy`, the C driver's source code which gets automatically compiled and linked. The other option is to compile utilizing [MongoDB's Meta Driver][7]. You require to download the [Meta C driver][7] from MongoDB site. -To build on POSIX-compliant systems (like Linux and OS X), 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: +To build on POSIX-compliant systems (like Linux and OS X), you require to ascertain +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: Compile using MonoDB's legacy C driver. @@ -65,7 +65,7 @@ estimating costs for the query execution plan. To see selected execution plans for a query, just run `EXPLAIN`. ```sql -Examples with MongoDB equelent statments. +Examples with MongoDB equivalent statments. -- load extension first time after install `CREATE EXTENSION mongo_fdw;` @@ -153,9 +153,7 @@ 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. For example, a nested field such as `"review": { - "Votes": 19 }` should be declared as `"review.Votes" INTEGER` in the `CREATE - TABLE` statement. + 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` From 9f971d9a3f5d24916cfa0df1b665997d4ad1c903 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 4 Jun 2014 19:55:51 +0500 Subject: [PATCH 029/239] Numeric array support for MongoDB's legacy and meta driver added --- README.md | 2 +- .../docs/buildscripts/__init__.py | 0 mongo_fdw.c | 11 +++++- mongo_fdw.h | 1 + mongo_query.c | 38 +++++++++++++++++++ mongo_query.h | 3 ++ mongo_wrapper.c | 11 ++++++ mongo_wrapper.h | 2 + mongo_wrapper_meta.c | 11 ++++++ 9 files changed, 77 insertions(+), 2 deletions(-) delete mode 100644 mongo-c-driver-v0.6/docs/buildscripts/__init__.py diff --git a/README.md b/README.md index a614783..4c775f8 100644 --- a/README.md +++ b/README.md @@ -197,4 +197,4 @@ See the [`LICENSE`][5] file for full details. [4]: CONTRIBUTING.md [5]: LICENSE [6]: https://github.com/mongodb/mongo-c-driver-legacy -[7]: https://github.com/mongodb/mongo-meta-driver +[7]: https://github.com/mongodb/mongo-c-driver diff --git a/mongo-c-driver-v0.6/docs/buildscripts/__init__.py b/mongo-c-driver-v0.6/docs/buildscripts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/mongo_fdw.c b/mongo_fdw.c index 65d5d6c..d0fa33e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -303,7 +303,7 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) NULL, /* no outer rel either */ NIL); /* no fdw_private data */ - /* add foreign path as the only possible path */ + /* add foreign path as the only possible path */ add_path(baserel, foreignPath); } @@ -869,6 +869,9 @@ MongoExecForeignUpdate(EState *estate, int attnum = lfirst_int(lc); Datum value; bool isnull; + + if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + continue; value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER @@ -1221,6 +1224,12 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) } break; } + case NUMERICARRAY_OID: + { + if (bsonType == BSON_TYPE_ARRAY) + compatibleTypes = true; + break; + } default: { /* diff --git a/mongo_fdw.h b/mongo_fdw.h index ba40cde..11f2a60 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -53,6 +53,7 @@ #include "utils/rel.h" #include "utils/memutils.h" + #ifdef META_DRIVER #define BSON bson_t #define BSON_TYPE bson_type_t diff --git a/mongo_query.c b/mongo_query.c index fae7543..1a58a65 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -477,6 +477,44 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu 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 t; + + 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, &t); + for (i = 0; i < num_elems; i++) + { + if (elem_nulls[i]) + continue; + + Datum valueDatum = DirectFunctionCall1(numeric_float8, elem_values[i]); + float8 valueFloat = DatumGetFloat8(valueDatum); +#ifdef META_DRIVER + status = BsonAppendDouble(&t, keyName, valueFloat); +#else + status = BsonAppendDouble(queryDocument, keyName, valueFloat); +#endif + } + BsonAppendFinishArray(queryDocument, &t); + pfree(elem_values); + pfree(elem_nulls); + break; + } default: { /* diff --git a/mongo_query.h b/mongo_query.h index 66f0c9f..d159dd3 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -15,6 +15,9 @@ #ifndef MONGO_QUERY_H #define MONGO_QUERY_H + +#define NUMERICARRAY_OID 1231 + bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); #endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper.c b/mongo_wrapper.c index e4c3604..3c93ab4 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -287,6 +287,17 @@ BsonAppendDate(BSON *b, const char* key, time_t v) } +bool BsonAppendStartArray(BSON *b, const char* key, BSON* c) +{ + return (bson_append_start_array(b, key) == MONGO_OK); +} + + +bool BsonAppendFinishArray(BSON *b, BSON *c) +{ + return (bson_append_finish_array(b) == MONGO_OK); +} + bool BsonAppendStartObject(BSON* b, char *key, BSON* r) { diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 6cc8c21..594ab1f 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -55,6 +55,8 @@ 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 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); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 70074c9..5e77804 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -361,6 +361,17 @@ 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 BsonFinish(BSON* b) From bce5fc3c5bdf841ac637af6e088f9f72d2cd4bec Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 5 Jun 2014 13:19:05 +0500 Subject: [PATCH 030/239] README file changes --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4c775f8..9bce266 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,32 @@ MongoDB Foreign Data Wrapper for PostgreSQL =========================================== -This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper for. +This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please also note that this version of `mongo_fdw` only works with +Please note that this version of `mongo_fdw` only works with PostgreSQL Version 9.3 and greater. Installation ------------ -This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's legacy driver][6] and [MongoDB's Meta Driver][7]. This is a compile time decision which MongoDB's driver this FDW will use. +This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's Legacy driver][6] and [MongoDB's C Driver][7]. This is a compile time decision which MongoDB's driver this FDW will use. -The [MongoDB][1] FDW includes the [legacy MongoDB C Driver][6] version 0.6. When you -Type `make -f Makefile. legacy`, the C driver's source code which gets automatically compiled and linked. The other option is to compile utilizing [MongoDB's Meta Driver][7]. You require to download the [Meta C driver][7] from MongoDB site. +The [MongoDB][1] FDW includes the [MongoDB's Legacy C Driver][6] version 0.6. When you +Type `make -f Makefile. legacy` or `make`, the [MongoDB's Legacy C Driver][6]'s source code which gets automatically compiled and linked. The other option is to compile utilizing [MongoDB's C Driver][7]. You require to download the [MongoDB's C driver][7] from [MongoDB][1] site. To build on POSIX-compliant systems (like Linux and OS X), you require to ascertain 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: -Compile using MonoDB's legacy C driver. +Compile using [MongoDB][1] legacy C driver. ```sh PATH=/usr/local/pgsql/bin/:$PATH make -f Makefile.legacy sudo PATH=/usr/local/pgsql/bin/:$PATH make install ``` -Compile using MonoDB's Meta C driver. +Compile using [MongoDB][1]'s C driver. Add `#deine META_DRIVER` in `config.h` file, then @@ -193,7 +193,7 @@ See the [`LICENSE`][5] file for full details. [1]: http://www.mongodb.com [2]: http://www.citusdata.com/blog/51-run-sql-on-mongodb -[3]: https://github.com/ibrarahmad/mongo_fdw/issues/new +[3]: https://github.com/enterprisedb/mongo_fdw/issues/new [4]: CONTRIBUTING.md [5]: LICENSE [6]: https://github.com/mongodb/mongo-c-driver-legacy From 1fd94bc95a808eecc553918bb2df67515e4359d1 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 6 Jun 2014 15:26:54 +0500 Subject: [PATCH 031/239] README file changes. --- README.md | 175 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 125 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 9bce266..76abb33 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,134 @@ -MongoDB Foreign Data Wrapper for PostgreSQL -=========================================== +# MongoDB Foreign Data Wrapper for PostgreSQL This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. Please note that this version of `mongo_fdw` only works with -PostgreSQL Version 9.3 and greater. +PostgreSQL Version **9.3** and greater. +## 1 - Installation -Installation ------------- +This [MongoDB][1] Foreign Data Wrapper is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's Legacy C driver][6] and [MongoDB's C Driver][7]. [MongoDB][1]'s driver to use is decided at compile time using a different Makefile. -This [MongoDB][1] FDW is compatible with two [MongoDB][1]'s 'C' drivers, [MongoDB's Legacy driver][6] and [MongoDB's C Driver][7]. This is a compile time decision which MongoDB's driver this FDW will use. +### 1.1 - Compiling Using 'Legacy C' Driver### -The [MongoDB][1] FDW includes the [MongoDB's Legacy C Driver][6] version 0.6. When you -Type `make -f Makefile. legacy` or `make`, the [MongoDB's Legacy C Driver][6]'s source code which gets automatically compiled and linked. The other option is to compile utilizing [MongoDB's C Driver][7]. You require to download the [MongoDB's C driver][7] from [MongoDB][1] site. +The [MongoDB][1]'s FDW includes the source code of [MongoDB's Legacy C Driver][6] version **0.6**. + +1 - Export `pg_config` Path To build on POSIX-compliant systems (like Linux and OS X), you require to ascertain -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: +that `pg_config` executable is in your path when you run make. This executable is typically in your PostgreSQL installation's bin directory. + +```sh +# export PATH=/usr/local/pgsql/bin:$PATH +``` + +2 - Compile code + +```sh +# make +``` + +OR + +```sh +# make -f Makefile.legacy +``` + +*Note: [MongoDB's Legacy C driver][6] will be compiled automatically.* + +3 - Install -Compile using [MongoDB][1] legacy C driver. +```sh +# make install +``` +OR ```sh -PATH=/usr/local/pgsql/bin/:$PATH make -f Makefile.legacy -sudo PATH=/usr/local/pgsql/bin/:$PATH make install +# make -f Makefile.legacy install ``` -Compile using [MongoDB][1]'s C driver. +### 1.2 - Compile Using [MongoDB][7]'s C Driver + +The source code of [MongoDB's C Driver][7] is not part of this repository. It can be downloaded from + +https://github.com/mongodb/mongo-c-driver + + +#### 1.2.1 - Install MongoDB's C Driver + +1 - Download [MongoDB's C Driver][7] -Add `#deine META_DRIVER` in `config.h` file, then +```sh +git clone https://github.com/mongodb/mongo-c-driver.git mongo-c-meta-driver +``` + +2 - Change directory ```sh -PATH=/usr/local/pgsql/bin/:$PATH make -f Makefile.meta -sudo PATH=/usr/local/pgsql/bin/:$PATH make install +cd mongo-c-meta-driver ``` -Note that we have tested the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu -systems. If you run into issues on other systems, please [let us know][3]. +3 - Configure Driver +```sh +./autogen.sh +``` -Usage ------ +4 - Compile Driver + +```sh +make +``` + +5 - Install Driver + +```sh +make install +``` + +*Note: Make sure you have permission to "/usr/local" (default installation location) folder.* + +#### 1.2.2 - Install Foreign Data Wrapper + +1 - Change configuration + +Add `#deine META_DRIVER` in `config.h` file + +1 - Export `pg_config` Path + +To build on POSIX-compliant systems (like Linux and OS X), you require to ascertain +that `pg_config` executable is in your path when you run make. This executable is typically in your PostgreSQL installation's bin directory. + +```sh +# export PATH=/usr/local/pgsql/bin:$PATH +``` + +2 - Compile code + +```sh +# make -f Makefile.meta +``` +2 - Install + +```sh +# make -f Makefile.meta install +``` + +*Note that we have verified the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu +systems. If you run into issues on other systems, please [let us know][3]* + + +## 2 - Usage The following parameters can be set on a MongoDB foreign server object: - * `address`: the address or hostname of the MongoDB server. - Defaults to `127.0.0.1` - * `port`: the port number of the MongoDB server. Defaults to `27017` + * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` + * **`port`**: the port number of the MongoDB server. Defaults to `27017` The following parameters can be set on a MongoDB foreign table object: - * `database`: the name of the MongoDB database to query. Defaults to `test` - * `collection`: the name of the MongoDB collection to query. Defaults to - the foreign table name used in the relevant `CREATE` command + * **`database`**: the name of the MongoDB database to query. Defaults to `test` + * **`collection`**: the name of the MongoDB collection to query. Defaults to the foreign table name used in the relevant `CREATE` command As an example, the following commands demonstrate loading the `mongo_fdw` wrapper, creating a server, and then creating a foreign table associated with @@ -64,36 +140,38 @@ default value mentioned above. estimating costs for the query execution plan. To see selected execution plans for a query, just run `EXPLAIN`. +Examples with [MongoDB][1]'s equivalent statments. + ```sql -Examples with MongoDB equivalent statments. -- load extension first time after install -`CREATE EXTENSION mongo_fdw;` +CREATE EXTENSION mongo_fdw; -- create server object -`CREATE SERVER mongo_server +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address '127.0.0.1', port '27017');` + OPTIONS (address '127.0.0.1', port '27017'); -- create foreign table -`CREATE FOREIGN TABLE warehouse( +CREATE FOREIGN TABLE warehouse( _id NAME, warehouse_id int, warehouse_name text, warehouse_created timestamptz) SERVER mongo_server - OPTIONS (database 'db', collection 'warehouse');` + OPTIONS (database 'db', collection 'warehouse'); +-- Note: first column of the table must be "_id" of type "NAME". -- select from table -`SELECT * FROM warehouse WHERE warehouse_id = 1;` +SELECT * FROM warehouse WHERE warehouse_id = 1; -_id | warehouse_id | warehouse_name | warehouse_created + _id | warehouse_id | warehouse_name | warehouse_created ------------------------+----------------+--------------------------- 53720b1904864dc1f5a571a0| 1 | UPS | 12-DEC-14 12:12:10 +05:00 -`db.warehouse.find({"warehouse_id" : 1}).pretty()` +db.warehouse.find({"warehouse_id" : 1}).pretty() { "_id" : ObjectId("53720b1904864dc1f5a571a0"), "warehouse_id" : 1, @@ -103,9 +181,9 @@ _id | warehouse_id | warehouse_name | warehouse_created -- insert row in table -`INSERT INTO warehouse values (0, 1, 'UPS', to_date('2014-12-12T07:12:10Z'));` +INSERT INTO warehouse values (0, 1, 'UPS', to_date('2014-12-12T07:12:10Z')); -`db.warehouse.insert` +db.warehouse.insert ( { "warehouse_id" : NumberInt(1), @@ -115,13 +193,13 @@ _id | warehouse_id | warehouse_name | warehouse_created ); -- delete row from table -`DELETE FROM warehouse where warehouse_id = 3;` +DELETE FROM warehouse where warehouse_id = 3; > db.warehouse.remove({"warehouse_id" : 2}) -- update a row of table -`UPDATE warehouse set warehouse_name = 'UPS_NEW' where warehouse_id = 1;` +UPDATE warehouse set warehouse_name = 'UPS_NEW' where warehouse_id = 1; db.warehouse.update ( @@ -135,21 +213,21 @@ db.warehouse.update ) -- explain a table -`EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1; +EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1; QUERY PLAN ----------------------------------------------------------------- Foreign Scan on warehouse (cost=0.00..0.00 rows=1000 width=44) Filter: (warehouse_id = 1) Foreign Namespace: db.warehouse Planning time: 0.671 ms -(4 rows)` +(4 rows) -- collect data distribution statistics` ANALYZE warehouse; + ``` -Limitations ------------ +## 3 - Limitations * If the BSON document key contains uppercase letters or occurs within a nested document, `mongo_fdw` requires the corresponding column names to be @@ -160,16 +238,14 @@ Limitations constant in `src/include/pg_config_manual.h`, compile, and reinstall. -Contributing ------------- +## 4 - Contributing Have a fix for a bug or an idea for a great new feature? Great! Check out the contribution guidelines [here][4]. For all other types of questions or comments about the wrapper please contact us at `ibrar.ahmed` `@` `enterprisedb.com`. -Support -------- +## 5 - Support This project will be modified to maintain compatibility with new PostgreSQL releases. The project owners set aside a day every month to look over open @@ -177,8 +253,7 @@ issues and support emails, but are not engaged in active feature development. Reported bugs will be addressed by apparent severity. -License -------- +## 6 - License Portions Copyright © 2004-2014, EnterpriseDB Corporation. From 2007c722609421403299d540e57fd3653c71cff5 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 6 Jun 2014 17:13:52 +0500 Subject: [PATCH 032/239] Throw error in case type of first column of MongoDB's foreign Table not "name". --- mongo_fdw.c | 5 ++++- mongo_query.c | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index d0fa33e..7115fd0 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -725,7 +725,7 @@ MongoExecForeignInsert(EState *estate, MONGO_CONN *mongoConnection = NULL; Oid foreignTableId = InvalidOid; BSON *b = NULL; - Oid typoid = InvalidOid; + Oid typoid; Datum value; bool isnull = false; @@ -757,6 +757,9 @@ MongoExecForeignInsert(EState *estate, if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) elog(ERROR, "first colum of MongoDB's foreign table must be \"_id\""); + if (typoid != NAMEOID) + elog(ERROR, "type of first colum of MongoDB's foreign table must be \"name\""); + if (attnum == 1) { /* diff --git a/mongo_query.c b/mongo_query.c index 1a58a65..7708db6 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -499,11 +499,13 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu BsonAppendStartArray(queryDocument, keyName, &t); for (i = 0; i < num_elems; i++) { + Datum valueDatum; + float8 valueFloat; if (elem_nulls[i]) continue; - Datum valueDatum = DirectFunctionCall1(numeric_float8, elem_values[i]); - float8 valueFloat = DatumGetFloat8(valueDatum); + valueDatum = DirectFunctionCall1(numeric_float8, elem_values[i]); + valueFloat = DatumGetFloat8(valueDatum); #ifdef META_DRIVER status = BsonAppendDouble(&t, keyName, valueFloat); #else From 990bdf1413ce8ef0631249ce7abfb32bee9b71b0 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 6 Jun 2014 20:09:09 +0500 Subject: [PATCH 033/239] Wrong BSON object was passed in case of Meta Drive --- mongo_query.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mongo_query.c b/mongo_query.c index 7708db6..246b715 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -239,8 +239,11 @@ QueryDocument(Oid relationId, List *opExpressionList) operatorName = get_opname(columnOperator->opno); mongoOperatorName = MongoOperatorName(operatorName); - +#ifdef META_DRIVER + AppendConstantValue(&r, mongoOperatorName, constant); +#else AppendConstantValue(queryDocument, mongoOperatorName, constant); +#endif } BsonAppendFinishObject(queryDocument, &r); } From 87b026cbedd3046da4f5daabade7c0fea7687a0b Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 9 Jun 2014 17:55:01 +0500 Subject: [PATCH 034/239] The "analyze" statement throwing error while retrieving rows from MongoDB. --- mongo_fdw.c | 46 ++++++++++++++++++++++++-------------------- mongo_query.c | 1 + mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 4 ++-- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 7115fd0..8c4b256 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -516,13 +516,15 @@ MongoIterateForeignScan(ForeignScanState *scanState) } else { - #ifndef META_DRIVER - /* - * The following is a courtesy check. In practice when Mongo shuts down, - * mongo_cursor_next() could possibly crash. This function first frees - * cursor->reply, and then references reply in mongo_cursor__destroy(). - */ - + #ifdef META_DRIVER + bson_error_t error; + if (mongoc_cursor_error (mongoCursor, &error)) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver error: %s", error.message))); + } + #else mongo_cursor_error_t errorCode = mongoCursor->err; if (errorCode != MONGO_CURSOR_EXHAUSTED) { @@ -997,7 +999,7 @@ ForeignTableDocumentCount(Oid foreignTableId) mongoConnection = GetConnection(options->addressName, options->portNumber); - MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); + documentCount = MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); MongoFreeOptions(options); @@ -1575,7 +1577,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, foreignTableId = RelationGetRelid(relation); queryDocument = QueryDocument(foreignTableId, NIL); - foreignPrivateList = list_make1(columnList); + foreignPrivateList = list_make2(columnList, NULL); /* only clean up the query struct, but not its data */ BsonDestroy(queryDocument); @@ -1632,20 +1634,22 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, } else { - #ifndef META_DRIVER - /* - * The following is a courtesy check. In practice when Mongo shuts down, - * mongo_cursor__next() could possibly crash. - */ - mongo_cursor_error_t errorCode = mongoCursor->err; - - if (errorCode != MONGO_CURSOR_EXHAUSTED) + #ifdef META_DRIVER + bson_error_t error; + if (mongoc_cursor_error (mongoCursor, &error)) { MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo 11collection"), - errhint("Mongo driver cursor error code: %d", - errorCode))); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver error: %s", error.message))); } + #else + mongo_cursor_error_t errorCode = mongoCursor->err; + if (errorCode != MONGO_CURSOR_EXHAUSTED) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver cursor error code: %d", errorCode))); + } #endif break; } @@ -1706,7 +1710,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, /* emit some interesting relation info */ relationName = RelationGetRelationName(relation); - ereport(errorLevel, (errmsg("\"%s\": collection contains %.0f rows; %d rows in sample", + ereport(errorLevel, (errmsg("\"%s\": collection contains %.0f rows; %d rows in sample", relationName, rowCount, sampleRowCount))); (*totalRowCount) = rowCount; diff --git a/mongo_query.c b/mongo_query.c index 246b715..8b95f1c 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -247,6 +247,7 @@ QueryDocument(Oid relationId, List *opExpressionList) } BsonAppendFinishObject(queryDocument, &r); } + if (!BsonFinish(queryDocument)) { ereport(ERROR, (errmsg("could not create document for query"), diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 3c93ab4..5dd53fe 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -324,7 +324,7 @@ BsonFinish(BSON* b) return (bson_finish(b) == MONGO_OK); } -int +double MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) { return mongo_count(conn, database, collection, b); diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 594ab1f..5d023f1 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -26,7 +26,7 @@ MONGO_CURSOR* MongoCursorCreate(MONGO_CONN* conn, char* database, char *collecti const BSON* MongoCursorBson(MONGO_CURSOR* c); bool MongoCursorNext(MONGO_CURSOR* c, BSON* b); void MongoCursorDestroy(MONGO_CURSOR* c); -int MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b); +double MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b); BSON* BsonCreate(void); void BsonDestroy(BSON *b); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 5e77804..bea95dd 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -387,7 +387,7 @@ BsonFinish(BSON* b) /* * Count the number of documents. */ -int +double MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) { BSON *cmd = NULL; @@ -417,5 +417,5 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti BsonDestroy(out); BsonDestroy(cmd); - return (int)count; + return count; } From 9f7e1255747166b374fa99622c7bd59ac94f3bf1 Mon Sep 17 00:00:00 2001 From: Ahsan Hadi Date: Tue, 24 Jun 2014 16:03:33 +0500 Subject: [PATCH 035/239] Misc changes to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 76abb33..1bef2dc 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ ANALYZE warehouse; Have a fix for a bug or an idea for a great new feature? Great! Check out the contribution guidelines [here][4]. For all other types of questions or comments -about the wrapper please contact us at `ibrar.ahmed` `@` `enterprisedb.com`. +about the wrapper please contact us at `mongo_fdw` `@` `enterprisedb.com`. ## 5 - Support @@ -252,6 +252,7 @@ releases. The project owners set aside a day every month to look over open issues and support emails, but are not engaged in active feature development. Reported bugs will be addressed by apparent severity. +As with many open source projects, you may be able to obtain support via the public mailing list (`mongo_fdw` `@` `enterprisedb.com`). If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support mongo_fdw. ## 6 - License From 19f16f495f848514add0cb9f37599750b62cbd92 Mon Sep 17 00:00:00 2001 From: Ahsan Hadi Date: Tue, 24 Jun 2014 16:05:50 +0500 Subject: [PATCH 036/239] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bd5c88b..e490f03 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ Using Issues `mongo_fdw`'s maintainers prefer that bug reports, feature requests, and pull requests are submitted as [GitHub Issues][1]. If you think you require personal -assistance, please **do not** open an issue: email `ibrar.ahmed` `@` `enterprisedb.com` +assistance, please **do not** open an issue: email `mongo_fdw` `@` `enterprisedb.com` instead. From 08bbee63259378a3fced1f6891cf8a5e9dbb2069 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 10 Jul 2014 12:38:13 +0500 Subject: [PATCH 037/239] Authentication using USER mapping --- connection.c | 4 ++-- mongo_fdw.c | 16 +++++++++++----- mongo_fdw.h | 19 ++++++++++++++----- mongo_wrapper.c | 13 ++++++++++++- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 7 +++++-- option.c | 11 ++++++++++- 7 files changed, 55 insertions(+), 17 deletions(-) diff --git a/connection.c b/connection.c index aefe924..94c8e18 100644 --- a/connection.c +++ b/connection.c @@ -60,7 +60,7 @@ static HTAB *ConnectionHash = NULL; * is established if we don't already have a suitable one. */ MONGO_CONN* -GetConnection(char *host, int32 port) +GetConnection(char *host, int32 port, char *databaseName, char *user, char *password) { bool found; ConnCacheEntry *entry; @@ -97,7 +97,7 @@ GetConnection(char *host, int32 port) } if (entry->conn == NULL) { - entry->conn = MongoConnect(host, port); + entry->conn = MongoConnect(host, port, databaseName, user, password); elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", entry->conn, host, port); } diff --git a/mongo_fdw.c b/mongo_fdw.c index 8c4b256..6695868 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -424,6 +424,9 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) HTAB *columnMappingHash = NULL; char *addressName = NULL; int32 portNumber = 0; + char *databaseName= NULL; + char *username= NULL; + char *password= NULL; ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; BSON *queryDocument = NULL; @@ -441,12 +444,15 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) /* resolve hostname and port number; and connect to mongo server */ addressName = mongoFdwOptions->addressName; portNumber = mongoFdwOptions->portNumber; + databaseName = mongoFdwOptions->databaseName; + username = mongoFdwOptions->username; + password = mongoFdwOptions->password; /* * Get connection to the foreign server. Connection manager will * establish new connection if necessary. */ - mongoConnection = GetConnection(addressName, portNumber); + mongoConnection = GetConnection(addressName, portNumber, databaseName, username, password); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; @@ -739,7 +745,7 @@ MongoExecForeignInsert(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber); + mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); b = BsonCreate(); @@ -852,7 +858,7 @@ MongoExecForeignUpdate(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber); + mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -935,7 +941,7 @@ MongoExecForeignDelete(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber); + mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -997,7 +1003,7 @@ ForeignTableDocumentCount(Oid foreignTableId) /* resolve foreign table options; and connect to mongo server */ options = MongoGetOptions(foreignTableId); - mongoConnection = GetConnection(options->addressName, options->portNumber); + mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); documentCount = MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); diff --git a/mongo_fdw.h b/mongo_fdw.h index 11f2a60..a4e9efa 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -52,7 +52,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/memutils.h" - +#include "catalog/pg_user_mapping.h" #ifdef META_DRIVER #define BSON bson_t @@ -83,6 +83,8 @@ #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" /* Default values for option parameters */ #define DEFAULT_IP_ADDRESS "127.0.0.1" @@ -112,16 +114,21 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ -static const uint32 ValidOptionCount = 4; +static const uint32 ValidOptionCount = 6; static const MongoValidOption ValidOptionArray[] = { /* foreign server options */ { OPTION_NAME_ADDRESS, ForeignServerRelationId }, - { OPTION_NAME_PORT, ForeignServerRelationId }, + { OPTION_NAME_PORT, ForeignServerRelationId }, /* foreign table options */ { OPTION_NAME_DATABASE, ForeignTableRelationId }, - { OPTION_NAME_COLLECTION, ForeignTableRelationId } + { OPTION_NAME_COLLECTION, ForeignTableRelationId }, + + /* User mapping options */ + { OPTION_NAME_USERNAME, UserMappingRelationId }, + { OPTION_NAME_PASSWORD, UserMappingRelationId } + }; @@ -136,6 +143,8 @@ typedef struct MongoFdwOptions int32 portNumber; char *databaseName; char *collectionName; + char *username; + char *password; } MongoFdwOptions; @@ -184,7 +193,7 @@ typedef struct ColumnMapping } ColumnMapping; -extern MONGO_CONN *GetConnection(char *host, int32 port); +extern MONGO_CONN *GetConnection(char *host, int32 port, char *databaneName, char *user, char *password); extern void cleanup_connection(void); extern void ReleaseConnection(MONGO_CONN* conn); diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 5dd53fe..6026878 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -26,7 +26,7 @@ #define QUAL_STRING_LEN 512 MONGO_CONN* -MongoConnect(const char* host, const unsigned short port) +MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password) { MONGO_CONN *conn; conn = mongo_create(); @@ -40,6 +40,17 @@ MongoConnect(const char* host, const unsigned short port) ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), errhint("Mongo driver connection error: %d", err))); } + if (user && password) + { + if (mongo_cmd_authenticate(conn, databaseName, user, password) != MONGO_OK) + { + int err = conn->err; + mongo_destroy(conn); + mongo_dispose(conn); + ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), + errhint("Mongo driver connection error: %d", err))); + } + } return conn; } diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 5d023f1..6a957bf 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -17,7 +17,7 @@ #include "mongo.h" #endif -MONGO_CONN* MongoConnect(const char* host, const unsigned short port); +MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); 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); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index bea95dd..f92513f 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -21,12 +21,15 @@ * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN* -MongoConnect(const char* host, const unsigned short port) +MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password) { MONGO_CONN *client = NULL; char* uri = NULL; - uri = bson_strdup_printf ("mongodb://%s:%hu/", host, port); + if (user && password) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/", user, password, host, port); + else + uri = bson_strdup_printf ("mongodb://%s:%hu/", host, port); client = mongoc_client_new(uri); diff --git a/option.c b/option.c index 5504373..1f1fa75 100644 --- a/option.c +++ b/option.c @@ -37,7 +37,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/memutils.h" - +#include "miscadmin.h" static char * MongoGetOptionValue(Oid foreignTableId, const char *optionName); @@ -148,6 +148,8 @@ MongoGetOptions(Oid foreignTableId) int32 portNumber = 0; char *databaseName = NULL; char *collectionName = NULL; + char *username= NULL; + char *password= NULL; addressName = MongoGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS); if (addressName == NULL) @@ -176,12 +178,16 @@ MongoGetOptions(Oid foreignTableId) { collectionName = get_rel_name(foreignTableId); } + username = MongoGetOptionValue(foreignTableId, OPTION_NAME_USERNAME); + password = MongoGetOptionValue(foreignTableId, OPTION_NAME_PASSWORD); mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); mongoFdwOptions->addressName = addressName; mongoFdwOptions->portNumber = portNumber; mongoFdwOptions->databaseName = databaseName; mongoFdwOptions->collectionName = collectionName; + mongoFdwOptions->username = username; + mongoFdwOptions->password = password; return mongoFdwOptions; } @@ -210,13 +216,16 @@ MongoGetOptionValue(Oid foreignTableId, const char *optionName) ForeignServer *foreignServer = NULL; List *optionList = NIL; ListCell *optionCell = NULL; + UserMapping *mapping= NULL; char *optionValue = NULL; foreignTable = GetForeignTable(foreignTableId); foreignServer = GetForeignServer(foreignTable->serverid); + mapping = GetUserMapping(GetUserId(), foreignTable->serverid); optionList = list_concat(optionList, foreignTable->options); optionList = list_concat(optionList, foreignServer->options); + optionList = list_concat(optionList, mapping->options); foreach(optionCell, optionList) { From f4f6c423633155c02d8df39301b815bff6b000a2 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 10 Jul 2014 12:59:57 +0500 Subject: [PATCH 038/239] README Changes for user mapping --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1bef2dc..354a08d 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,11 @@ CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); +-- create user mapping +CREATE USER MAPPING FOR postgres + SERVER mongo_server + OPTIONS (username 'mongo_user', password 'mongo_pass'); + -- create foreign table CREATE FOREIGN TABLE warehouse( _id NAME, From 9da15a0ffa9954f2fc6aadf90067b7e95179443a Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 30 Sep 2014 23:36:50 +0500 Subject: [PATCH 039/239] License header change --- config.h | 17 +++++++++++++++++ mongo_fdw.c | 11 +++++++---- mongo_fdw.h | 9 ++++++--- mongo_query.c | 11 ++++++----- mongo_query.h | 9 ++++++--- mongo_wrapper.c | 9 ++++++--- mongo_wrapper.h | 11 ++++++++++- mongo_wrapper_meta.c | 10 +++++++--- option.c | 10 ++++++---- 9 files changed, 71 insertions(+), 26 deletions(-) diff --git a/config.h b/config.h index 6139f3e..ee6ce48 100644 --- a/config.h +++ b/config.h @@ -1,3 +1,20 @@ +/*------------------------------------------------------------------------- + * + * config.h + * Foreign-data wrapper for remote MongoDB servers + * + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group + * + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright (c) 2012–2014 Citus Data, Inc. + * + * IDENTIFICATION + * config.h + * + *------------------------------------------------------------------------- + */ + /* * Define if you want to compile the MongoFDW with Meta C Driver, otherwise * it will compile using MongoDB legacy C Driver diff --git a/mongo_fdw.c b/mongo_fdw.c index 510968d..6485559 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1,13 +1,16 @@ /*------------------------------------------------------------------------- * * mongo_fdw.c + * Foreign-data wrapper for remote MongoDB servers * - * Function definitions for MongoDB foreign data wrapper. These functions access - * data stored in MongoDB through the official C driver. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. + * + * IDENTIFICATION + * mongo_fdw.c * *------------------------------------------------------------------------- */ diff --git a/mongo_fdw.h b/mongo_fdw.h index a4e9efa..c040fd0 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -1,13 +1,16 @@ /*------------------------------------------------------------------------- * * mongo_fdw.h + * Foreign-data wrapper for remote MongoDB servers * - * Type and function declarations for MongoDB foreign data wrapper. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * mongo_fdw.h * *------------------------------------------------------------------------- */ diff --git a/mongo_query.c b/mongo_query.c index 1d4b7a3..d8356e5 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -1,15 +1,16 @@ /*------------------------------------------------------------------------- * * mongo_query.c + * Foreign-data wrapper for remote MongoDB servers * - * Function definitions for sending queries to MongoDB. These functions assume - * that queries are sent through the official MongoDB C driver, and apply query - * optimizations to reduce the amount of data fetched from the driver. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * mongo_query.c * *------------------------------------------------------------------------- */ diff --git a/mongo_query.h b/mongo_query.h index d159dd3..9ba8508 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -1,13 +1,16 @@ /*------------------------------------------------------------------------- * * mongo_query.h + * Foreign-data wrapper for remote MongoDB servers * - * Type and function declarations for constructing queries to send to MongoDB. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * mongo_query.h * *------------------------------------------------------------------------- */ diff --git a/mongo_wrapper.c b/mongo_wrapper.c index d20b678..99dadd5 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -1,13 +1,16 @@ /*------------------------------------------------------------------------- * * mongo_wrapper.c + * Foreign-data wrapper for remote MongoDB servers * - * Wrapper functions for MongoDB's old legacy Driver. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * mongo_wrapper.c * *------------------------------------------------------------------------- */ diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 6a957bf..f3a26d8 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -1,10 +1,19 @@ /*------------------------------------------------------------------------- * * mongo_wrapper.h + * Foreign-data wrapper for remote MongoDB servers + * + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group + * + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. + * + * Portions Copyright (c) 2012–2014 Citus Data, Inc. + * + * IDENTIFICATION + * mongo_wrapper.h * *------------------------------------------------------------------------- */ - #ifndef MONGO_WRAPPER_H #define MONGO_WRAPPER_H diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index f92513f..400bd12 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -1,17 +1,21 @@ /*------------------------------------------------------------------------- * * mongo_wrapper_meta.c + * Foreign-data wrapper for remote MongoDB servers * - * Wrapper functions for MongoDB's new Meta Driver. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * mongo_wrapper_meta.c * *------------------------------------------------------------------------- */ + #include "postgres.h" #include diff --git a/option.c b/option.c index 1f1fa75..bbd3ed0 100644 --- a/option.c +++ b/option.c @@ -1,14 +1,16 @@ /*------------------------------------------------------------------------- * * option.c + * Foreign-data wrapper for remote MongoDB servers * - * Function definitions for MongoDB foreign data wrapper. These functions access - * data stored in MongoDB through the official C driver. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2012–2014 Citus Data, Inc. * + * IDENTIFICATION + * option.c * *------------------------------------------------------------------------- */ From b6aa5e145102265ba6ccb9c9f7c0e6396f806aea Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 30 Sep 2014 23:44:00 +0500 Subject: [PATCH 040/239] PostgreSQL Version 9.5 support --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4e24993..afcb3e3 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4)) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) endif From f81428339d7fdc328b17668a8f67dbb8aebf092b Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 30 Sep 2014 23:45:40 +0500 Subject: [PATCH 041/239] PostgreSQL Version 9.5 support --- Makefile | 2 +- Makefile.meta | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index afcb3e3..a65c28e 100644 --- a/Makefile +++ b/Makefile @@ -45,5 +45,5 @@ ifndef MAJORVERSION endif ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) - $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) + $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 569d2f0..52a527a 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -41,6 +41,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4)) - $(error PostgreSQL 9.3 or 9.4 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) + $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) endif From c9b34617ff2d7c588d96489db1bde0a938065841 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 30 Sep 2014 23:56:26 +0500 Subject: [PATCH 042/239] README File changes --- README.md | 58 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 2725555..2042ad9 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. Please note that this version of `mongo_fdw` only works with PostgreSQL Version **9.3** and greater. -## 1 - Installation - +Installation +------------ The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and includes it as a git submodule. If you are cloning this repository for the first time, be sure to pass the --recursive option to git clone in order to @@ -19,14 +19,37 @@ not up-to-date, run git submodule update --init. When you type `make`, the C driver's source code also gets automatically compiled and linked. -*Note: Make sure you have permission to "/usr/local" (default installation location) folder.* - -*Note that we have verified the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu -systems. If you run into issues on other systems, please [let us know][3]* - - -## 2 - Usage - +`*Note: Make sure you have permission to "/usr/local" (default installation location) folder.*` + +`*Note that we have verified the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3]*` + +Enhancments +----------- +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 insert/update and delete statements for the foreign tables using the mongo_fdw. + +Connection Pooling +------------------ +The latest version comes with a connection pooler that utilises the same mongo +database connection for all the queries in the same session. The previous version +would open a new mongodb connection for every query. +This is a performance enhancement. + +New MongoDB C Driver Support +---------------------------- +The third enhancement is to add a new [MongoDB][1]' C driver. The current implementation is +based on the legacy driver of MongoDB. But [MongoDB][1] is provided completely new library +for driver called MongoDB's Meta Driver. So I have added support of that driver. +Now compile time option is available to use legacy and Meta driver. I am sure there +are many other benefits of the new Mongo-C-driver that we are not leveraging but we +will adopt those as we learn more about the new C driver. + +Usage +----- The following parameters can be set on a MongoDB foreign server object: * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` @@ -139,7 +162,8 @@ ANALYZE warehouse; ``` -## 3 - Limitations +Limitations +----------- * If the BSON document key contains uppercase letters or occurs within a nested document, `mongo_fdw` requires the corresponding column names to be @@ -150,15 +174,15 @@ ANALYZE warehouse; constant in `src/include/pg_config_manual.h`, compile, and reinstall. -## 4 - Contributing - +Contributing +------------ Have a fix for a bug or an idea for a great new feature? Great! Check out the contribution guidelines [here][4]. For all other types of questions or comments about the wrapper please contact us at `mongo_fdw` `@` `enterprisedb.com`. -## 5 - Support - +Support +------- This project will be modified to maintain compatibility with new PostgreSQL releases. The project owners set aside a day every month to look over open issues and support emails, but are not engaged in active feature development. @@ -166,8 +190,8 @@ Reported bugs will be addressed by apparent severity. As with many open source projects, you may be able to obtain support via the public mailing list (`mongo_fdw` `@` `enterprisedb.com`). If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support mongo_fdw. -## 6 - License - +License +------- Portions Copyright © 2004-2014, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. From e5d1ec08e3bc73bc0971a02d70df73f9a1289dd9 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 1 Oct 2014 15:02:14 +0500 Subject: [PATCH 043/239] Error message typos fix r Please enter the commit message for your changes. Lines starting --- mongo_fdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 6485559..997b27e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -776,10 +776,10 @@ MongoExecForeignInsert(EState *estate, /* first column of MongoDB's foreign table must be _id */ if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) - elog(ERROR, "first colum of MongoDB's foreign table must be \"_id\""); + elog(ERROR, "first column of MongoDB's foreign table must be \"_id\""); if (typoid != NAMEOID) - elog(ERROR, "type of first colum of MongoDB's foreign table must be \"name\""); + elog(ERROR, "type of first column of MongoDB's foreign table must be \"NAME\""); if (attnum == 1) { From 81673981612e66df97f3e3d5811fea131789abf0 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 1 Oct 2014 15:04:30 +0500 Subject: [PATCH 044/239] README typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2042ad9..b5e413b 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ compiled and linked. `*Note that we have verified the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3]*` -Enhancments +Enhancements ----------- The following enhancements are added to the latest version of mongo_fdw From a4b043c0fedcdc294becfbe1a644b27c7fd54733 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 1 Oct 2014 15:18:21 +0500 Subject: [PATCH 045/239] Function name change in connection.c and option. c file to avoid conflict with other libraries. --- connection.c | 22 +++++++++++++--------- mongo_fdw.c | 38 +++++++++++++++++++------------------- mongo_fdw.h | 16 ++++++++-------- option.c | 42 ++++++++++++++++-------------------------- 4 files changed, 56 insertions(+), 62 deletions(-) diff --git a/connection.c b/connection.c index 94c8e18..6a9464e 100644 --- a/connection.c +++ b/connection.c @@ -1,12 +1,16 @@ /*------------------------------------------------------------------------- * * connection.c - * Connection management functions for mongo_fdw + * Foreign-data wrapper for remote MongoDB servers * - * Portions Copyright © 2004-2014, EnterpriseDB Corporation. + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * - * Portions Copyright © 2012–2014 Citus Data, Inc. + * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. * + * Portions Copyright (c) 2012–2014 Citus Data, Inc. + * + * IDENTIFICATION + * connection.c * *------------------------------------------------------------------------- */ @@ -54,13 +58,13 @@ typedef struct ConnCacheEntry static HTAB *ConnectionHash = NULL; /* - * GetConnection: + * mongo_get_connection: * Get a mong 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* -GetConnection(char *host, int32 port, char *databaseName, char *user, char *password) +mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password) { bool found; ConnCacheEntry *entry; @@ -106,11 +110,11 @@ GetConnection(char *host, int32 port, char *databaseName, char *user, char *pass } /* - * cleanup_connection: + * mongo_cleanup_connection: * Delete all the cache entries on backend exists. */ void -cleanup_connection() +mongo_cleanup_connection() { HASH_SEQ_STATUS scan; ConnCacheEntry *entry; @@ -131,10 +135,10 @@ cleanup_connection() } /* - * Release connection created by calling GetConnection. + * Release connection created by calling mongo_get_connection. */ void -ReleaseConnection(MONGO_CONN *conn) +mongo_release_connection(MONGO_CONN *conn) { /* * We don't close the connection indvisually here, will do all connection diff --git a/mongo_fdw.c b/mongo_fdw.c index 997b27e..80858b8 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -208,7 +208,7 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) static void mongo_fdw_exit(int code, Datum arg) { - cleanup_connection(); + mongo_cleanup_connection(); } @@ -385,14 +385,14 @@ MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = MongoGetOptions(foreignTableId); + mongoFdwOptions = mongo_get_options(foreignTableId); /* construct fully qualified collection name */ namespaceName = makeStringInfo(); appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, mongoFdwOptions->collectionName); - MongoFreeOptions(mongoFdwOptions); + mongo_free_options(mongoFdwOptions); ExplainPropertyText("Foreign Namespace", namespaceName->data, explainState); } @@ -409,14 +409,14 @@ MongoExplainForeignModify(ModifyTableState *mtstate, Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); - mongoFdwOptions = MongoGetOptions(foreignTableId); + mongoFdwOptions = mongo_get_options(foreignTableId); /* construct fully qualified collection name */ namespaceName = makeStringInfo(); appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, mongoFdwOptions->collectionName); - MongoFreeOptions(mongoFdwOptions); + mongo_free_options(mongoFdwOptions); ExplainPropertyText("Foreign Namespace", namespaceName->data, es); } @@ -452,7 +452,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) return; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = MongoGetOptions(foreignTableId); + mongoFdwOptions = mongo_get_options(foreignTableId); /* resolve hostname and port number; and connect to mongo server */ addressName = mongoFdwOptions->addressName; @@ -465,7 +465,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) * Get connection to the foreign server. Connection manager will * establish new connection if necessary. */ - mongoConnection = GetConnection(addressName, portNumber, databaseName, username, password); + mongoConnection = mongo_get_connection(addressName, portNumber, databaseName, username, password); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; @@ -572,7 +572,7 @@ MongoEndForeignScan(ForeignScanState *scanState) { if (fmstate->mongoFdwOptions) { - MongoFreeOptions(fmstate->mongoFdwOptions); + mongo_free_options(fmstate->mongoFdwOptions); fmstate->mongoFdwOptions = NULL; } MongoFreeScanState(fmstate); @@ -598,14 +598,14 @@ MongoReScanForeignScan(ForeignScanState *scanState) /* reconstruct full collection name */ foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = MongoGetOptions(foreignTableId); + mongoFdwOptions = mongo_get_options(foreignTableId); /* reconstruct cursor for collection name and set query */ fmstate->mongoCursor = MongoCursorCreate(mongoConnection, fmstate->mongoFdwOptions->databaseName, fmstate->mongoFdwOptions->collectionName, fmstate->queryDocument); - MongoFreeOptions(mongoFdwOptions); + mongo_free_options(mongoFdwOptions); } static List * @@ -707,7 +707,7 @@ MongoBeginForeignModify(ModifyTableState *mtstate, fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); fmstate->rel = rel; - fmstate->mongoFdwOptions = MongoGetOptions(foreignTableId); + fmstate->mongoFdwOptions = mongo_get_options(foreignTableId); fmstate->target_attrs = (List *) list_nth(fdw_private, 0); @@ -758,7 +758,7 @@ MongoExecForeignInsert(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); b = BsonCreate(); @@ -871,7 +871,7 @@ MongoExecForeignUpdate(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -954,7 +954,7 @@ MongoExecForeignDelete(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -992,7 +992,7 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { if (fmstate->mongoFdwOptions) { - MongoFreeOptions(fmstate->mongoFdwOptions); + mongo_free_options(fmstate->mongoFdwOptions); fmstate->mongoFdwOptions = NULL; } MongoFreeScanState(fmstate); @@ -1014,13 +1014,13 @@ ForeignTableDocumentCount(Oid foreignTableId) double documentCount = 0.0; /* resolve foreign table options; and connect to mongo server */ - options = MongoGetOptions(foreignTableId); + options = mongo_get_options(foreignTableId); - mongoConnection = GetConnection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); documentCount = MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); - MongoFreeOptions(options); + mongo_free_options(options); return documentCount; } @@ -1693,7 +1693,7 @@ MongoFreeScanState(MongoFdwModifyState *fmstate) } /* Release remote connection */ - ReleaseConnection(fmstate->mongoConnection); + mongo_release_connection(fmstate->mongoConnection); } diff --git a/mongo_fdw.h b/mongo_fdw.h index c040fd0..8d38079 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -195,21 +195,21 @@ typedef struct ColumnMapping Oid columnArrayTypeId; } ColumnMapping; +/* options.c */ +extern MongoFdwOptions * mongo_get_options(Oid foreignTableId); +extern void mongo_free_options(MongoFdwOptions *mongoFdwOptions); +extern StringInfo mongo_option_names_string(Oid currentContextId); -extern MONGO_CONN *GetConnection(char *host, int32 port, char *databaneName, char *user, char *password); -extern void cleanup_connection(void); -extern void ReleaseConnection(MONGO_CONN* conn); - -extern StringInfo OptionNamesString(Oid currentContextId); +/* connection.c */ +extern MONGO_CONN *mongo_get_connection(char *host, int32 port, char *databaneName, char *user, char *password); +extern void mongo_cleanup_connection(void); +extern void mongo_release_connection(MONGO_CONN* conn); /* Function declarations related to creating the mongo query */ extern List * ApplicableOpExpressionList(RelOptInfo *baserel); extern BSON * QueryDocument(Oid relationId, List *opExpressionList); extern List * ColumnList(RelOptInfo *baserel); -extern MongoFdwOptions * MongoGetOptions(Oid foreignTableId); -extern void MongoFreeOptions(MongoFdwOptions *mongoFdwOptions); - /* Function declarations for foreign data wrapper */ extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); diff --git a/option.c b/option.c index bbd3ed0..eb8991a 100644 --- a/option.c +++ b/option.c @@ -41,7 +41,7 @@ #include "utils/memutils.h" #include "miscadmin.h" -static char * MongoGetOptionValue(Oid foreignTableId, const char *optionName); +static char * mongo_get_option_value(Oid foreignTableId, const char *optionName); /* * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER, @@ -88,7 +88,7 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) /* if invalid option, display an informative error message */ if (!optionValid) { - StringInfo optionNamesString = OptionNamesString(optionContextId); + StringInfo optionNamesString = mongo_option_names_string(optionContextId); ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), errmsg("invalid option \"%s\"", optionName), @@ -108,11 +108,11 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) } /* - * OptionNamesString finds all options that are valid for the current context, + * 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 -OptionNamesString(Oid currentContextId) +mongo_option_names_string(Oid currentContextId) { StringInfo optionNamesString = makeStringInfo(); bool firstOptionPrinted = false; @@ -137,12 +137,12 @@ OptionNamesString(Oid currentContextId) /* - * MongoGetOptions returns the option values to be used when connecting to and + * 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 * -MongoGetOptions(Oid foreignTableId) +mongo_get_options(Oid foreignTableId) { MongoFdwOptions *mongoFdwOptions = NULL; char *addressName = NULL; @@ -153,35 +153,26 @@ MongoGetOptions(Oid foreignTableId) char *username= NULL; char *password= NULL; - addressName = MongoGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS); + addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); if (addressName == NULL) - { addressName = pstrdup(DEFAULT_IP_ADDRESS); - } - portName = MongoGetOptionValue(foreignTableId, OPTION_NAME_PORT); + portName = mongo_get_option_value(foreignTableId, OPTION_NAME_PORT); if (portName == NULL) - { portNumber = DEFAULT_PORT_NUMBER; - } else - { portNumber = pg_atoi(portName, sizeof(int32), 0); - } - databaseName = MongoGetOptionValue(foreignTableId, OPTION_NAME_DATABASE); + databaseName = mongo_get_option_value(foreignTableId, OPTION_NAME_DATABASE); if (databaseName == NULL) - { databaseName = pstrdup(DEFAULT_DATABASE_NAME); - } - collectionName = MongoGetOptionValue(foreignTableId, OPTION_NAME_COLLECTION); + collectionName = mongo_get_option_value(foreignTableId, OPTION_NAME_COLLECTION); if (collectionName == NULL) - { collectionName = get_rel_name(foreignTableId); - } - username = MongoGetOptionValue(foreignTableId, OPTION_NAME_USERNAME); - password = MongoGetOptionValue(foreignTableId, OPTION_NAME_PASSWORD); + + username = mongo_get_option_value(foreignTableId, OPTION_NAME_USERNAME); + password = mongo_get_option_value(foreignTableId, OPTION_NAME_PASSWORD); mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); mongoFdwOptions->addressName = addressName; @@ -196,7 +187,7 @@ MongoGetOptions(Oid foreignTableId) void -MongoFreeOptions(MongoFdwOptions *mongoFdwOptions) +mongo_free_options(MongoFdwOptions *mongoFdwOptions) { if (mongoFdwOptions) { @@ -207,12 +198,12 @@ MongoFreeOptions(MongoFdwOptions *mongoFdwOptions) } /* - * MongoGetOptionValue walks over foreign table and foreign server options, and + * mongo_get_option_value walks over foreign table and foreign server options, and * looks for the option with the given name. If found, the function returns the * option's value. */ static char * -MongoGetOptionValue(Oid foreignTableId, const char *optionName) +mongo_get_option_value(Oid foreignTableId, const char *optionName) { ForeignTable *foreignTable = NULL; ForeignServer *foreignServer = NULL; @@ -240,7 +231,6 @@ MongoGetOptionValue(Oid foreignTableId, const char *optionName) break; } } - return optionValue; } From da603d525b22fd4598975127ab2790b70d8321a3 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 1 Oct 2014 15:28:05 +0500 Subject: [PATCH 046/239] README Changes --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b5e413b..cec383d 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,10 @@ not up-to-date, run git submodule update --init. When you type `make`, the C driver's source code also gets automatically compiled and linked. -`*Note: Make sure you have permission to "/usr/local" (default installation location) folder.*` +Note: Make sure you have permission to "/usr/local" (default installation location) folder. -`*Note that we have verified the `mongo_fdw` extension only on MacOS X, Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3]*` +Note that we have verified the `mongo_fdw` extension only on MacOS X, +Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3] Enhancements ----------- From 8e6ca32eb91e9169c6013d51c805f6e6d9985be3 Mon Sep 17 00:00:00 2001 From: TruongSinh Tran-Nguyen Date: Thu, 2 Oct 2014 14:48:41 +0300 Subject: [PATCH 047/239] read/write binary data (bytea) --- mongo_fdw.c | 18 ++++++++++++++++++ mongo_fdw.h | 1 + mongo_query.c | 15 +++++++++++++++ mongo_wrapper.c | 13 +++++++++++++ mongo_wrapper.h | 3 +++ 5 files changed, 50 insertions(+) diff --git a/mongo_fdw.c b/mongo_fdw.c index 80858b8..a4cc8c3 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1227,6 +1227,14 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) } break; } + case BYTEAOID: + { + if (bsonType == BSON_TYPE_BINDATA) + { + compatibleTypes = true; + } + break; + } case NAMEOID: { /* @@ -1432,6 +1440,16 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) Int32GetDatum(columnTypeMod)); break; } + case BYTEAOID: + { + int value_len = BsonIterBinLen(bsonIterator); + const char *value = BsonIterBinData(bsonIterator); + bytea *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); diff --git a/mongo_fdw.h b/mongo_fdw.h index 8d38079..9a6eb99 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -75,6 +75,7 @@ #define BSON_TYPE_INT32 BSON_INT #define BSON_TYPE_INT64 BSON_LONG #define BSON_TYPE_DOUBLE BSON_DOUBLE + #define BSON_TYPE_BINDATA BSON_BINDATA #define BSON_TYPE_BOOL BSON_BOOL #define BSON_TYPE_UTF8 BSON_STRING #define BSON_TYPE_OID BSON_OID diff --git a/mongo_query.c b/mongo_query.c index d8356e5..77945b8 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -449,6 +449,21 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu 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); + } + status = BsonAppendBinary(queryDocument, keyName, data, len); + break; + } case NAMEOID: { char *outputString = NULL; diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 99dadd5..cbe0b2a 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -202,6 +202,15 @@ BsonIterString(BSON_ITERATOR *it) return bson_iterator_string(it); } +const char* BsonIterBinData(BSON_ITERATOR *it) +{ + return bson_iterator_bin_data(it); +} + +int BsonIterBinLen(BSON_ITERATOR *it) +{ + return bson_iterator_bin_len(it); +} const bson_oid_t * BsonIterOid(BSON_ITERATOR *it) { @@ -294,6 +303,10 @@ BsonAppendUTF8(BSON *b, const char* key, char *v) return (bson_append_string(b, key, v) == MONGO_OK); } +bool BsonAppendBinary(BSON *b, const char* key, char *v, size_t len) +{ + return (bson_append_binary(b, key, BSON_BIN_BINARY, v, len) == MONGO_OK); +} bool BsonAppendDate(BSON *b, const char* key, time_t v) { diff --git a/mongo_wrapper.h b/mongo_wrapper.h index f3a26d8..b08640e 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -47,6 +47,8 @@ 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); +int BsonIterBinLen(BSON_ITERATOR *it); const bson_oid_t * BsonIterOid(BSON_ITERATOR *it); time_t BsonIterDate(BSON_ITERATOR *it); const char* BsonIterKey(BSON_ITERATOR *it); @@ -63,6 +65,7 @@ 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); From 6cdfae1720a3b3673af8caec2224ac3e6e67f9c1 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Thu, 16 Oct 2014 16:25:19 -0700 Subject: [PATCH 048/239] attempting to get mongo meta driver to work with 1.0.2 mongo c drivers updating .gitmodules adding META_DRIVER flag to Makefile.meta fixing bson_iterator type reference fixing BSON error reporting adding readPreference support forgot to change header file fixing more reference to mongo_get_connection using readPreference) fixing option for read_preference fixing includes fixing includes removing submodule for meta driver in favor of pkg-config fixing legacy driver iter methods fixing string iterator fixing regex iter fixing code iter type fixing missing method for meta driver fixing binary iterator fixing JsonDump fixing method signature fixing binary iterator fixing BsonAppendBinary setting slaveOk=true for when readPreference is provided fixing readPreference fixing SLAVE_OK forgot to delete some unused code trying to fix collection count patch to make count be able to read from slave fixing count method references trying to fix count method still tring to work around mongo driver bugs still struggling still struggling sigh finally! getting rid of some useless stuff Adding documentation for installing meta driver Fixing formatting --- .gitmodules | 3 + Makefile.meta | 12 +- README.md | 8 + connection.c | 9 + mongo_fdw.c | 3085 +++++++++++++++++++++--------------------- mongo_fdw.h | 197 ++- mongo_query.c | 5 + mongo_wrapper.h | 8 + mongo_wrapper_meta.c | 76 +- option.c | 9 +- 10 files changed, 1801 insertions(+), 1611 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8da9629..181eb58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "mongo-c-driver"] path = mongo-c-driver url = ../../mongodb/mongo-c-driver.git +[submodule "mongo-c-meta-driver"] + path = mongo-c-meta-driver + url = ../../mongodb/mongo-c-driver.git diff --git a/Makefile.meta b/Makefile.meta index 52a527a..338e8ff 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -13,21 +13,15 @@ MODULE_big = mongo_fdw # environment object file. # -MONGO_DRIVER = mongo-c-meta-driver -MONGO_PATH = $(MONGO_DRIVER)/src/mongoc -MONGO_INCLUDE = -I$(MONGO_DRIVER)/src/libbson/src/bson/ -I$(MONGO_PATH) -PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -SHLIB_LINK = -L$(MONGO_DRIVER)/.libs -lmongoc-1.0 -SHLIB_LINK += -L$(MONGO_DRIVER)/src/libbson/.libs -lbson-1.0 +MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) +PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -DMETA_DRIVER +SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql -$(MONGO_DRIVER)/%.os: - $(MAKE) -C $(MONGO_DRIVER) $*.os - # # 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 diff --git a/README.md b/README.md index cec383d..07261d1 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,20 @@ Now compile time option is available to use legacy and Meta driver. I am sure t are many other benefits of the new Mongo-C-driver that we are not leveraging but we will adopt those as we learn more about the new C driver. +In order to use MongoDB driver 1.0.0+, take the following steps: + + * clone `libbson` version 1.0.0+ (https://github.com/mongodb/libbson). Follow install directions on that project's README. + * clone `libmongoc` version 1.0.0+ (https://github.com/mongodb/mongo-c-driver). Follow the install directions, except make sure to also run `./configure --with-libbson=system` after running automake but before running make. This should be the default behavior, but to be certain include this step. + * ensure pkg-config / pkgconf is installed on your system. + * run `make -f Makefile.meta && make -f Makefile.meta install` + Usage ----- The following parameters can be set on a MongoDB foreign server object: * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` * **`port`**: the port number of the MongoDB server. Defaults to `27017` + * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary' The following parameters can be set on a MongoDB foreign table object: diff --git a/connection.c b/connection.c index 6a9464e..ac4659d 100644 --- a/connection.c +++ b/connection.c @@ -64,7 +64,11 @@ static HTAB *ConnectionHash = NULL; * is established if we don't already have a suitable one. */ MONGO_CONN* +#ifdef META_DRIVER +mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password, char *readPreference) +#else mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password) +#endif { bool found; ConnCacheEntry *entry; @@ -101,7 +105,12 @@ mongo_get_connection(char *host, int32 port, char *databaseName, char *user, cha } if (entry->conn == NULL) { +#ifdef META_DRIVER + entry->conn = MongoConnect(host, port, databaseName, user, password, readPreference); +#else entry->conn = MongoConnect(host, port, databaseName, user, password); +#endif + elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", entry->conn, host, port); } diff --git a/mongo_fdw.c b/mongo_fdw.c index a4cc8c3..b5ade30 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_fdw.c - * Foreign-data wrapper for remote MongoDB servers + * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * @@ -10,7 +10,7 @@ * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION - * mongo_fdw.c + * mongo_fdw.c * *------------------------------------------------------------------------- */ @@ -65,90 +65,89 @@ #include "utils/memutils.h" #include "utils/jsonapi.h" #if PG_VERSION_NUM >= 90300 - #include "access/htup_details.h" + #include "access/htup_details.h" #endif - /* Local functions forward declarations */ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId, ForeignPath *bestPath, - List *targetList, List *restrictionClauses); + Oid foreignTableId, ForeignPath *bestPath, + List *targetList, List *restrictionClauses); static void MongoExplainForeignScan(ForeignScanState *scanState, - ExplainState *explainState); + ExplainState *explainState); static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); static void MongoReScanForeignScan(ForeignScanState *scanState); static TupleTableSlot *MongoExecForeignUpdate(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static TupleTableSlot *MongoExecForeignDelete(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static void MongoEndForeignModify(EState *estate, - ResultRelInfo *resultRelInfo); + ResultRelInfo *resultRelInfo); static void MongoAddForeignUpdateTargets(Query *parsetree, - RangeTblEntry *target_rte, - Relation target_relation); + RangeTblEntry *target_rte, + Relation target_relation); static void MongoBeginForeignModify(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - List *fdw_private, - int subplan_index, - int eflags); + ResultRelInfo *resultRelInfo, + List *fdw_private, + int subplan_index, + int eflags); static TupleTableSlot *MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static List *MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index); + ModifyTable *plan, + Index resultRelation, + int subplan_index); static void MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, List *fdw_private, - int subplan_index, ExplainState *es); + ResultRelInfo *rinfo, List *fdw_private, + int subplan_index, ExplainState *es); /* local functions */ static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls); static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, - int32 columnTypeMod); + int32 columnTypeMod); static void MongoFreeScanState(MongoFdwModifyState *fmstate); static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount); + AcquireSampleRowsFunc *acquireSampleRowsFunc, + BlockNumber *totalPageCount); static int MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount); + HeapTuple *sampleRows, int targetRowCount, + double *totalRowCount, double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); const char * EscapeJsonString(const char *string); -void DumpJson(StringInfo buffer, const char *bsonData, bool isArray); +void DumpJson(StringInfo buffer, BSON_ITERATOR *bsonData, bool isArray); /* the null action object used for pure validation */ static JsonSemAction nullSemAction = { - NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL }; /* declarations for dynamic loading */ @@ -163,7 +162,7 @@ PG_FUNCTION_INFO_V1(mongo_fdw_handler); void _PG_init(void) { - on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL)); + on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL)); } /* @@ -173,33 +172,33 @@ _PG_init(void) Datum mongo_fdw_handler(PG_FUNCTION_ARGS) { - FdwRoutine *fdwRoutine = makeNode(FdwRoutine); - fdwRoutine->GetForeignRelSize = MongoGetForeignRelSize; - fdwRoutine->GetForeignPaths = MongoGetForeignPaths; - fdwRoutine->GetForeignPlan = MongoGetForeignPlan; - fdwRoutine->BeginForeignScan = MongoBeginForeignScan; - fdwRoutine->IterateForeignScan = MongoIterateForeignScan; - fdwRoutine->ReScanForeignScan = MongoReScanForeignScan; - fdwRoutine->EndForeignScan = MongoEndForeignScan; - fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; - - /* support for insert / update / delete */ - fdwRoutine->ExecForeignInsert = MongoExecForeignInsert; - fdwRoutine->BeginForeignModify = MongoBeginForeignModify; - fdwRoutine->PlanForeignModify = MongoPlanForeignModify; - fdwRoutine->AddForeignUpdateTargets = MongoAddForeignUpdateTargets; - fdwRoutine->ExecForeignUpdate = MongoExecForeignUpdate; - fdwRoutine->ExecForeignDelete = MongoExecForeignDelete; - fdwRoutine->EndForeignModify = MongoEndForeignModify; - - /* support for EXPLAIN */ - fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; - fdwRoutine->ExplainForeignModify = MongoExplainForeignModify; - - /* support for ANALYSE */ - fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; - - PG_RETURN_POINTER(fdwRoutine); + FdwRoutine *fdwRoutine = makeNode(FdwRoutine); + fdwRoutine->GetForeignRelSize = MongoGetForeignRelSize; + fdwRoutine->GetForeignPaths = MongoGetForeignPaths; + fdwRoutine->GetForeignPlan = MongoGetForeignPlan; + fdwRoutine->BeginForeignScan = MongoBeginForeignScan; + fdwRoutine->IterateForeignScan = MongoIterateForeignScan; + fdwRoutine->ReScanForeignScan = MongoReScanForeignScan; + fdwRoutine->EndForeignScan = MongoEndForeignScan; + fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; + + /* support for insert / update / delete */ + fdwRoutine->ExecForeignInsert = MongoExecForeignInsert; + fdwRoutine->BeginForeignModify = MongoBeginForeignModify; + fdwRoutine->PlanForeignModify = MongoPlanForeignModify; + fdwRoutine->AddForeignUpdateTargets = MongoAddForeignUpdateTargets; + fdwRoutine->ExecForeignUpdate = MongoExecForeignUpdate; + fdwRoutine->ExecForeignDelete = MongoExecForeignDelete; + fdwRoutine->EndForeignModify = MongoEndForeignModify; + + /* support for EXPLAIN */ + fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; + fdwRoutine->ExplainForeignModify = MongoExplainForeignModify; + + /* support for ANALYSE */ + fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; + + PG_RETURN_POINTER(fdwRoutine); } /* @@ -208,7 +207,7 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) static void mongo_fdw_exit(int code, Datum arg) { - mongo_cleanup_connection(); + mongo_cleanup_connection(); } @@ -218,26 +217,26 @@ mongo_fdw_exit(int code, Datum arg) static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) { - double documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount > 0.0) - { - /* - * We estimate the number of rows returned after restriction qualifiers - * are applied. This will be more accurate if analyze is run on this - * relation. - */ - List *rowClauseList = baserel->baserestrictinfo; - double rowSelectivity = clauselist_selectivity(root, rowClauseList, - 0, JOIN_INNER, NULL); - - double outputRowCount = clamp_row_est(documentCount * rowSelectivity); - baserel->rows = outputRowCount; - } - else - { - ereport(DEBUG1, (errmsg("could not retrieve document count for collection"), - errhint("Falling back to default estimates in planning"))); - } + double documentCount = ForeignTableDocumentCount(foreignTableId); + if (documentCount > 0.0) + { + /* + * We estimate the number of rows returned after restriction qualifiers + * are applied. This will be more accurate if analyze is run on this + * relation. + */ + List *rowClauseList = baserel->baserestrictinfo; + double rowSelectivity = clauselist_selectivity(root, rowClauseList, + 0, JOIN_INNER, NULL); + + double outputRowCount = clamp_row_est(documentCount * rowSelectivity); + baserel->rows = outputRowCount; + } + else + { + ereport(DEBUG1, (errmsg("could not retrieve document count for collection"), + errhint("Falling back to default estimates in planning"))); + } } @@ -250,74 +249,74 @@ MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableI static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) { - double tupleFilterCost = baserel->baserestrictcost.per_tuple; - double inputRowCount = 0.0; - double documentSelectivity = 0.0; - double foreignTableSize = 0; - int32 documentWidth = 0; - BlockNumber pageCount = 0; - double totalDiskAccessCost = 0.0; - double cpuCostPerDoc = 0.0; - double cpuCostPerRow = 0.0; - double totalCpuCost = 0.0; - double connectionCost = 0.0; - double documentCount = 0.0; - List *opExpressionList = NIL; - Cost startupCost = 0.0; - Cost totalCost = 0.0; - Path *foreignPath = NULL; - - documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount > 0.0) - { - /* - * We estimate the number of rows returned after restriction qualifiers - * are applied by MongoDB. - */ - opExpressionList = ApplicableOpExpressionList(baserel); - 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"))); - } - - /* create a foreign path node */ - foreignPath = (Path *) create_foreignscan_path(root, baserel, baserel->rows, - startupCost, totalCost, - NIL, /* no pathkeys */ - NULL, /* no outer rel either */ - NIL); /* no fdw_private data */ - - /* add foreign path as the only possible path */ - add_path(baserel, foreignPath); + double tupleFilterCost = baserel->baserestrictcost.per_tuple; + double inputRowCount = 0.0; + double documentSelectivity = 0.0; + double foreignTableSize = 0; + int32 documentWidth = 0; + BlockNumber pageCount = 0; + double totalDiskAccessCost = 0.0; + double cpuCostPerDoc = 0.0; + double cpuCostPerRow = 0.0; + double totalCpuCost = 0.0; + double connectionCost = 0.0; + double documentCount = 0.0; + List *opExpressionList = NIL; + Cost startupCost = 0.0; + Cost totalCost = 0.0; + Path *foreignPath = NULL; + + documentCount = ForeignTableDocumentCount(foreignTableId); + if (documentCount > 0.0) + { + /* + * We estimate the number of rows returned after restriction qualifiers + * are applied by MongoDB. + */ + opExpressionList = ApplicableOpExpressionList(baserel); + 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"))); + } + + /* create a foreign path node */ + foreignPath = (Path *) create_foreignscan_path(root, baserel, baserel->rows, + startupCost, totalCost, + NIL, /* no pathkeys */ + NULL, /* no outer rel either */ + NIL); /* no fdw_private data */ + + /* add foreign path as the only possible path */ + add_path(baserel, foreignPath); } @@ -327,50 +326,50 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) * scan, but that decision isn't deterministic or visible to us. */ static ForeignScan * -MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, - ForeignPath *bestPath, List *targetList, List *restrictionClauses) +MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, + ForeignPath *bestPath, List *targetList, List *restrictionClauses) { - Index scanRangeTableIndex = baserel->relid; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - List *opExpressionList = NIL; - BSON *queryDocument = NULL; - List *columnList = NIL; - - /* - * We push down applicable restriction clauses to MongoDB, but for simplicity - * we currently put all the restrictionClauses into the plan node's qual - * list for the executor to re-check. So all we have to do here is strip - * RestrictInfo nodes from the clauses and ignore pseudoconstants (which - * will be handled elsewhere). - */ - restrictionClauses = extract_actual_clauses(restrictionClauses, false); - - /* - * 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. - */ - opExpressionList = ApplicableOpExpressionList(baserel); - queryDocument = QueryDocument(foreignTableId, opExpressionList); - - /* we don't need to serialize column list as lists are copiable */ - columnList = ColumnList(baserel); - - /* construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(columnList, opExpressionList); - - /* only clean up the query struct */ - BsonDestroy(queryDocument); - - /* create the foreign scan node */ - foreignScan = make_foreignscan(targetList, restrictionClauses, - scanRangeTableIndex, - NIL, /* no expressions to evaluate */ - foreignPrivateList); - - return foreignScan; + Index scanRangeTableIndex = baserel->relid; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + List *opExpressionList = NIL; + BSON *queryDocument = NULL; + List *columnList = NIL; + + /* + * We push down applicable restriction clauses to MongoDB, but for simplicity + * we currently put all the restrictionClauses into the plan node's qual + * list for the executor to re-check. So all we have to do here is strip + * RestrictInfo nodes from the clauses and ignore pseudoconstants (which + * will be handled elsewhere). + */ + restrictionClauses = extract_actual_clauses(restrictionClauses, false); + + /* + * 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. + */ + opExpressionList = ApplicableOpExpressionList(baserel); + queryDocument = QueryDocument(foreignTableId, opExpressionList); + + /* we don't need to serialize column list as lists are copiable */ + columnList = ColumnList(baserel); + + /* construct foreign plan with query document and column list */ + foreignPrivateList = list_make2(columnList, opExpressionList); + + /* only clean up the query struct */ + BsonDestroy(queryDocument); + + /* create the foreign scan node */ + foreignScan = make_foreignscan(targetList, restrictionClauses, + scanRangeTableIndex, + NIL, /* no expressions to evaluate */ + foreignPrivateList); + + return foreignScan; } @@ -380,44 +379,44 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, static void MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) { - MongoFdwOptions *mongoFdwOptions = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *mongoFdwOptions = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); + foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + mongoFdwOptions = mongo_get_options(foreignTableId); - /* construct fully qualified collection name */ - namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, - mongoFdwOptions->collectionName); + /* construct fully qualified collection name */ + namespaceName = makeStringInfo(); + appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, + mongoFdwOptions->collectionName); - mongo_free_options(mongoFdwOptions); + mongo_free_options(mongoFdwOptions); - ExplainPropertyText("Foreign Namespace", namespaceName->data, explainState); + ExplainPropertyText("Foreign Namespace", namespaceName->data, explainState); } static void MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, - List *fdw_private, - int subplan_index, - ExplainState *es) + ResultRelInfo *rinfo, + List *fdw_private, + int subplan_index, + ExplainState *es) { - MongoFdwOptions *mongoFdwOptions = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *mongoFdwOptions = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; - foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); - mongoFdwOptions = mongo_get_options(foreignTableId); + foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); + mongoFdwOptions = mongo_get_options(foreignTableId); - /* construct fully qualified collection name */ - namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, - mongoFdwOptions->collectionName); + /* construct fully qualified collection name */ + namespaceName = makeStringInfo(); + appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, + mongoFdwOptions->collectionName); - mongo_free_options(mongoFdwOptions); - ExplainPropertyText("Foreign Namespace", namespaceName->data, es); + mongo_free_options(mongoFdwOptions); + ExplainPropertyText("Foreign Namespace", namespaceName->data, es); } @@ -430,66 +429,70 @@ MongoExplainForeignModify(ModifyTableState *mtstate, static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) { - MONGO_CONN *mongoConnection = NULL; - MONGO_CURSOR *mongoCursor = NULL; - Oid foreignTableId = InvalidOid; - List *columnList = NIL; - HTAB *columnMappingHash = NULL; - char *addressName = NULL; - int32 portNumber = 0; - char *databaseName= NULL; - char *username= NULL; - char *password= NULL; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - BSON *queryDocument = NULL; - MongoFdwOptions *mongoFdwOptions = NULL; - MongoFdwModifyState *fmstate = NULL; - List *opExpressionList = NIL; - - /* if Explain with no Analyze, do nothing */ - if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) - return; - - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); - - /* resolve hostname and port number; and connect to mongo server */ - addressName = mongoFdwOptions->addressName; - portNumber = mongoFdwOptions->portNumber; - databaseName = mongoFdwOptions->databaseName; - username = mongoFdwOptions->username; - password = mongoFdwOptions->password; - - /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. - */ - mongoConnection = mongo_get_connection(addressName, portNumber, databaseName, username, password); - - foreignScan = (ForeignScan *) scanState->ss.ps.plan; - foreignPrivateList = foreignScan->fdw_private; - Assert(list_length(foreignPrivateList) == 2); - - columnList = list_nth(foreignPrivateList, 0); - opExpressionList = list_nth(foreignPrivateList, 1); - - queryDocument = QueryDocument(foreignTableId, opExpressionList); - - columnMappingHash = ColumnMappingHash(foreignTableId, columnList); - - /* create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->databaseName, mongoFdwOptions->collectionName, queryDocument); - - /* create and set foreign execution state */ - fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); - fmstate->columnMappingHash = columnMappingHash; - fmstate->mongoConnection = mongoConnection; - fmstate->mongoCursor = mongoCursor; - fmstate->queryDocument = queryDocument; - fmstate->mongoFdwOptions = mongoFdwOptions; - - scanState->fdw_state = (void *) fmstate; + MONGO_CONN *mongoConnection = NULL; + MONGO_CURSOR *mongoCursor = NULL; + Oid foreignTableId = InvalidOid; + List *columnList = NIL; + HTAB *columnMappingHash = NULL; + char *addressName = NULL; + int32 portNumber = 0; + char *databaseName= NULL; + char *username= NULL; + char *password= NULL; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + BSON *queryDocument = NULL; + MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwModifyState *fmstate = NULL; + List *opExpressionList = NIL; + + /* if Explain with no Analyze, do nothing */ + if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) + return; + + foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + mongoFdwOptions = mongo_get_options(foreignTableId); + + /* resolve hostname and port number; and connect to mongo server */ + addressName = mongoFdwOptions->addressName; + portNumber = mongoFdwOptions->portNumber; + databaseName = mongoFdwOptions->databaseName; + username = mongoFdwOptions->username; + password = mongoFdwOptions->password; + + /* + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. + */ +#ifdef META_DRIVER + mongoConnection = mongo_get_connection(addressName, portNumber, databaseName, username, password, mongoFdwOptions->readPreference); +#else + mongoConnection = mongo_get_connection(addressName, portNumber, databaseName, username, password); +#endif + + foreignScan = (ForeignScan *) scanState->ss.ps.plan; + foreignPrivateList = foreignScan->fdw_private; + Assert(list_length(foreignPrivateList) == 2); + + columnList = list_nth(foreignPrivateList, 0); + opExpressionList = list_nth(foreignPrivateList, 1); + + queryDocument = QueryDocument(foreignTableId, opExpressionList); + + columnMappingHash = ColumnMappingHash(foreignTableId, columnList); + + /* create cursor for collection name and set query */ + mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->databaseName, mongoFdwOptions->collectionName, queryDocument); + + /* create and set foreign execution state */ + fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + fmstate->columnMappingHash = columnMappingHash; + fmstate->mongoConnection = mongoConnection; + fmstate->mongoCursor = mongoCursor; + fmstate->queryDocument = queryDocument; + fmstate->mongoFdwOptions = mongoFdwOptions; + + scanState->fdw_state = (void *) fmstate; } @@ -501,60 +504,60 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - TupleTableSlot *tupleSlot = scanState->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; - - /* - * 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 */ - - FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); - - ExecStoreVirtualTuple(tupleSlot); - } - else - { - #ifdef META_DRIVER - bson_error_t error; - if (mongoc_cursor_error (mongoCursor, &error)) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver error: %s", error.message))); - } - #else - mongo_cursor_error_t errorCode = mongoCursor->err; - if (errorCode != MONGO_CURSOR_EXHAUSTED) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); - } - #endif - } - - return tupleSlot; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + TupleTableSlot *tupleSlot = scanState->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; + + /* + * 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 */ + + FillTupleSlot(bsonDocument, bsonDocumentKey, + columnMappingHash, columnValues, columnNulls); + + ExecStoreVirtualTuple(tupleSlot); + } + else + { + #ifdef META_DRIVER + bson_error_t error; + if (mongoc_cursor_error (mongoCursor, &error)) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver error: %s", error.message))); + } + #else + mongo_cursor_error_t errorCode = mongoCursor->err; + if (errorCode != MONGO_CURSOR_EXHAUSTED) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver cursor error code: %d", errorCode))); + } + #endif + } + + return tupleSlot; } @@ -565,18 +568,18 @@ MongoIterateForeignScan(ForeignScanState *scanState) static void MongoEndForeignScan(ForeignScanState *scanState) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - - /* if we executed a query, reclaim mongo related resources */ - if (fmstate != NULL) - { - if (fmstate->mongoFdwOptions) - { - mongo_free_options(fmstate->mongoFdwOptions); - fmstate->mongoFdwOptions = NULL; - } - MongoFreeScanState(fmstate); - } + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + + /* if we executed a query, reclaim mongo related resources */ + if (fmstate != NULL) + { + if (fmstate->mongoFdwOptions) + { + mongo_free_options(fmstate->mongoFdwOptions); + fmstate->mongoFdwOptions = NULL; + } + MongoFreeScanState(fmstate); + } } @@ -588,91 +591,91 @@ MongoEndForeignScan(ForeignScanState *scanState) static void MongoReScanForeignScan(ForeignScanState *scanState) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *mongoFdwOptions = NULL; - Oid foreignTableId = InvalidOid; - - /* close down the old cursor */ - MongoCursorDestroy(fmstate->mongoCursor); - - /* reconstruct full collection name */ - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); - - /* reconstruct cursor for collection name and set query */ - fmstate->mongoCursor = MongoCursorCreate(mongoConnection, - fmstate->mongoFdwOptions->databaseName, - fmstate->mongoFdwOptions->collectionName, - fmstate->queryDocument); - mongo_free_options(mongoFdwOptions); + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + MONGO_CONN *mongoConnection = fmstate->mongoConnection; + MongoFdwOptions *mongoFdwOptions = NULL; + Oid foreignTableId = InvalidOid; + + /* close down the old cursor */ + MongoCursorDestroy(fmstate->mongoCursor); + + /* reconstruct full collection name */ + foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + mongoFdwOptions = mongo_get_options(foreignTableId); + + /* reconstruct cursor for collection name and set query */ + fmstate->mongoCursor = MongoCursorCreate(mongoConnection, + fmstate->mongoFdwOptions->databaseName, + fmstate->mongoFdwOptions->collectionName, + fmstate->queryDocument); + mongo_free_options(mongoFdwOptions); } static List * MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index) + 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 = heap_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 = tupdesc->attrs[attnum - 1]; - - if (!attr->attisdropped) - targetAttrs = lappend_int(targetAttrs, attnum); - } - } - else if (operation == CMD_UPDATE) - { - Bitmapset *tmpset = bms_copy(rte->modifiedCols); - AttrNumber col; - - while ((col = bms_first_member(tmpset)) >= 0) - { - col += FirstLowInvalidHeapAttributeNumber; - 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"); - - heap_close(rel, NoLock); - - return list_make1(targetAttrs); + 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 = heap_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 = tupdesc->attrs[attnum - 1]; + + if (!attr->attisdropped) + targetAttrs = lappend_int(targetAttrs, attnum); + } + } + else if (operation == CMD_UPDATE) + { + Bitmapset *tmpset = bms_copy(rte->modifiedCols); + AttrNumber col; + + while ((col = bms_first_member(tmpset)) >= 0) + { + col += FirstLowInvalidHeapAttributeNumber; + 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"); + + heap_close(rel, NoLock); + + return list_make1(targetAttrs); } @@ -681,55 +684,55 @@ MongoPlanForeignModify(PlannerInfo *root, */ static void MongoBeginForeignModify(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - List *fdw_private, - int subplan_index, - int eflags) + ResultRelInfo *resultRelInfo, + List *fdw_private, + int subplan_index, + int eflags) { - MongoFdwModifyState *fmstate = NULL; - Relation rel = resultRelInfo->ri_RelationDesc; - AttrNumber n_params = 0; - Oid typefnoid = InvalidOid; - bool isvarlena = false; - ListCell *lc = NULL; - Oid foreignTableId = InvalidOid; - - /* - * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState - * stays NULL. - */ - if (eflags & EXEC_FLAG_EXPLAIN_ONLY) - return; - - foreignTableId = RelationGetRelid(rel); - - /* Begin constructing MongoFdwModifyState. */ - fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); - - fmstate->rel = rel; - fmstate->mongoFdwOptions = mongo_get_options(foreignTableId); - - fmstate->target_attrs = (List *) list_nth(fdw_private, 0); - - n_params = list_length(fmstate->target_attrs) + 1; - fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params); - fmstate->p_nums = 0; - - /* Set up for remaining transmittable parameters */ - foreach(lc, fmstate->target_attrs) - { - int attnum = lfirst_int(lc); - Form_pg_attribute attr = RelationGetDescr(rel)->attrs[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; + MongoFdwModifyState *fmstate = NULL; + Relation rel = resultRelInfo->ri_RelationDesc; + AttrNumber n_params = 0; + Oid typefnoid = InvalidOid; + bool isvarlena = false; + ListCell *lc = NULL; + Oid foreignTableId = InvalidOid; + + /* + * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState + * stays NULL. + */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) + return; + + foreignTableId = RelationGetRelid(rel); + + /* Begin constructing MongoFdwModifyState. */ + fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + + fmstate->rel = rel; + fmstate->mongoFdwOptions = mongo_get_options(foreignTableId); + + fmstate->target_attrs = (List *) list_nth(fdw_private, 0); + + n_params = list_length(fmstate->target_attrs) + 1; + fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params); + fmstate->p_nums = 0; + + /* Set up for remaining transmittable parameters */ + foreach(lc, fmstate->target_attrs) + { + int attnum = lfirst_int(lc); + Form_pg_attribute attr = RelationGetDescr(rel)->attrs[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; } @@ -738,71 +741,75 @@ MongoBeginForeignModify(ModifyTableState *mtstate, */ static TupleTableSlot * MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Oid foreignTableId = InvalidOid; - BSON *b = NULL; - Oid typoid; - Datum value; - bool isnull = false; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Oid foreignTableId = InvalidOid; + BSON *b = NULL; + Oid typoid; + Datum value; + bool isnull = false; - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); - - b = BsonCreate(); - - typoid = get_atttype(foreignTableId, 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(slot->tts_tupleDescriptor->attrs[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 (attnum == 1) - { - /* - * Ignore the value of first column which is row identifier in MongoDb (_id) - * and let MongoDB to insert the unique value for that column. - */ - } - else - { - AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); - } - } - } - BsonFinish(b); - - /* Now we are ready to insert tuple / document into MongoDB */ - MongoInsert(mongoConnection, options->databaseName, options->collectionName, b); - - BsonDestroy(b); +#ifdef META_DRIVER + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password, options->readPreference); +#else + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#endif - return slot; + b = BsonCreate(); + + typoid = get_atttype(foreignTableId, 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(slot->tts_tupleDescriptor->attrs[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 (attnum == 1) + { + /* + * Ignore the value of first column which is row identifier in MongoDb (_id) + * and let MongoDB to insert the unique value for that column. + */ + } + else + { + AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); + } + } + } + BsonFinish(b); + + /* Now we are ready to insert tuple / document into MongoDB */ + MongoInsert(mongoConnection, options->databaseName, options->collectionName, b); + + BsonDestroy(b); + + return slot; } @@ -813,191 +820,199 @@ MongoExecForeignInsert(EState *estate, */ static void MongoAddForeignUpdateTargets(Query *parsetree, - RangeTblEntry *target_rte, - Relation target_relation) + RangeTblEntry *target_rte, + Relation target_relation) { - Var *var = NULL; - const char *attrname = NULL; - TargetEntry *tle = NULL; - - /* - * What we need is the rowid which is the first column - */ - Form_pg_attribute attr = - RelationGetDescr(target_relation)->attrs[0]; - - /* Make a Var representing the desired value */ - var = makeVar(parsetree->resultRelation, - 1, - attr->atttypid, - attr->atttypmod, - InvalidOid, - 0); - - /* Wrap it in a TLE with the right name ... */ - attrname = NameStr(attr->attname); - - 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); + Var *var = NULL; + const char *attrname = NULL; + TargetEntry *tle = NULL; + + /* + * What we need is the rowid which is the first column + */ + Form_pg_attribute attr = + RelationGetDescr(target_relation)->attrs[0]; + + /* Make a Var representing the desired value */ + var = makeVar(parsetree->resultRelation, + 1, + attr->atttypid, + attr->atttypmod, + InvalidOid, + 0); + + /* Wrap it in a TLE with the right name ... */ + attrname = NameStr(attr->attname); + + 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); } static TupleTableSlot * MongoExecForeignUpdate(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; - BSON *op = NULL; - BSON set; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; + BSON *op = NULL; + BSON set; - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#ifdef META_DRIVER + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password, options->readPreference); +#else + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#endif - /* Get the id that was passed up as a resjunk column */ - datum = ExecGetJunkAttribute(planSlot, 1, &isNull); + /* Get the id that was passed up as a resjunk column */ + datum = ExecGetJunkAttribute(planSlot, 1, &isNull); - columnName = get_relid_attribute_name(foreignTableId, 1); + columnName = get_relid_attribute_name(foreignTableId, 1); - typoid = get_atttype(foreignTableId, 1); + typoid = get_atttype(foreignTableId, 1); - b = BsonCreate(); - BsonAppendStartObject(b, "$set", &set); + b = BsonCreate(); + BsonAppendStartObject(b, "$set", &set); - /* get following parameters from slot */ - if (slot != NULL && fmstate->target_attrs != NIL) - { - ListCell *lc; + /* get following parameters from slot */ + if (slot != NULL && fmstate->target_attrs != NIL) + { + ListCell *lc; - foreach(lc, fmstate->target_attrs) - { - int attnum = lfirst_int(lc); - Datum value; - bool isnull; + foreach(lc, fmstate->target_attrs) + { + int attnum = lfirst_int(lc); + Datum value; + bool isnull; - if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) - continue; + if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + continue; - value = slot_getattr(slot, attnum, &isnull); + value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER - AppenMongoValue(&set, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + AppenMongoValue(&set, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); #else - AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, + isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); #endif - } - } - BsonAppendFinishObject(b, &set); - BsonFinish(b); - - op = BsonCreate(); - if (!AppenMongoValue(op, columnName, datum, false, typoid)) - { - BsonDestroy(b); - return NULL; - } - BsonFinish(op); - - /* We are ready to update the row into MongoDB */ - MongoUpdate(mongoConnection, options->databaseName, options->collectionName, op, b); - - BsonDestroy(op); - BsonDestroy(b); - - /* Return NULL if nothing was updated on the remote end */ - return slot; + } + } + BsonAppendFinishObject(b, &set); + BsonFinish(b); + + op = BsonCreate(); + if (!AppenMongoValue(op, columnName, datum, false, typoid)) + { + BsonDestroy(b); + return NULL; + } + BsonFinish(op); + + /* We are ready to update the row into MongoDB */ + MongoUpdate(mongoConnection, options->databaseName, options->collectionName, op, b); + + BsonDestroy(op); + BsonDestroy(b); + + /* Return NULL if nothing was updated on the remote end */ + return slot; } /* * MongoExecForeignDelete - * Delete one row from a foreign table + * Delete one row from a foreign table */ static TupleTableSlot * MongoExecForeignDelete(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); + foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + /* resolve foreign table options; and connect to mongo server */ + options = fmstate->mongoFdwOptions; - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#ifdef META_DRIVER + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password, options->readPreference); +#else + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#endif - /* Get the id that was passed up as a resjunk column */ - datum = ExecGetJunkAttribute(planSlot, 1, &isNull); + /* Get the id that was passed up as a resjunk column */ + datum = ExecGetJunkAttribute(planSlot, 1, &isNull); - columnName = get_relid_attribute_name(foreignTableId, 1); + columnName = get_relid_attribute_name(foreignTableId, 1); - typoid = get_atttype(foreignTableId, 1); + typoid = get_atttype(foreignTableId, 1); - b = BsonCreate(); - if (!AppenMongoValue(b,columnName, datum, false, typoid)) - { - BsonDestroy(b); - return NULL; - } - BsonFinish(b); + b = BsonCreate(); + if (!AppenMongoValue(b,columnName, datum, false, typoid)) + { + BsonDestroy(b); + return NULL; + } + BsonFinish(b); - /* Now we are ready to delete a single document from MongoDB */ - MongoDelete(mongoConnection, options->databaseName, options->collectionName, b); + /* Now we are ready to delete a single document from MongoDB */ + MongoDelete(mongoConnection, options->databaseName, options->collectionName, b); - BsonDestroy(b); + BsonDestroy(b); - /* Return NULL if nothing was updated on the remote end */ - return slot; + /* Return NULL if nothing was updated on the remote end */ + return slot; } /* * MongoEndForeignModify - * Finish an insert/update/delete operation on a foreign table + * Finish an insert/update/delete operation on a foreign table */ static void MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - if (fmstate) - { - if (fmstate->mongoFdwOptions) - { - mongo_free_options(fmstate->mongoFdwOptions); - fmstate->mongoFdwOptions = NULL; - } - MongoFreeScanState(fmstate); - pfree(fmstate); - } + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + if (fmstate) + { + if (fmstate->mongoFdwOptions) + { + mongo_free_options(fmstate->mongoFdwOptions); + fmstate->mongoFdwOptions = NULL; + } + MongoFreeScanState(fmstate); + pfree(fmstate); + } } /* @@ -1008,21 +1023,25 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) static double ForeignTableDocumentCount(Oid foreignTableId) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - const BSON *emptyQuery = NULL; - double documentCount = 0.0; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + const BSON *emptyQuery = NULL; + double documentCount = 0.0; - /* resolve foreign table options; and connect to mongo server */ - options = mongo_get_options(foreignTableId); + /* resolve foreign table options; and connect to mongo server */ + options = mongo_get_options(foreignTableId); - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#ifdef META_DRIVER + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password, options->readPreference); +#else + mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); +#endif - documentCount = MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); + documentCount = MongoAggregateCount(mongoConnection, options->databaseName, options->collectionName, emptyQuery); - mongo_free_options(options); + mongo_free_options(options); - return documentCount; + return documentCount; } @@ -1034,46 +1053,46 @@ ForeignTableDocumentCount(Oid foreignTableId) static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList) { - ListCell *columnCell = NULL; - const long hashTableSize = 2048; - HTAB *columnMappingHash = NULL; - - /* create hash table */ - HASHCTL hashInfo; - 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", hashTableSize, &hashInfo, - (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT)); - Assert(columnMappingHash != NULL); - - foreach(columnCell, columnList) - { - Var *column = (Var *) lfirst(columnCell); - AttrNumber columnId = column->varattno; - - ColumnMapping *columnMapping = NULL; - char *columnName = NULL; - bool handleFound = false; - void *hashKey = NULL; - - columnName = get_relid_attribute_name(foreignTableId, columnId); - hashKey = (void *) columnName; - - columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, - HASH_ENTER, &handleFound); - Assert(columnMapping != NULL); - - columnMapping->columnIndex = columnId - 1; - columnMapping->columnTypeId = column->vartype; - columnMapping->columnTypeMod = column->vartypmod; - columnMapping->columnArrayTypeId = get_element_type(column->vartype); - } - - return columnMappingHash; + ListCell *columnCell = NULL; + const long hashTableSize = 2048; + HTAB *columnMappingHash = NULL; + + /* create hash table */ + HASHCTL hashInfo; + 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", hashTableSize, &hashInfo, + (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT)); + Assert(columnMappingHash != NULL); + + foreach(columnCell, columnList) + { + Var *column = (Var *) lfirst(columnCell); + AttrNumber columnId = column->varattno; + + ColumnMapping *columnMapping = NULL; + char *columnName = NULL; + bool handleFound = false; + void *hashKey = NULL; + + columnName = get_relid_attribute_name(foreignTableId, columnId); + hashKey = (void *) columnName; + + columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, + HASH_ENTER, &handleFound); + Assert(columnMapping != NULL); + + columnMapping->columnIndex = columnId - 1; + columnMapping->columnTypeId = column->vartype; + columnMapping->columnTypeMod = column->vartypmod; + columnMapping->columnArrayTypeId = get_element_type(column->vartype); + } + + return columnMappingHash; } @@ -1087,100 +1106,100 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) */ static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) + HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) { - BSON_ITERATOR bsonIterator = { NULL, 0 }; - BsonIterInit(&bsonIterator, (BSON*)bsonDocument); - - while (BsonIterNext(&bsonIterator)) - { - const char *bsonKey = BsonIterKey(&bsonIterator); - BSON_TYPE bsonType = BsonIterType(&bsonIterator); - - ColumnMapping *columnMapping = NULL; - Oid columnTypeId = InvalidOid; - Oid columnArrayTypeId = InvalidOid; - bool compatibleTypes = false; - bool handleFound = false; - const char *bsonFullKey = NULL; - void *hashKey = 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 && columnTypeId != JSONOID) - { - BSON subObject; - BsonIterSubObject(&bsonIterator, &subObject); - FillTupleSlot(&subObject, bsonFullKey, - columnMappingHash, columnValues, columnNulls); - continue; - } - - /* if no corresponding column or null BSON value, continue */ - if (columnMapping == NULL || bsonType == BSON_TYPE_NULL) - { - continue; - } - - /* check if columns have compatible types */ - - if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) - { - compatibleTypes = true; - } - else - { - compatibleTypes = ColumnTypesCompatible(bsonType, columnTypeId); - } - - /* if types are incompatible, leave this column null */ - if (!compatibleTypes) - { - continue; - } - - /* fill in corresponding column value and null flag */ - if (OidIsValid(columnArrayTypeId)) - { - int32 columnIndex = columnMapping->columnIndex; - - columnValues[columnIndex] = ColumnValueArray(&bsonIterator, - columnArrayTypeId); - columnNulls[columnIndex] = false; - } - else - { - int32 columnIndex = columnMapping->columnIndex; - Oid columnTypeMod = columnMapping->columnTypeMod; - - columnValues[columnIndex] = ColumnValue(&bsonIterator, - columnTypeId, columnTypeMod); - columnNulls[columnIndex] = false; - } - } + BSON_ITERATOR bsonIterator = { NULL, 0 }; + BsonIterInit(&bsonIterator, (BSON*)bsonDocument); + + while (BsonIterNext(&bsonIterator)) + { + const char *bsonKey = BsonIterKey(&bsonIterator); + BSON_TYPE bsonType = BsonIterType(&bsonIterator); + + ColumnMapping *columnMapping = NULL; + Oid columnTypeId = InvalidOid; + Oid columnArrayTypeId = InvalidOid; + bool compatibleTypes = false; + bool handleFound = false; + const char *bsonFullKey = NULL; + void *hashKey = 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 && columnTypeId != JSONOID) + { + BSON subObject; + BsonIterSubObject(&bsonIterator, &subObject); + FillTupleSlot(&subObject, bsonFullKey, + columnMappingHash, columnValues, columnNulls); + continue; + } + + /* if no corresponding column or null BSON value, continue */ + if (columnMapping == NULL || bsonType == BSON_TYPE_NULL) + { + continue; + } + + /* check if columns have compatible types */ + + if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) + { + compatibleTypes = true; + } + else + { + compatibleTypes = ColumnTypesCompatible(bsonType, columnTypeId); + } + + /* if types are incompatible, leave this column null */ + if (!compatibleTypes) + { + continue; + } + + /* fill in corresponding column value and null flag */ + if (OidIsValid(columnArrayTypeId)) + { + int32 columnIndex = columnMapping->columnIndex; + + columnValues[columnIndex] = ColumnValueArray(&bsonIterator, + columnArrayTypeId); + columnNulls[columnIndex] = false; + } + else + { + int32 columnIndex = columnMapping->columnIndex; + Oid columnTypeMod = columnMapping->columnTypeMod; + + columnValues[columnIndex] = ColumnValue(&bsonIterator, + columnTypeId, columnTypeMod); + columnNulls[columnIndex] = false; + } + } } @@ -1192,101 +1211,101 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, static bool ColumnTypesCompatible(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; - } - 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; - } - break; - } - case NAMEOID: - { - /* - * 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. - */ - if (bsonType == BSON_TYPE_OID) - { - 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_OBJECT || bsonType == BSON_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; + 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; + } + 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; + } + break; + } + case NAMEOID: + { + /* + * 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. + */ + if (bsonType == BSON_TYPE_OID) + { + 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; } @@ -1299,47 +1318,47 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { - Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); - uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; - uint32 arrayGrowthFactor = 2; - uint32 arrayIndex = 0; - - ArrayType *columnValueObject = NULL; - Datum columnValueDatum = 0; - bool typeByValue = false; - char typeAlignment = 0; - int16 typeLength = 0; - - BSON_ITERATOR bsonSubIterator = { NULL, 0 }; - BsonIterSubIter(bsonIterator, &bsonSubIterator); - while (BsonIterNext(&bsonSubIterator)) - { - BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); - bool compatibleTypes = false; - - compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); - if (bsonType == BSON_TYPE_NULL || !compatibleTypes) - { - continue; - } - - if (arrayIndex >= arrayCapacity) - { - arrayCapacity *= arrayGrowthFactor; - columnValueArray = repalloc(columnValueArray, arrayCapacity * sizeof(Datum)); - } - - /* use default type modifier (0) to convert column value */ - columnValueArray[arrayIndex] = ColumnValue(&bsonSubIterator, valueTypeId, 0); - arrayIndex++; - } - - get_typlenbyvalalign(valueTypeId, &typeLength, &typeByValue, &typeAlignment); - columnValueObject = construct_array(columnValueArray, arrayIndex, valueTypeId, - typeLength, typeByValue, typeAlignment); - - columnValueDatum = PointerGetDatum(columnValueObject); - return columnValueDatum; + Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); + uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; + uint32 arrayGrowthFactor = 2; + uint32 arrayIndex = 0; + + ArrayType *columnValueObject = NULL; + Datum columnValueDatum = 0; + bool typeByValue = false; + char typeAlignment = 0; + int16 typeLength = 0; + + BSON_ITERATOR bsonSubIterator = { NULL, 0 }; + BsonIterSubIter(bsonIterator, &bsonSubIterator); + while (BsonIterNext(&bsonSubIterator)) + { + BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); + bool compatibleTypes = false; + + compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); + if (bsonType == BSON_TYPE_NULL || !compatibleTypes) + { + continue; + } + + if (arrayIndex >= arrayCapacity) + { + arrayCapacity *= arrayGrowthFactor; + columnValueArray = repalloc(columnValueArray, arrayCapacity * sizeof(Datum)); + } + + /* use default type modifier (0) to convert column value */ + columnValueArray[arrayIndex] = ColumnValue(&bsonSubIterator, valueTypeId, 0); + arrayIndex++; + } + + get_typlenbyvalalign(valueTypeId, &typeLength, &typeByValue, &typeAlignment); + columnValueObject = construct_array(columnValueArray, arrayIndex, valueTypeId, + typeLength, typeByValue, typeAlignment); + + columnValueDatum = PointerGetDatum(columnValueObject); + return columnValueDatum; } @@ -1351,161 +1370,166 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { - Datum columnValue = 0; - - 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 = Float8GetDatum(value); - - /* overlook type modifiers for numeric */ - columnValue = DirectFunctionCall1(float8_numeric, valueDatum); - 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 = BsonIterBinLen(bsonIterator); - const char *value = BsonIterBinData(bsonIterator); - bytea *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; - bool is_array; - StringInfo buffer = makeStringInfo(); - - bson_type type = bson_iterator_type(bsonIterator); - if (type != BSON_ARRAY && type != BSON_OBJECT) - { - ereport(ERROR, (errmsg("cannot convert scolar to json"))); - } - is_array = (BSON_ARRAY == type); - - DumpJson(buffer, bson_iterator_value(bsonIterator), is_array); - - result = cstring_to_text_with_len(buffer->data, buffer->len); - - /* validate it */ - lex = makeJsonLexContext(result, false); - 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; + Datum columnValue = 0; + + 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 = Float8GetDatum(value); + + /* overlook type modifiers for numeric */ + columnValue = DirectFunctionCall1(float8_numeric, valueDatum); + 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; + const char *value; +#ifdef META_DRIVER + value = BsonIterBinData(bsonIterator, &value_len); +#else + value_len = BsonIterBinLen(bsonIterator); + value = BsonIterBinData(bsonIterator); +#endif + bytea *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; + bool is_array; + StringInfo buffer = makeStringInfo(); + + BSON_TYPE type = BSON_ITER_TYPE(bsonIterator); + if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT) + { + ereport(ERROR, (errmsg("cannot convert scalar to json"))); + } + is_array = (BSON_TYPE_ARRAY == type); + + DumpJson(buffer, bsonIterator, is_array); + + result = cstring_to_text_with_len(buffer->data, buffer->len); + + /* validate it */ + lex = makeJsonLexContext(result, false); + 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; } - /* * DumpJson converts BSON document to a JSON string. * isArray signifies if bsonData is contents of array or object. @@ -1514,177 +1538,208 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) * * [1] http://docs.mongodb.org/manual/reference/mongodb-extended-json/ */ +#ifdef META_DRIVER void -DumpJson(StringInfo output, const char *bsonData, bool isArray) { - bson_iterator i; - const char *key; - bool isFirstElement; - char beginSymbol, endSymbol; - bson_type t; - bson_iterator_from_buffer(&i, bsonData); - - if (isArray) - { - beginSymbol = '['; - endSymbol = ']'; - } - else - { - beginSymbol = '{'; - endSymbol = '}'; - } - - appendStringInfoChar(output, beginSymbol); - - isFirstElement = true; - while (bson_iterator_next(&i)) - { - if (!isFirstElement) - { - appendStringInfoChar(output, ','); - } - - t = bson_iterator_type(&i); - if (t == 0) break; - key = bson_iterator_key(&i); - - if (!isArray) - { - appendStringInfo(output, "\"%s\":", key); - } - - switch (t) - { - case BSON_DOUBLE: - appendStringInfo(output, "%f", bson_iterator_double(&i)); - break; - case BSON_STRING: - appendStringInfo(output, "\"%s\"", - EscapeJsonString(bson_iterator_string(&i))); - break; - case BSON_SYMBOL: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("`symbol` BSON type is deprecated and " - "unsupported"), - errhint("Symbol: %s", bson_iterator_string(&i)))); - break; - case BSON_OID: - { - char oidhex[25]; - bson_oid_to_string(bson_iterator_oid(&i), oidhex); - appendStringInfo(output, "\"%s\"", oidhex); - break; - } - case BSON_BOOL: - appendStringInfoString( - output, bson_iterator_bool(&i) ? "true" : "false"); - break; - case BSON_DATE: - appendStringInfo(output, "{\"$date\":%ld}", - (long int)bson_iterator_date(&i)); - break; - case BSON_BINDATA: - /* It's possible to encode the data with base64 here. */ - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `binary data` BSON type " - "is not implemented"))); - break; - case BSON_UNDEFINED: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("`undefined` BSON type is deprecated " - "and unsupported"))); - break; - case BSON_NULL: - appendStringInfoString(output, "null"); - break; - case BSON_REGEX: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `regex` BSON type is " - "not implemented"), - errhint("Regex: %s", bson_iterator_regex(&i)))); - break; - case BSON_CODE: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `code` BSON type is " - "not implemented"), - errhint("Code: %s", bson_iterator_code(&i)))); - break; - case BSON_CODEWSCOPE: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `code with scope` BSON " - "type is not implemented"))); - break; - case BSON_INT: - appendStringInfo(output, "%d", bson_iterator_int(&i)); - break; - case BSON_LONG: - appendStringInfo(output, "%ld", (uint64_t)bson_iterator_long(&i)); - break; - case BSON_TIMESTAMP: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("internal `timestamp` BSON type is " - "and unsupported"))); - break; - case BSON_OBJECT: - DumpJson(output, bson_iterator_value(&i), false); - break; - case BSON_ARRAY: - DumpJson(output, bson_iterator_value(&i), true); - break; - default: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("unsupported BSON type: %d", t))); - } - isFirstElement = false; - } - appendStringInfoChar(output, endSymbol); +DumpJson(StringInfo output, BSON_ITERATOR *iter, bool isArray) { + char *str; + const uint8_t *buf; + uint32_t buf_len; + const BSON *bson; + if (isArray) + { + bson_iter_document (&iter, &buf_len, &buf); + bson_init_static (&bson, buf, buf_len); + str = bson_array_as_json(bson, NULL); + } + else + { + bson_iter_array (&iter, &buf_len, &buf); + bson_init_static (&bson, buf, buf_len); + str = bson_as_json(bson, NULL); + } + appendStringInfoChar(output, *str); + bson_free (str); } - +#else +void +DumpJson(StringInfo output, BSON_ITERATOR *iter, bool isArray) { + BSON_ITERATOR i; + const char *key; + const char *bsonData; + bool isFirstElement; + char beginSymbol, endSymbol; + BSON_TYPE t; + + bsonData = BSON_ITER_VALUE(iter); + + bson_iterator_from_buffer(&i, bsonData); + + if (isArray) + { + beginSymbol = '['; + endSymbol = ']'; + } + else + { + beginSymbol = '{'; + endSymbol = '}'; + } + + appendStringInfoChar(output, beginSymbol); + + isFirstElement = true; + while (BSON_ITER_NEXT(&i)) + { + if (!isFirstElement) + { + appendStringInfoChar(output, ','); + } + + t = BSON_ITER_TYPE(&i); + if (t == 0) break; + key = BSON_ITER_KEY(&i); + + if (!isArray) + { + appendStringInfo(output, "\"%s\":", key); + } + + switch (t) + { + case BSON_TYPE_DOUBLE: + appendStringInfo(output, "%f", BSON_ITER_DOUBLE(&i)); + break; + case BSON_TYPE_UTF8: + appendStringInfo(output, "\"%s\"", + EscapeJsonString(BSON_ITER_UTF8(&i))); + break; + case BSON_TYPE_SYMBOL: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("`symbol` BSON type is deprecated and " + "unsupported"), + errhint("Symbol: %s", BSON_ITER_UTF8(&i)))); + break; + case BSON_TYPE_OID: + { + char oidhex[25]; + bson_oid_to_string(BSON_ITER_OID(&i), oidhex); + appendStringInfo(output, "\"%s\"", oidhex); + break; + } + case BSON_TYPE_BOOL: + appendStringInfoString( + output, BSON_ITER_BOOL(&i) ? "true" : "false"); + break; + case BSON_TYPE_DATE_TIME: + appendStringInfo(output, "{\"$date\":%ld}", + (long int)BSON_ITER_DATE_TIME(&i)); + break; + case BSON_TYPE_BINDATA: + /* It's possible to encode the data with base64 here. */ + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `binary data` BSON type " + "is not implemented"))); + break; + case BSON_TYPE_UNDEFINED: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("`undefined` BSON type is deprecated " + "and unsupported"))); + break; + case BSON_TYPE_NULL: + appendStringInfoString(output, "null"); + break; + case BSON_TYPE_REGEX: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `regex` BSON type is " + "not implemented"), + errhint("Regex: %s", BSON_ITER_REGEX(&i)))); + break; + case BSON_TYPE_CODE: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `code` BSON type is " + "not implemented"), + errhint("Code: %s", BSON_ITER_CODE(&i)))); + break; + case BSON_TYPE_CODEWSCOPE: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for `code with scope` BSON " + "type is not implemented"))); + break; + case BSON_TYPE_INT32: + appendStringInfo(output, "%d", BSON_ITER_INT32(&i)); + break; + case BSON_TYPE_INT64: + appendStringInfo(output, "%ld", (uint64_t)BSON_ITER_INT64(&i)); + break; + case BSON_TYPE_TIMESTAMP: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("internal `timestamp` BSON type is " + "and unsupported"))); + break; + case BSON_TYPE_DOCUMENT: + bson_t tags; + uint32_t len; + const uint8_t *data; + bson_init_static(&tags, data, len); + DumpJson(output, BSON_ITER_VALUE(&i), false); + break; + case BSON_TYPE_ARRAY: + DumpJson(output, BSON_ITER_VALUE(&i), true); + break; + default: + ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("unsupported BSON type: %d", t))); + } + isFirstElement = false; + } + appendStringInfoChar(output, endSymbol); +} +#endif /* * EscapeJsonString escapes a string for safe inclusion in JSON. */ const char * EscapeJsonString(const char *string) { - StringInfo buffer; - const char *ptr; - int i, segmentStartIdx, len; - bool needsEscaping = false; - for (ptr = string; *ptr; ++ptr) { - if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ - || *ptr == '\\') { - needsEscaping = true; - } - } - - if (!needsEscaping) return string; - - buffer = makeStringInfo(); - len = strlen(string); - segmentStartIdx = 0; - for (i = 0; i < len; ++i) { - if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' - || string[i] == '\t' || string[i] == '\\') { - if (segmentStartIdx < i) { - appendBinaryStringInfo(buffer, string + segmentStartIdx, - i - segmentStartIdx); - } - - appendStringInfoChar(buffer, '\\'); - if (string[i] == '"') appendStringInfoChar(buffer, '"'); - else if (string[i] == '\r') appendStringInfoChar(buffer, 'r'); - else if (string[i] == '\n') appendStringInfoChar(buffer, 'n'); - else if (string[i] == '\t') appendStringInfoChar(buffer, 't'); - else if (string[i] == '\\') appendStringInfoChar(buffer, '\\'); - - segmentStartIdx = i + 1; - } - } - if (segmentStartIdx < len) { - appendBinaryStringInfo(buffer, string + segmentStartIdx, - len - segmentStartIdx); - } - return buffer->data; + StringInfo buffer; + const char *ptr; + int i, segmentStartIdx, len; + bool needsEscaping = false; + for (ptr = string; *ptr; ++ptr) { + if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ + || *ptr == '\\') { + needsEscaping = true; + } + } + + if (!needsEscaping) return string; + + buffer = makeStringInfo(); + len = strlen(string); + segmentStartIdx = 0; + for (i = 0; i < len; ++i) { + if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' + || string[i] == '\t' || string[i] == '\\') { + if (segmentStartIdx < i) { + appendBinaryStringInfo(buffer, string + segmentStartIdx, + i - segmentStartIdx); + } + + appendStringInfoChar(buffer, '\\'); + if (string[i] == '"') appendStringInfoChar(buffer, '"'); + else if (string[i] == '\r') appendStringInfoChar(buffer, 'r'); + else if (string[i] == '\n') appendStringInfoChar(buffer, 'n'); + else if (string[i] == '\t') appendStringInfoChar(buffer, 't'); + else if (string[i] == '\\') appendStringInfoChar(buffer, '\\'); + + segmentStartIdx = i + 1; + } + } + if (segmentStartIdx < len) { + appendBinaryStringInfo(buffer, string + segmentStartIdx, + len - segmentStartIdx); + } + return buffer->data; } @@ -1695,23 +1750,23 @@ EscapeJsonString(const char *string) { static void MongoFreeScanState(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); + 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); } @@ -1720,47 +1775,47 @@ MongoFreeScanState(MongoFdwModifyState *fmstate) */ static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount) + AcquireSampleRowsFunc *acquireSampleRowsFunc, + BlockNumber *totalPageCount) { - BlockNumber pageCount = 0; - int attributeCount = 0; - int32 *attributeWidths = NULL; - Oid foreignTableId = InvalidOid; - int32 documentWidth = 0; - double documentCount = 0.0; - double foreignTableSize = 0; - - foreignTableId = RelationGetRelid(relation); - - documentCount = ForeignTableDocumentCount(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(ERROR, (errmsg("could not retrieve document count for collection"), - errhint("could not collect statistics about foreign table"))); - } - - (*totalPageCount) = pageCount; - (*acquireSampleRowsFunc) = MongoAcquireSampleRows; - - return true; + BlockNumber pageCount = 0; + int attributeCount = 0; + int32 *attributeWidths = NULL; + Oid foreignTableId = InvalidOid; + int32 documentWidth = 0; + double documentCount = 0.0; + double foreignTableSize = 0; + + foreignTableId = RelationGetRelid(relation); + + documentCount = ForeignTableDocumentCount(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(ERROR, (errmsg("could not retrieve document count for collection"), + errhint("could not collect statistics about foreign table"))); + } + + (*totalPageCount) = pageCount; + (*acquireSampleRowsFunc) = MongoAcquireSampleRows; + + return true; } @@ -1780,194 +1835,194 @@ MongoAnalyzeForeignTable(Relation relation, */ static int MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount) + HeapTuple *sampleRows, int targetRowCount, + double *totalRowCount, double *totalDeadRowCount) { - int sampleRowCount = 0; - double rowCount = 0; - double rowCountToSkip = -1; /* -1 means not set yet */ - double randomState = 0; - Datum *columnValues = NULL; - bool *columnNulls = NULL; - Oid foreignTableId = InvalidOid; - TupleDesc tupleDescriptor = NULL; - Form_pg_attribute *attributesPtr = NULL; - AttrNumber columnCount = 0; - AttrNumber columnId = 0; - HTAB *columnMappingHash = NULL; - MONGO_CURSOR *mongoCursor = NULL; - BSON *queryDocument = NULL; - List *columnList = NIL; - ForeignScanState *scanState = NULL; - List *foreignPrivateList = NIL; - ForeignScan *foreignScan = NULL; - MongoFdwModifyState *fmstate = NULL; - char *relationName = NULL; - int executorFlags = 0; - MemoryContext oldContext = CurrentMemoryContext; - MemoryContext tupleContext = NULL; - - /* create list of columns in the relation */ - tupleDescriptor = RelationGetDescr(relation); - columnCount = tupleDescriptor->natts; - attributesPtr = tupleDescriptor->attrs; - - for (columnId = 1; columnId <= columnCount; columnId++) - { - Var *column = (Var *) palloc0(sizeof(Var)); - - /* only assign required fields for column mapping hash */ - column->varattno = columnId; - column->vartype = attributesPtr[columnId-1]->atttypid; - column->vartypmod = attributesPtr[columnId-1]->atttypmod; - - columnList = lappend(columnList, column); - } - - /* create state structure */ - scanState = makeNode(ForeignScanState); - scanState->ss.ss_currentRelation = relation; - - foreignTableId = RelationGetRelid(relation); - queryDocument = QueryDocument(foreignTableId, NIL); - foreignPrivateList = list_make2(columnList, NULL); - - /* only clean up the query struct, but not its data */ - BsonDestroy(queryDocument); - - foreignScan = makeNode(ForeignScan); - foreignScan->fdw_private = foreignPrivateList; - - scanState->ss.ps.plan = (Plan *) foreignScan; - - MongoBeginForeignScan(scanState, executorFlags); - - fmstate = (MongoFdwModifyState *) scanState->fdw_state; - mongoCursor = fmstate->mongoCursor; - columnMappingHash = fmstate->columnMappingHash; - - /* - * 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_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - - /* prepare for sampling rows */ - randomState = anl_init_selection_state(targetRowCount); - - columnValues = (Datum *) palloc0(columnCount * sizeof(Datum)); - columnNulls = (bool *) palloc0(columnCount * sizeof(bool)); - - for (;;) - { - /* check for user-requested abort or sleep */ - vacuum_delay_point(); - - /* 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); - - FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); - - MemoryContextSwitchTo(oldContext); - } - else - { - #ifdef META_DRIVER - bson_error_t error; - if (mongoc_cursor_error (mongoCursor, &error)) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver error: %s", error.message))); - } - #else - mongo_cursor_error_t errorCode = mongoCursor->err; - if (errorCode != MONGO_CURSOR_EXHAUSTED) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); - } - #endif - 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; - } - - /* clean up */ - MemoryContextDelete(tupleContext); - MongoFreeScanState(fmstate); - - 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; + int sampleRowCount = 0; + double rowCount = 0; + double rowCountToSkip = -1; /* -1 means not set yet */ + double randomState = 0; + Datum *columnValues = NULL; + bool *columnNulls = NULL; + Oid foreignTableId = InvalidOid; + TupleDesc tupleDescriptor = NULL; + Form_pg_attribute *attributesPtr = NULL; + AttrNumber columnCount = 0; + AttrNumber columnId = 0; + HTAB *columnMappingHash = NULL; + MONGO_CURSOR *mongoCursor = NULL; + BSON *queryDocument = NULL; + List *columnList = NIL; + ForeignScanState *scanState = NULL; + List *foreignPrivateList = NIL; + ForeignScan *foreignScan = NULL; + MongoFdwModifyState *fmstate = NULL; + char *relationName = NULL; + int executorFlags = 0; + MemoryContext oldContext = CurrentMemoryContext; + MemoryContext tupleContext = NULL; + + /* create list of columns in the relation */ + tupleDescriptor = RelationGetDescr(relation); + columnCount = tupleDescriptor->natts; + attributesPtr = tupleDescriptor->attrs; + + for (columnId = 1; columnId <= columnCount; columnId++) + { + Var *column = (Var *) palloc0(sizeof(Var)); + + /* only assign required fields for column mapping hash */ + column->varattno = columnId; + column->vartype = attributesPtr[columnId-1]->atttypid; + column->vartypmod = attributesPtr[columnId-1]->atttypmod; + + columnList = lappend(columnList, column); + } + + /* create state structure */ + scanState = makeNode(ForeignScanState); + scanState->ss.ss_currentRelation = relation; + + foreignTableId = RelationGetRelid(relation); + queryDocument = QueryDocument(foreignTableId, NIL); + foreignPrivateList = list_make2(columnList, NULL); + + /* only clean up the query struct, but not its data */ + BsonDestroy(queryDocument); + + foreignScan = makeNode(ForeignScan); + foreignScan->fdw_private = foreignPrivateList; + + scanState->ss.ps.plan = (Plan *) foreignScan; + + MongoBeginForeignScan(scanState, executorFlags); + + fmstate = (MongoFdwModifyState *) scanState->fdw_state; + mongoCursor = fmstate->mongoCursor; + columnMappingHash = fmstate->columnMappingHash; + + /* + * 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_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* prepare for sampling rows */ + randomState = anl_init_selection_state(targetRowCount); + + columnValues = (Datum *) palloc0(columnCount * sizeof(Datum)); + columnNulls = (bool *) palloc0(columnCount * sizeof(bool)); + + for (;;) + { + /* check for user-requested abort or sleep */ + vacuum_delay_point(); + + /* 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); + + FillTupleSlot(bsonDocument, bsonDocumentKey, + columnMappingHash, columnValues, columnNulls); + + MemoryContextSwitchTo(oldContext); + } + else + { + #ifdef META_DRIVER + bson_error_t error; + if (mongoc_cursor_error (mongoCursor, &error)) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver error: %s", error.message))); + } + #else + mongo_cursor_error_t errorCode = mongoCursor->err; + if (errorCode != MONGO_CURSOR_EXHAUSTED) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver cursor error code: %d", errorCode))); + } + #endif + 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; + } + + /* clean up */ + MemoryContextDelete(tupleContext); + MongoFreeScanState(fmstate); + + 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; } diff --git a/mongo_fdw.h b/mongo_fdw.h index 9a6eb99..e0820a5 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_fdw.h - * Foreign-data wrapper for remote MongoDB servers + * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * @@ -10,7 +10,7 @@ * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION - * mongo_fdw.h + * mongo_fdw.h * *------------------------------------------------------------------------- */ @@ -23,9 +23,9 @@ #include "bson.h" #ifdef META_DRIVER - #include "mongoc.h" + #include "mongoc.h" #else - #include "mongo.h" + #include "mongo.h" #endif #include "fmgr.h" @@ -58,28 +58,87 @@ #include "catalog/pg_user_mapping.h" #ifdef META_DRIVER - #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 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 #else - #define BSON bson - #define BSON_TYPE bson_type - #define BSON_ITERATOR bson_iterator - #define MONGO_CONN mongo - #define MONGO_CURSOR mongo_cursor - #define BSON_TYPE_DOCUMENT BSON_OBJECT - #define BSON_TYPE_NULL BSON_NULL - #define BSON_TYPE_ARRAY BSON_ARRAY - #define BSON_TYPE_INT32 BSON_INT - #define BSON_TYPE_INT64 BSON_LONG - #define BSON_TYPE_DOUBLE BSON_DOUBLE - #define BSON_TYPE_BINDATA BSON_BINDATA - #define BSON_TYPE_BOOL BSON_BOOL - #define BSON_TYPE_UTF8 BSON_STRING - #define BSON_TYPE_OID BSON_OID - #define BSON_TYPE_DATE_TIME BSON_DATE + #define BSON bson + #define BSON_TYPE bson_type + #define BSON_ITERATOR bson_iterator + #define MONGO_CONN mongo + #define MONGO_CURSOR mongo_cursor + #define BSON_TYPE_DOCUMENT BSON_OBJECT + #define BSON_TYPE_NULL BSON_NULL + #define BSON_TYPE_ARRAY BSON_ARRAY + #define BSON_TYPE_INT32 BSON_INT + #define BSON_TYPE_INT64 BSON_LONG + #define BSON_TYPE_DOUBLE BSON_DOUBLE + #define BSON_TYPE_BINDATA BSON_BINDATA + #define BSON_TYPE_BOOL BSON_BOOL + #define BSON_TYPE_UTF8 BSON_STRING + #define BSON_TYPE_OID BSON_OID + #define BSON_TYPE_DATE_TIME BSON_DATE + #define BSON_TYPE_SYMBOL BSON_SYMBOL + #define BSON_TYPE_UNDEFINED BSON_UNDEFINED + #define BSON_TYPE_REGEX BSON_REGEX + #define BSON_TYPE_CODE BSON_CODE + #define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE + #define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP + + #define BSON_ITER_BOOL bson_iterator_bool + #define BSON_ITER_DOUBLE bson_iterator_double + #define BSON_ITER_INT32 bson_iterator_int + #define BSON_ITER_INT64 bson_iterator_long + #define BSON_ITER_OID bson_iterator_oid + #define BSON_ITER_UTF8 bson_iterator_string + #define BSON_ITER_REGEX bson_iterator_regex + #define BSON_ITER_DATE_TIME bson_iterator_date + #define BSON_ITER_CODE bson_iterator_code + #define BSON_ITER_VALUE bson_iterator_value + #define BSON_ITER_KEY bson_iterator_key + #define BSON_ITER_NEXT bson_iterator_next + #define BSON_ITER_TYPE bson_iterator_type + #define BSON_ITER_BINARY bson_iterator_bin_data #endif /* Defines for valid option names */ @@ -89,6 +148,9 @@ #define OPTION_NAME_COLLECTION "collection" #define OPTION_NAME_USERNAME "username" #define OPTION_NAME_PASSWORD "password" +#ifdef META_DRIVER +#define OPTION_NAME_READ_PREFERENCE "read_preference" +#endif /* Default values for option parameters */ #define DEFAULT_IP_ADDRESS "127.0.0.1" @@ -111,27 +173,35 @@ */ typedef struct MongoValidOption { - const char *optionName; - Oid optionContextId; + const char *optionName; + Oid optionContextId; } MongoValidOption; /* Array of options that are valid for mongo_fdw */ +#ifdef META_DRIVER +static const uint32 ValidOptionCount = 7; +#else static const uint32 ValidOptionCount = 6; +#endif static const MongoValidOption ValidOptionArray[] = { - /* foreign server options */ - { OPTION_NAME_ADDRESS, ForeignServerRelationId }, - { OPTION_NAME_PORT, ForeignServerRelationId }, + /* foreign server options */ + { OPTION_NAME_ADDRESS, ForeignServerRelationId }, + { OPTION_NAME_PORT, ForeignServerRelationId }, - /* foreign table options */ - { OPTION_NAME_DATABASE, ForeignTableRelationId }, - { OPTION_NAME_COLLECTION, ForeignTableRelationId }, +#ifdef META_DRIVER + { OPTION_NAME_READ_PREFERENCE, ForeignServerRelationId }, +#endif - /* User mapping options */ - { OPTION_NAME_USERNAME, UserMappingRelationId }, - { OPTION_NAME_PASSWORD, UserMappingRelationId } + /* foreign table options */ + { OPTION_NAME_DATABASE, ForeignTableRelationId }, + { OPTION_NAME_COLLECTION, ForeignTableRelationId }, + + /* User mapping options */ + { OPTION_NAME_USERNAME, UserMappingRelationId }, + { OPTION_NAME_PASSWORD, UserMappingRelationId } }; @@ -143,12 +213,15 @@ static const MongoValidOption ValidOptionArray[] = */ typedef struct MongoFdwOptions { - char *addressName; - int32 portNumber; - char *databaseName; - char *collectionName; - char *username; - char *password; + char *addressName; + int32 portNumber; + char *databaseName; + char *collectionName; + char *username; + char *password; +#ifdef META_DRIVER + char *readPreference; +#endif } MongoFdwOptions; @@ -161,23 +234,23 @@ typedef struct MongoFdwOptions */ typedef struct MongoFdwModifyState { - Relation rel; /* relcache entry for the foreign table */ - List *target_attrs; /* list of target attribute numbers */ + 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 */ + /* 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; + struct HTAB *columnMappingHash; - MONGO_CONN *mongoConnection; /* MongoDB connection */ - MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ - BSON *queryDocument; /* Bson Document */ + MONGO_CONN *mongoConnection; /* MongoDB connection */ + MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ + BSON *queryDocument; /* Bson Document */ - MongoFdwOptions *mongoFdwOptions; + MongoFdwOptions *mongoFdwOptions; - /* working memory context */ - MemoryContext temp_cxt; /* context for per-tuple temporary data */ + /* working memory context */ + MemoryContext temp_cxt; /* context for per-tuple temporary data */ } MongoFdwModifyState; @@ -189,11 +262,11 @@ typedef struct MongoFdwModifyState */ typedef struct ColumnMapping { - char columnName[NAMEDATALEN]; - uint32 columnIndex; - Oid columnTypeId; - int32 columnTypeMod; - Oid columnArrayTypeId; + char columnName[NAMEDATALEN]; + uint32 columnIndex; + Oid columnTypeId; + int32 columnTypeMod; + Oid columnArrayTypeId; } ColumnMapping; /* options.c */ @@ -202,7 +275,11 @@ extern void mongo_free_options(MongoFdwOptions *mongoFdwOptions); extern StringInfo mongo_option_names_string(Oid currentContextId); /* connection.c */ -extern MONGO_CONN *mongo_get_connection(char *host, int32 port, char *databaneName, char *user, char *password); +#ifdef META_DRIVER +extern MONGO_CONN *mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password, char *readPreference); +#else +extern MONGO_CONN *mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password); +#endif extern void mongo_cleanup_connection(void); extern void mongo_release_connection(MONGO_CONN* conn); diff --git a/mongo_query.c b/mongo_query.c index 77945b8..8358903 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -251,8 +251,13 @@ QueryDocument(Oid relationId, List *opExpressionList) if (!BsonFinish(queryDocument)) { +#ifdef META_DRIVER + ereport(ERROR, (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); +#else ereport(ERROR, (errmsg("could not create document for query"), errhint("BSON error: %d", queryDocument->err))); +#endif } return queryDocument; diff --git a/mongo_wrapper.h b/mongo_wrapper.h index b08640e..8406010 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -26,7 +26,11 @@ #include "mongo.h" #endif +#ifdef META_DRIVER +MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, char *readPreference); +#else MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); +#endif 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); @@ -47,8 +51,12 @@ int64_t BsonIterInt64(BSON_ITERATOR *it); double BsonIterDouble(BSON_ITERATOR *it); bool BsonIterBool(BSON_ITERATOR *it); const char* BsonIterString(BSON_ITERATOR *it); +#ifdef META_DRIVER +const char* BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); +#else const char* BsonIterBinData(BSON_ITERATOR *it); int BsonIterBinLen(BSON_ITERATOR *it); +#endif const bson_oid_t * BsonIterOid(BSON_ITERATOR *it); time_t BsonIterDate(BSON_ITERATOR *it); const char* BsonIterKey(BSON_ITERATOR *it); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 400bd12..1234354 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -25,13 +25,17 @@ * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN* -MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password) +MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, char *readPreference) { MONGO_CONN *client = NULL; char* uri = NULL; - if (user && password) + if (user && password && readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/?readPreference=%s", user, password, host, port, readPreference); + else if (user && password) uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/", user, password, host, port); + else if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%hu/?readPreference=%s", host, port, readPreference); else uri = bson_strdup_printf ("mongodb://%s:%hu/", host, port); @@ -130,7 +134,7 @@ MongoCursorCreate(MONGO_CONN* conn, char* database, char *collection, BSON* q) bson_error_t error; c = mongoc_client_get_collection (conn, database, collection); - cur = mongoc_collection_find(c, MONGOC_QUERY_NONE, 0, 0, 0, q, NULL, NULL); + cur = mongoc_collection_find(c, MONGOC_QUERY_SLAVE_OK, 0, 0, 0, q, NULL, NULL); mongoc_cursor_error(cur, &error); if (!cur) ereport(ERROR, (errmsg("failed to create cursor"), @@ -247,6 +251,14 @@ BsonIterString(BSON_ITERATOR *it) 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) @@ -354,6 +366,11 @@ 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, v, len); +} bool BsonAppendDate(BSON *b, const char* key, time_t v) @@ -395,34 +412,41 @@ BsonFinish(BSON* b) * Count the number of documents. */ double -MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) -{ - BSON *cmd = NULL; - BSON *out; - double count = -1; - bool r = false; - bson_error_t error; - mongoc_collection_t *c = NULL; +MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) +{ + const BSON *command; + BSON *reply; + const BSON *doc; + double count; + mongoc_cursor_t *cursor; + bool ret; + + command = BsonCreate(); + reply = BsonCreate(); + BsonAppendUTF8(command, "count", (char*)collection); + if (b) /* not empty */ + BsonAppendBson(command, "query", (BSON*)b); - c = mongoc_client_get_collection (conn, database, collection); + BsonFinish(command); - cmd = BsonCreate(); - out = BsonCreate(); - BsonAppendUTF8(cmd, "count", (char*)collection); - if (b) /* not empty */ - BsonAppendBson(cmd, "query", (BSON*)b); + cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, 0, command, NULL, NULL); - BsonFinish(cmd); - r = mongoc_collection_command_simple(c, cmd, NULL, out, &error); - if (r) - { + ret = mongoc_cursor_next(cursor, &doc); + + if (ret) { + bson_copy_to(doc, reply); bson_iter_t it; - if (bson_iter_init_find(&it, out, "n")) - count = BsonIterDouble(&it); + if (bson_iter_init_find(&it, reply, "n")) + count = BsonIterDouble(&it); + } else { + count = -1; } - mongoc_collection_destroy(c); - BsonDestroy(out); - BsonDestroy(cmd); + mongoc_cursor_destroy(cursor); + + BsonDestroy(reply); + BsonDestroy(doc); + BsonDestroy(command); + return count; } diff --git a/option.c b/option.c index eb8991a..5448bf6 100644 --- a/option.c +++ b/option.c @@ -152,6 +152,11 @@ mongo_get_options(Oid foreignTableId) char *collectionName = NULL; char *username= NULL; char *password= NULL; +#ifdef META_DRIVER + char *readPreference = NULL; + + readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); +#endif addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); if (addressName == NULL) @@ -181,11 +186,13 @@ mongo_get_options(Oid foreignTableId) mongoFdwOptions->collectionName = collectionName; mongoFdwOptions->username = username; mongoFdwOptions->password = password; +#ifdef META_DRIVER + mongoFdwOptions->readPreference = readPreference; +#endif return mongoFdwOptions; } - void mongo_free_options(MongoFdwOptions *mongoFdwOptions) { From 1f705b1de3ee0c9ac68381ad485ae5982c0e2f5c Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Sun, 19 Oct 2014 22:51:56 -0700 Subject: [PATCH 049/239] Removing mongo-c-meta-driver submodule --- .gitmodules | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 181eb58..8da9629 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "mongo-c-driver"] path = mongo-c-driver url = ../../mongodb/mongo-c-driver.git -[submodule "mongo-c-meta-driver"] - path = mongo-c-meta-driver - url = ../../mongodb/mongo-c-driver.git From 851f954c88c74f8da70c9a7263a8ef58c68e3dad Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 28 Oct 2014 22:08:10 -0700 Subject: [PATCH 050/239] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 07261d1..339fee3 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ In order to use MongoDB driver 1.0.0+, take the following steps: * clone `libmongoc` version 1.0.0+ (https://github.com/mongodb/mongo-c-driver). Follow the install directions, except make sure to also run `./configure --with-libbson=system` after running automake but before running make. This should be the default behavior, but to be certain include this step. * ensure pkg-config / pkgconf is installed on your system. * run `make -f Makefile.meta && make -f Makefile.meta install` + * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, then try running `ldconfig` Usage ----- From 7a3d32e8f6374ec712ba24939290208e5874bf72 Mon Sep 17 00:00:00 2001 From: TruongSinh Tran-Nguyen Date: Tue, 28 Oct 2014 11:43:18 +0200 Subject: [PATCH 051/239] support write text[] (TEXTARRAYOID) --- mongo_query.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/mongo_query.c b/mongo_query.c index 77945b8..c9b43b0 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -537,6 +537,42 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu 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 t; + + 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, &t); + for (i = 0; i < num_elems; i++) + { + if (elem_nulls[i]) + continue; + char *valueString = NULL; + Oid outputFunctionId = InvalidOid; + bool typeVarLength = false; + getTypeOutputInfo(TEXTOID, &outputFunctionId, &typeVarLength); + valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); + status = BsonAppendUTF8(queryDocument, keyName, valueString); + } + BsonAppendFinishArray(queryDocument, &t); + pfree(elem_values); + pfree(elem_nulls); + break; + } default: { /* From c25671b625dab8a25008b426ca3407a2af31b293 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 30 Oct 2014 22:25:51 +0500 Subject: [PATCH 052/239] Error #7: Fixed compiler warning --- mongo_fdw.c | 2 +- mongo_query.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index a4cc8c3..6844abf 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1618,7 +1618,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) { appendStringInfo(output, "%d", bson_iterator_int(&i)); break; case BSON_LONG: - appendStringInfo(output, "%ld", (uint64_t)bson_iterator_long(&i)); + appendStringInfo(output, "%lld", (uint64_t)bson_iterator_long(&i)); break; case BSON_TIMESTAMP: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), diff --git a/mongo_query.c b/mongo_query.c index c9b43b0..c041b30 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -559,11 +559,11 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu BsonAppendStartArray(queryDocument, keyName, &t); for (i = 0; i < num_elems; i++) { - if (elem_nulls[i]) - continue; char *valueString = NULL; Oid outputFunctionId = InvalidOid; bool typeVarLength = false; + if (elem_nulls[i]) + continue; getTypeOutputInfo(TEXTOID, &outputFunctionId, &typeVarLength); valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); status = BsonAppendUTF8(queryDocument, keyName, valueString); From 579452745bed9da9945fe969c3e9b60dfbc80e53 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 31 Oct 2014 00:20:51 +0500 Subject: [PATCH 053/239] Error #8: Using serverid and userid for connection pooling instead of hot and port. --- connection.c | 23 ++++----- mongo_fdw.c | 121 +++++++++++++++++++++++++++++++++++++----------- mongo_fdw.h | 12 ++--- mongo_wrapper.c | 4 +- option.c | 31 +++++++------ 5 files changed, 128 insertions(+), 63 deletions(-) diff --git a/connection.c b/connection.c index 6a9464e..83560c2 100644 --- a/connection.c +++ b/connection.c @@ -34,16 +34,14 @@ /* * Connection cache hash table entry * - * The lookup key in this hash table is the foreign Mongo server name / IP - * and the server port number. (We use just one connection per user per foreign server, + * 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.) - * - * The "conn" pointer can be NULL if we don't currently have a live connection. */ typedef struct ConnCacheKey { - char host[HOST_LEN]; /* MongoDB's host name / IP address */ - int32 port; /* MongoDB's port number */ + Oid serverid; /* OID of foreign server */ + Oid userid; /* OID of local user whose mapping we use */ } ConnCacheKey; typedef struct ConnCacheEntry @@ -64,7 +62,7 @@ static HTAB *ConnectionHash = NULL; * is established if we don't already have a suitable one. */ MONGO_CONN* -mongo_get_connection(char *host, int32 port, char *databaseName, char *user, char *password) +mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions *opt) { bool found; ConnCacheEntry *entry; @@ -85,10 +83,9 @@ mongo_get_connection(char *host, int32 port, char *databaseName, char *user, cha HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); } - /* Create hash key for the entry */ - memset(key.host, 0, HOST_LEN); - strncpy(key.host, host, HOST_LEN); - key.port = port; + /* 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. @@ -101,9 +98,9 @@ mongo_get_connection(char *host, int32 port, char *databaseName, char *user, cha } if (entry->conn == NULL) { - entry->conn = MongoConnect(host, port, databaseName, user, password); + entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", - entry->conn, host, port); + entry->conn, opt->svr_address, opt->svr_port); } return entry->conn; diff --git a/mongo_fdw.c b/mongo_fdw.c index 6844abf..d50c71e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -389,7 +389,7 @@ MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) /* construct fully qualified collection name */ namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, + appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->svr_database, mongoFdwOptions->collectionName); mongo_free_options(mongoFdwOptions); @@ -413,7 +413,7 @@ MongoExplainForeignModify(ModifyTableState *mtstate, /* construct fully qualified collection name */ namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->databaseName, + appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->svr_database, mongoFdwOptions->collectionName); mongo_free_options(mongoFdwOptions); @@ -435,17 +435,21 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) Oid foreignTableId = InvalidOid; List *columnList = NIL; HTAB *columnMappingHash = NULL; - char *addressName = NULL; - int32 portNumber = 0; - char *databaseName= NULL; - char *username= NULL; - char *password= NULL; ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; BSON *queryDocument = NULL; MongoFdwOptions *mongoFdwOptions = NULL; MongoFdwModifyState *fmstate = NULL; List *opExpressionList = NIL; + RangeTblEntry *rte; + EState *estate = scanState->ss.ps.state; + ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; + + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; + /* if Explain with no Analyze, do nothing */ if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) @@ -454,18 +458,25 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); mongoFdwOptions = mongo_get_options(foreignTableId); - /* resolve hostname and port number; and connect to mongo server */ - addressName = mongoFdwOptions->addressName; - portNumber = mongoFdwOptions->portNumber; - databaseName = mongoFdwOptions->databaseName; - username = mongoFdwOptions->username; - password = mongoFdwOptions->password; + fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + /* + * Identify which user to do the remote access as. This should match what + * ExecCheckRTEPerms() does. + */ + rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table); + userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); + + /* Get info about foreign table. */ + fmstate->rel = scanState->ss.ss_currentRelation; + table = GetForeignTable(RelationGetRelid(fmstate->rel)); + server = GetForeignServer(table->serverid); + user = GetUserMapping(userid, server->serverid); /* * Get connection to the foreign server. Connection manager will * establish new connection if necessary. */ - mongoConnection = mongo_get_connection(addressName, portNumber, databaseName, username, password); + mongoConnection = mongo_get_connection(server, user, mongoFdwOptions); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; @@ -479,10 +490,9 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) columnMappingHash = ColumnMappingHash(foreignTableId, columnList); /* create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->databaseName, mongoFdwOptions->collectionName, queryDocument); + mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->svr_database, mongoFdwOptions->collectionName, queryDocument); /* create and set foreign execution state */ - fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); fmstate->columnMappingHash = columnMappingHash; fmstate->mongoConnection = mongoConnection; fmstate->mongoCursor = mongoCursor; @@ -602,7 +612,7 @@ MongoReScanForeignScan(ForeignScanState *scanState) /* reconstruct cursor for collection name and set query */ fmstate->mongoCursor = MongoCursorCreate(mongoConnection, - fmstate->mongoFdwOptions->databaseName, + fmstate->mongoFdwOptions->svr_database, fmstate->mongoFdwOptions->collectionName, fmstate->queryDocument); mongo_free_options(mongoFdwOptions); @@ -750,15 +760,28 @@ MongoExecForeignInsert(EState *estate, Datum value; bool isnull = false; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + userid = GetUserId(); - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + /* Get info about foreign table. */ + table = GetForeignTable(RelationGetRelid(fmstate->rel)); + server = GetForeignServer(table->serverid); + user = GetUserMapping(userid, server->serverid); + + /* + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. + */ + options = fmstate->mongoFdwOptions; + mongoConnection = mongo_get_connection(server, user, options); b = BsonCreate(); @@ -798,7 +821,7 @@ MongoExecForeignInsert(EState *estate, BsonFinish(b); /* Now we are ready to insert tuple / document into MongoDB */ - MongoInsert(mongoConnection, options->databaseName, options->collectionName, b); + MongoInsert(mongoConnection, options->svr_database, options->collectionName, b); BsonDestroy(b); @@ -863,6 +886,11 @@ MongoExecForeignUpdate(EState *estate, BSON *b = NULL; BSON *op = NULL; BSON set; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -871,7 +899,16 @@ MongoExecForeignUpdate(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + /* Get info about foreign table. */ + table = GetForeignTable(foreignTableId); + server = GetForeignServer(table->serverid); + user = GetUserMapping(userid, server->serverid); + + /* + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. + */ + mongoConnection = mongo_get_connection(server, user, options); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -919,7 +956,7 @@ MongoExecForeignUpdate(EState *estate, BsonFinish(op); /* We are ready to update the row into MongoDB */ - MongoUpdate(mongoConnection, options->databaseName, options->collectionName, op, b); + MongoUpdate(mongoConnection, options->svr_database, options->collectionName, op, b); BsonDestroy(op); BsonDestroy(b); @@ -947,6 +984,12 @@ MongoExecForeignDelete(EState *estate, Oid typoid = InvalidOid; BSON *b = NULL; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; + + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); @@ -954,7 +997,16 @@ MongoExecForeignDelete(EState *estate, /* resolve foreign table options; and connect to mongo server */ options = fmstate->mongoFdwOptions; - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + /* Get info about foreign table. */ + table = GetForeignTable(foreignTableId); + server = GetForeignServer(table->serverid); + user = GetUserMapping(userid, server->serverid); + + /* + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. + */ + mongoConnection = mongo_get_connection(server, user, options); /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -972,7 +1024,7 @@ MongoExecForeignDelete(EState *estate, BsonFinish(b); /* Now we are ready to delete a single document from MongoDB */ - MongoDelete(mongoConnection, options->databaseName, options->collectionName, b); + MongoDelete(mongoConnection, options->svr_database, options->collectionName, b); BsonDestroy(b); @@ -1012,13 +1064,28 @@ ForeignTableDocumentCount(Oid foreignTableId) MONGO_CONN *mongoConnection = NULL; const BSON *emptyQuery = NULL; double documentCount = 0.0; + 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); - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); + /* + * 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->databaseName, options->collectionName, emptyQuery); + documentCount = MongoAggregateCount(mongoConnection, options->svr_database, options->collectionName, emptyQuery); mongo_free_options(options); diff --git a/mongo_fdw.h b/mongo_fdw.h index 9a6eb99..9960ec3 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -143,12 +143,12 @@ static const MongoValidOption ValidOptionArray[] = */ typedef struct MongoFdwOptions { - char *addressName; - int32 portNumber; - char *databaseName; + char *svr_address; + int32 svr_port; + char *svr_database; char *collectionName; - char *username; - char *password; + char *svr_username; + char *svr_password; } MongoFdwOptions; @@ -202,7 +202,7 @@ extern void mongo_free_options(MongoFdwOptions *mongoFdwOptions); extern StringInfo mongo_option_names_string(Oid currentContextId); /* connection.c */ -extern MONGO_CONN *mongo_get_connection(char *host, int32 port, char *databaneName, char *user, char *password); +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); diff --git a/mongo_wrapper.c b/mongo_wrapper.c index cbe0b2a..fe05ef6 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -47,11 +47,11 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch { if (mongo_cmd_authenticate(conn, databaseName, user, password) != MONGO_OK) { - int err = conn->err; + char *str = pstrdup(conn->errstr); mongo_destroy(conn); mongo_dealloc(conn); ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), - errhint("Mongo driver connection error: %d", err))); + errhint("Mongo driver connection error: %s", str))); } } return conn; diff --git a/option.c b/option.c index eb8991a..b6519ba 100644 --- a/option.c +++ b/option.c @@ -148,10 +148,10 @@ mongo_get_options(Oid foreignTableId) char *addressName = NULL; char *portName = NULL; int32 portNumber = 0; - char *databaseName = NULL; + char *svr_database = NULL; char *collectionName = NULL; - char *username= NULL; - char *password= NULL; + char *svr_username= NULL; + char *svr_password= NULL; addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); if (addressName == NULL) @@ -163,24 +163,25 @@ mongo_get_options(Oid foreignTableId) else portNumber = pg_atoi(portName, sizeof(int32), 0); - databaseName = mongo_get_option_value(foreignTableId, OPTION_NAME_DATABASE); - if (databaseName == NULL) - databaseName = pstrdup(DEFAULT_DATABASE_NAME); + svr_database = mongo_get_option_value(foreignTableId, OPTION_NAME_DATABASE); + if (svr_database == NULL) + svr_database = pstrdup(DEFAULT_DATABASE_NAME); collectionName = mongo_get_option_value(foreignTableId, OPTION_NAME_COLLECTION); if (collectionName == NULL) collectionName = get_rel_name(foreignTableId); - username = mongo_get_option_value(foreignTableId, OPTION_NAME_USERNAME); - password = mongo_get_option_value(foreignTableId, OPTION_NAME_PASSWORD); + svr_username = mongo_get_option_value(foreignTableId, OPTION_NAME_USERNAME); + svr_password = mongo_get_option_value(foreignTableId, OPTION_NAME_PASSWORD); mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); - mongoFdwOptions->addressName = addressName; - mongoFdwOptions->portNumber = portNumber; - mongoFdwOptions->databaseName = databaseName; + + mongoFdwOptions->svr_address = addressName; + mongoFdwOptions->svr_port = portNumber; + mongoFdwOptions->svr_database = svr_database; mongoFdwOptions->collectionName = collectionName; - mongoFdwOptions->username = username; - mongoFdwOptions->password = password; + mongoFdwOptions->svr_username = svr_username; + mongoFdwOptions->svr_password = svr_password; return mongoFdwOptions; } @@ -191,8 +192,8 @@ mongo_free_options(MongoFdwOptions *mongoFdwOptions) { if (mongoFdwOptions) { - pfree(mongoFdwOptions->addressName); - pfree(mongoFdwOptions->databaseName); + pfree(mongoFdwOptions->svr_address); + pfree(mongoFdwOptions->svr_database); pfree(mongoFdwOptions); } } From 005b342173feb489c36e971d9f1abe2f6f823e6e Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 31 Oct 2014 00:44:00 +0500 Subject: [PATCH 054/239] Fixed some formating issues. --- connection.c | 6 +- mongo_fdw.c | 409 ++++++++++++++++++++++++++------------------------- option.c | 38 ++--- 3 files changed, 227 insertions(+), 226 deletions(-) diff --git a/connection.c b/connection.c index 83560c2..9276420 100644 --- a/connection.c +++ b/connection.c @@ -64,9 +64,9 @@ static HTAB *ConnectionHash = NULL; MONGO_CONN* mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions *opt) { - bool found; - ConnCacheEntry *entry; - ConnCacheKey key; + bool found; + ConnCacheEntry *entry; + ConnCacheKey key; /* First time through, initialize connection cache hashtable */ if (ConnectionHash == NULL) diff --git a/mongo_fdw.c b/mongo_fdw.c index d50c71e..f8c3535 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -71,72 +71,72 @@ /* Local functions forward declarations */ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId, ForeignPath *bestPath, - List *targetList, List *restrictionClauses); + Oid foreignTableId, ForeignPath *bestPath, + List *targetList, List *restrictionClauses); static void MongoExplainForeignScan(ForeignScanState *scanState, - ExplainState *explainState); + ExplainState *explainState); static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); static void MongoReScanForeignScan(ForeignScanState *scanState); static TupleTableSlot *MongoExecForeignUpdate(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static TupleTableSlot *MongoExecForeignDelete(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static void MongoEndForeignModify(EState *estate, - ResultRelInfo *resultRelInfo); + ResultRelInfo *resultRelInfo); static void MongoAddForeignUpdateTargets(Query *parsetree, - RangeTblEntry *target_rte, - Relation target_relation); + RangeTblEntry *target_rte, + Relation target_relation); static void MongoBeginForeignModify(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - List *fdw_private, - int subplan_index, - int eflags); + ResultRelInfo *resultRelInfo, + List *fdw_private, + int subplan_index, + int eflags); static TupleTableSlot *MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static List *MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index); + ModifyTable *plan, + Index resultRelation, + int subplan_index); static void MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, List *fdw_private, - int subplan_index, ExplainState *es); + ResultRelInfo *rinfo, List *fdw_private, + int subplan_index, ExplainState *es); /* local functions */ static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls); static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod); static void MongoFreeScanState(MongoFdwModifyState *fmstate); static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount); + AcquireSampleRowsFunc *acquireSampleRowsFunc, + BlockNumber *totalPageCount); static int MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount); + HeapTuple *sampleRows, int targetRowCount, + double *totalRowCount, double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); @@ -250,22 +250,22 @@ MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableI static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) { - double tupleFilterCost = baserel->baserestrictcost.per_tuple; - double inputRowCount = 0.0; - double documentSelectivity = 0.0; - double foreignTableSize = 0; - int32 documentWidth = 0; - BlockNumber pageCount = 0; - double totalDiskAccessCost = 0.0; - double cpuCostPerDoc = 0.0; - double cpuCostPerRow = 0.0; - double totalCpuCost = 0.0; - double connectionCost = 0.0; - double documentCount = 0.0; - List *opExpressionList = NIL; - Cost startupCost = 0.0; - Cost totalCost = 0.0; - Path *foreignPath = NULL; + double tupleFilterCost = baserel->baserestrictcost.per_tuple; + double inputRowCount = 0.0; + double documentSelectivity = 0.0; + double foreignTableSize = 0; + int32 documentWidth = 0; + BlockNumber pageCount = 0; + double totalDiskAccessCost = 0.0; + double cpuCostPerDoc = 0.0; + double cpuCostPerRow = 0.0; + double totalCpuCost = 0.0; + double connectionCost = 0.0; + double documentCount = 0.0; + List *opExpressionList = NIL; + Cost startupCost = 0.0; + Cost totalCost = 0.0; + Path *foreignPath = NULL; documentCount = ForeignTableDocumentCount(foreignTableId); if (documentCount > 0.0) @@ -330,12 +330,12 @@ static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, ForeignPath *bestPath, List *targetList, List *restrictionClauses) { - Index scanRangeTableIndex = baserel->relid; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - List *opExpressionList = NIL; - BSON *queryDocument = NULL; - List *columnList = NIL; + Index scanRangeTableIndex = baserel->relid; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + List *opExpressionList = NIL; + BSON *queryDocument = NULL; + List *columnList = NIL; /* * We push down applicable restriction clauses to MongoDB, but for simplicity @@ -380,9 +380,9 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, static void MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) { - MongoFdwOptions *mongoFdwOptions = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *mongoFdwOptions = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); mongoFdwOptions = mongo_get_options(foreignTableId); @@ -404,9 +404,9 @@ MongoExplainForeignModify(ModifyTableState *mtstate, int subplan_index, ExplainState *es) { - MongoFdwOptions *mongoFdwOptions = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *mongoFdwOptions = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); mongoFdwOptions = mongo_get_options(foreignTableId); @@ -430,25 +430,24 @@ MongoExplainForeignModify(ModifyTableState *mtstate, static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) { - MONGO_CONN *mongoConnection = NULL; - MONGO_CURSOR *mongoCursor = NULL; - Oid foreignTableId = InvalidOid; - List *columnList = NIL; - HTAB *columnMappingHash = NULL; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - BSON *queryDocument = NULL; - MongoFdwOptions *mongoFdwOptions = NULL; - MongoFdwModifyState *fmstate = NULL; - List *opExpressionList = NIL; - RangeTblEntry *rte; - EState *estate = scanState->ss.ps.state; - ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; - - Oid userid; - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MONGO_CONN *mongoConnection = NULL; + MONGO_CURSOR *mongoCursor = NULL; + Oid foreignTableId = InvalidOid; + List *columnList = NIL; + HTAB *columnMappingHash = NULL; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + BSON *queryDocument = NULL; + MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwModifyState *fmstate = NULL; + List *opExpressionList = NIL; + RangeTblEntry *rte; + EState *estate = scanState->ss.ps.state; + ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* if Explain with no Analyze, do nothing */ @@ -512,14 +511,13 @@ static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - TupleTableSlot *tupleSlot = scanState->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; + TupleTableSlot *tupleSlot = scanState->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; /* * We execute the protocol to load a virtual tuple into a slot. We first @@ -598,10 +596,10 @@ MongoEndForeignScan(ForeignScanState *scanState) static void MongoReScanForeignScan(ForeignScanState *scanState) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *mongoFdwOptions = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + MONGO_CONN *mongoConnection = fmstate->mongoConnection; + MongoFdwOptions *mongoFdwOptions = NULL; + Oid foreignTableId = InvalidOid; /* close down the old cursor */ MongoCursorDestroy(fmstate->mongoCursor); @@ -624,10 +622,10 @@ MongoPlanForeignModify(PlannerInfo *root, Index resultRelation, int subplan_index) { - CmdType operation = plan->operation; - RangeTblEntry *rte = planner_rt_fetch(resultRelation, root); - Relation rel; - List* targetAttrs = NIL; + 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 @@ -696,13 +694,13 @@ MongoBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { - MongoFdwModifyState *fmstate = NULL; - Relation rel = resultRelInfo->ri_RelationDesc; - AttrNumber n_params = 0; - Oid typefnoid = InvalidOid; - bool isvarlena = false; - ListCell *lc = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwModifyState *fmstate = NULL; + Relation rel = resultRelInfo->ri_RelationDesc; + AttrNumber n_params = 0; + Oid typefnoid = InvalidOid; + bool isvarlena = false; + ListCell *lc = NULL; + Oid foreignTableId = InvalidOid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -752,18 +750,17 @@ MongoExecForeignInsert(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Oid foreignTableId = InvalidOid; - BSON *b = NULL; - Oid typoid; - Datum value; - bool isnull = false; - - Oid userid; - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Oid foreignTableId = InvalidOid; + BSON *b = NULL; + Oid typoid; + Datum value; + bool isnull = false; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -839,9 +836,9 @@ MongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) { - Var *var = NULL; - const char *attrname = NULL; - TargetEntry *tle = NULL; + Var *var = NULL; + const char *attrname = NULL; + TargetEntry *tle = NULL; /* * What we need is the rowid which is the first column @@ -876,20 +873,20 @@ MongoExecForeignUpdate(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; - BSON *op = NULL; - BSON set; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; + BSON *op = NULL; + BSON set; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -975,19 +972,18 @@ MongoExecForeignDelete(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; - - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -1060,14 +1056,14 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) static double ForeignTableDocumentCount(Oid foreignTableId) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - const BSON *emptyQuery = NULL; - double documentCount = 0.0; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + const BSON *emptyQuery = NULL; + double documentCount = 0.0; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* Get info about foreign table. */ @@ -1101,9 +1097,9 @@ ForeignTableDocumentCount(Oid foreignTableId) static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList) { - ListCell *columnCell = NULL; - const long hashTableSize = 2048; - HTAB *columnMappingHash = NULL; + ListCell *columnCell = NULL; + const long hashTableSize = 2048; + HTAB *columnMappingHash = NULL; /* create hash table */ HASHCTL hashInfo; @@ -1366,16 +1362,16 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { - Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); - uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; - uint32 arrayGrowthFactor = 2; - uint32 arrayIndex = 0; + Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); + uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; + uint32 arrayGrowthFactor = 2; + uint32 arrayIndex = 0; - ArrayType *columnValueObject = NULL; - Datum columnValueDatum = 0; - bool typeByValue = false; - char typeAlignment = 0; - int16 typeLength = 0; + ArrayType *columnValueObject = NULL; + Datum columnValueDatum = 0; + bool typeByValue = false; + char typeAlignment = 0; + int16 typeLength = 0; BSON_ITERATOR bsonSubIterator = { NULL, 0 }; BsonIterSubIter(bsonIterator, &bsonSubIterator); @@ -1582,12 +1578,15 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) * [1] http://docs.mongodb.org/manual/reference/mongodb-extended-json/ */ void -DumpJson(StringInfo output, const char *bsonData, bool isArray) { - bson_iterator i; - const char *key; - bool isFirstElement; - char beginSymbol, endSymbol; - bson_type t; +DumpJson(StringInfo output, const char *bsonData, bool isArray) +{ + bson_iterator i; + const char *key; + bool isFirstElement; + char beginSymbol, endSymbol; + bson_type t; + + bson_iterator_from_buffer(&i, bsonData); if (isArray) @@ -1712,10 +1711,12 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) { * EscapeJsonString escapes a string for safe inclusion in JSON. */ const char * -EscapeJsonString(const char *string) { - StringInfo buffer; - const char *ptr; - int i, segmentStartIdx, len; +EscapeJsonString(const char *string) +{ + StringInfo buffer; + const char *ptr; + int i, segmentStartIdx, len; + bool needsEscaping = false; for (ptr = string; *ptr; ++ptr) { if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ @@ -1790,13 +1791,13 @@ MongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *acquireSampleRowsFunc, BlockNumber *totalPageCount) { - BlockNumber pageCount = 0; - int attributeCount = 0; - int32 *attributeWidths = NULL; - Oid foreignTableId = InvalidOid; - int32 documentWidth = 0; - double documentCount = 0.0; - double foreignTableSize = 0; + BlockNumber pageCount = 0; + int attributeCount = 0; + int32 *attributeWidths = NULL; + Oid foreignTableId = InvalidOid; + int32 documentWidth = 0; + double documentCount = 0.0; + double foreignTableSize = 0; foreignTableId = RelationGetRelid(relation); @@ -1850,29 +1851,29 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, double *totalRowCount, double *totalDeadRowCount) { - int sampleRowCount = 0; - double rowCount = 0; - double rowCountToSkip = -1; /* -1 means not set yet */ - double randomState = 0; - Datum *columnValues = NULL; - bool *columnNulls = NULL; - Oid foreignTableId = InvalidOid; - TupleDesc tupleDescriptor = NULL; - Form_pg_attribute *attributesPtr = NULL; - AttrNumber columnCount = 0; - AttrNumber columnId = 0; - HTAB *columnMappingHash = NULL; - MONGO_CURSOR *mongoCursor = NULL; - BSON *queryDocument = NULL; - List *columnList = NIL; - ForeignScanState *scanState = NULL; - List *foreignPrivateList = NIL; - ForeignScan *foreignScan = NULL; - MongoFdwModifyState *fmstate = NULL; - char *relationName = NULL; - int executorFlags = 0; - MemoryContext oldContext = CurrentMemoryContext; - MemoryContext tupleContext = NULL; + int sampleRowCount = 0; + double rowCount = 0; + double rowCountToSkip = -1; /* -1 means not set yet */ + double randomState = 0; + Datum *columnValues = NULL; + bool *columnNulls = NULL; + Oid foreignTableId = InvalidOid; + TupleDesc tupleDescriptor = NULL; + Form_pg_attribute *attributesPtr = NULL; + AttrNumber columnCount = 0; + AttrNumber columnId = 0; + HTAB *columnMappingHash = NULL; + MONGO_CURSOR *mongoCursor = NULL; + BSON *queryDocument = NULL; + List *columnList = NIL; + ForeignScanState *scanState = NULL; + List *foreignPrivateList = NIL; + ForeignScan *foreignScan = NULL; + MongoFdwModifyState *fmstate = NULL; + char *relationName = NULL; + int executorFlags = 0; + MemoryContext oldContext = CurrentMemoryContext; + MemoryContext tupleContext = NULL; /* create list of columns in the relation */ tupleDescriptor = RelationGetDescr(relation); diff --git a/option.c b/option.c index b6519ba..c78e1e2 100644 --- a/option.c +++ b/option.c @@ -61,9 +61,9 @@ PG_FUNCTION_INFO_V1(mongo_fdw_validator); Datum mongo_fdw_validator(PG_FUNCTION_ARGS) { - Datum optionArray = PG_GETARG_DATUM(0); - Oid optionContextId = PG_GETARG_OID(1); - List *optionList = untransformRelOptions(optionArray); + Datum optionArray = PG_GETARG_DATUM(0); + Oid optionContextId = PG_GETARG_OID(1); + List *optionList = untransformRelOptions(optionArray); ListCell *optionCell = NULL; foreach(optionCell, optionList) @@ -114,8 +114,8 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) StringInfo mongo_option_names_string(Oid currentContextId) { - StringInfo optionNamesString = makeStringInfo(); - bool firstOptionPrinted = false; + StringInfo optionNamesString = makeStringInfo(); + bool firstOptionPrinted = false; int32 optionIndex = 0; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) @@ -144,14 +144,14 @@ mongo_option_names_string(Oid currentContextId) MongoFdwOptions * mongo_get_options(Oid foreignTableId) { - MongoFdwOptions *mongoFdwOptions = NULL; - char *addressName = NULL; - char *portName = NULL; - int32 portNumber = 0; - char *svr_database = NULL; - char *collectionName = NULL; - char *svr_username= NULL; - char *svr_password= NULL; + MongoFdwOptions *mongoFdwOptions = NULL; + char *addressName = NULL; + char *portName = NULL; + int32 portNumber = 0; + char *svr_database = NULL; + char *collectionName = NULL; + char *svr_username= NULL; + char *svr_password= NULL; addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); if (addressName == NULL) @@ -206,12 +206,12 @@ mongo_free_options(MongoFdwOptions *mongoFdwOptions) static char * mongo_get_option_value(Oid foreignTableId, const char *optionName) { - ForeignTable *foreignTable = NULL; - ForeignServer *foreignServer = NULL; - List *optionList = NIL; - ListCell *optionCell = NULL; - UserMapping *mapping= NULL; - char *optionValue = NULL; + ForeignTable *foreignTable = NULL; + ForeignServer *foreignServer = NULL; + List *optionList = NIL; + ListCell *optionCell = NULL; + UserMapping *mapping= NULL; + char *optionValue = NULL; foreignTable = GetForeignTable(foreignTableId); foreignServer = GetForeignServer(foreignTable->serverid); From 2ad3238288563364a2be362dc9445abc0eeaa8d5 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 31 Oct 2014 00:56:58 +0500 Subject: [PATCH 055/239] Change the Option's variable name from mongoFdwOptions to options --- mongo_fdw.c | 60 ++++++++++++++++++++++++++--------------------------- mongo_fdw.h | 16 +++++++------- option.c | 28 ++++++++++++------------- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index f8c3535..6c880c5 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -380,19 +380,19 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, static void MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) { - MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwOptions *options = NULL; StringInfo namespaceName = NULL; Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); + options = mongo_get_options(foreignTableId); /* construct fully qualified collection name */ namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->svr_database, - mongoFdwOptions->collectionName); + appendStringInfo(namespaceName, "%s.%s", options->svr_database, + options->collectionName); - mongo_free_options(mongoFdwOptions); + mongo_free_options(options); ExplainPropertyText("Foreign Namespace", namespaceName->data, explainState); } @@ -404,19 +404,19 @@ MongoExplainForeignModify(ModifyTableState *mtstate, int subplan_index, ExplainState *es) { - MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwOptions *options = NULL; StringInfo namespaceName = NULL; Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); - mongoFdwOptions = mongo_get_options(foreignTableId); + options = mongo_get_options(foreignTableId); /* construct fully qualified collection name */ namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", mongoFdwOptions->svr_database, - mongoFdwOptions->collectionName); + appendStringInfo(namespaceName, "%s.%s", options->svr_database, + options->collectionName); - mongo_free_options(mongoFdwOptions); + mongo_free_options(options); ExplainPropertyText("Foreign Namespace", namespaceName->data, es); } @@ -438,7 +438,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) ForeignScan *foreignScan = NULL; List *foreignPrivateList = NIL; BSON *queryDocument = NULL; - MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwOptions *options = NULL; MongoFdwModifyState *fmstate = NULL; List *opExpressionList = NIL; RangeTblEntry *rte; @@ -455,7 +455,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) return; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); + options = mongo_get_options(foreignTableId); fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); /* @@ -475,7 +475,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) * Get connection to the foreign server. Connection manager will * establish new connection if necessary. */ - mongoConnection = mongo_get_connection(server, user, mongoFdwOptions); + mongoConnection = mongo_get_connection(server, user, options); foreignScan = (ForeignScan *) scanState->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; @@ -489,14 +489,14 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) columnMappingHash = ColumnMappingHash(foreignTableId, columnList); /* create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, mongoFdwOptions->svr_database, mongoFdwOptions->collectionName, queryDocument); + mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); /* create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; fmstate->mongoConnection = mongoConnection; fmstate->mongoCursor = mongoCursor; fmstate->queryDocument = queryDocument; - fmstate->mongoFdwOptions = mongoFdwOptions; + fmstate->options = options; scanState->fdw_state = (void *) fmstate; } @@ -578,10 +578,10 @@ MongoEndForeignScan(ForeignScanState *scanState) /* if we executed a query, reclaim mongo related resources */ if (fmstate != NULL) { - if (fmstate->mongoFdwOptions) + if (fmstate->options) { - mongo_free_options(fmstate->mongoFdwOptions); - fmstate->mongoFdwOptions = NULL; + mongo_free_options(fmstate->options); + fmstate->options = NULL; } MongoFreeScanState(fmstate); } @@ -598,7 +598,7 @@ MongoReScanForeignScan(ForeignScanState *scanState) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwOptions *options = NULL; Oid foreignTableId = InvalidOid; /* close down the old cursor */ @@ -606,14 +606,14 @@ MongoReScanForeignScan(ForeignScanState *scanState) /* reconstruct full collection name */ foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); - mongoFdwOptions = mongo_get_options(foreignTableId); + options = mongo_get_options(foreignTableId); /* reconstruct cursor for collection name and set query */ fmstate->mongoCursor = MongoCursorCreate(mongoConnection, - fmstate->mongoFdwOptions->svr_database, - fmstate->mongoFdwOptions->collectionName, + fmstate->options->svr_database, + fmstate->options->collectionName, fmstate->queryDocument); - mongo_free_options(mongoFdwOptions); + mongo_free_options(options); } static List * @@ -715,7 +715,7 @@ MongoBeginForeignModify(ModifyTableState *mtstate, fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); fmstate->rel = rel; - fmstate->mongoFdwOptions = mongo_get_options(foreignTableId); + fmstate->options = mongo_get_options(foreignTableId); fmstate->target_attrs = (List *) list_nth(fdw_private, 0); @@ -777,7 +777,7 @@ MongoExecForeignInsert(EState *estate, * Get connection to the foreign server. Connection manager will * establish new connection if necessary. */ - options = fmstate->mongoFdwOptions; + options = fmstate->options; mongoConnection = mongo_get_connection(server, user, options); b = BsonCreate(); @@ -894,7 +894,7 @@ MongoExecForeignUpdate(EState *estate, foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + options = fmstate->options; /* Get info about foreign table. */ table = GetForeignTable(foreignTableId); @@ -991,7 +991,7 @@ MongoExecForeignDelete(EState *estate, foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); /* resolve foreign table options; and connect to mongo server */ - options = fmstate->mongoFdwOptions; + options = fmstate->options; /* Get info about foreign table. */ table = GetForeignTable(foreignTableId); @@ -1038,10 +1038,10 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; if (fmstate) { - if (fmstate->mongoFdwOptions) + if (fmstate->options) { - mongo_free_options(fmstate->mongoFdwOptions); - fmstate->mongoFdwOptions = NULL; + mongo_free_options(fmstate->options); + fmstate->options = NULL; } MongoFreeScanState(fmstate); pfree(fmstate); diff --git a/mongo_fdw.h b/mongo_fdw.h index 9960ec3..b2874cd 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -161,20 +161,20 @@ typedef struct MongoFdwOptions */ typedef struct MongoFdwModifyState { - Relation rel; /* relcache entry for the foreign table */ - List *target_attrs; /* list of target attribute numbers */ + 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 */ + 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_CONN *mongoConnection; /* MongoDB connection */ MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ - BSON *queryDocument; /* Bson Document */ + BSON *queryDocument; /* Bson Document */ - MongoFdwOptions *mongoFdwOptions; + MongoFdwOptions *options; /* working memory context */ MemoryContext temp_cxt; /* context for per-tuple temporary data */ @@ -198,7 +198,7 @@ typedef struct ColumnMapping /* options.c */ extern MongoFdwOptions * mongo_get_options(Oid foreignTableId); -extern void mongo_free_options(MongoFdwOptions *mongoFdwOptions); +extern void mongo_free_options(MongoFdwOptions *options); extern StringInfo mongo_option_names_string(Oid currentContextId); /* connection.c */ diff --git a/option.c b/option.c index c78e1e2..51230bb 100644 --- a/option.c +++ b/option.c @@ -144,7 +144,7 @@ mongo_option_names_string(Oid currentContextId) MongoFdwOptions * mongo_get_options(Oid foreignTableId) { - MongoFdwOptions *mongoFdwOptions = NULL; + MongoFdwOptions *options = NULL; char *addressName = NULL; char *portName = NULL; int32 portNumber = 0; @@ -174,27 +174,27 @@ mongo_get_options(Oid foreignTableId) svr_username = mongo_get_option_value(foreignTableId, OPTION_NAME_USERNAME); svr_password = mongo_get_option_value(foreignTableId, OPTION_NAME_PASSWORD); - mongoFdwOptions = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); + options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); - mongoFdwOptions->svr_address = addressName; - mongoFdwOptions->svr_port = portNumber; - mongoFdwOptions->svr_database = svr_database; - mongoFdwOptions->collectionName = collectionName; - mongoFdwOptions->svr_username = svr_username; - mongoFdwOptions->svr_password = svr_password; + options->svr_address = addressName; + options->svr_port = portNumber; + options->svr_database = svr_database; + options->collectionName = collectionName; + options->svr_username = svr_username; + options->svr_password = svr_password; - return mongoFdwOptions; + return options; } void -mongo_free_options(MongoFdwOptions *mongoFdwOptions) +mongo_free_options(MongoFdwOptions *options) { - if (mongoFdwOptions) + if (options) { - pfree(mongoFdwOptions->svr_address); - pfree(mongoFdwOptions->svr_database); - pfree(mongoFdwOptions); + pfree(options->svr_address); + pfree(options->svr_database); + pfree(options); } } From 463046f1cea9e5ddd366775ff11c35319910da2e Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Mon, 3 Nov 2014 13:41:12 -0800 Subject: [PATCH 056/239] fixing json dumps logic for meta driver --- mongo_fdw.c | 59 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index b5ade30..128c232 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1508,7 +1508,18 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) } is_array = (BSON_TYPE_ARRAY == type); +#ifdef META_DRIVER + if (is_array) + { + DumpJsonArray(buffer, bsonIterator); + } + else + { + DumpJsonObject(buffer, bsonIterator); + } +#else DumpJson(buffer, bsonIterator, is_array); +#endif result = cstring_to_text_with_len(buffer->data, buffer->len); @@ -1540,25 +1551,43 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) */ #ifdef META_DRIVER void -DumpJson(StringInfo output, BSON_ITERATOR *iter, bool isArray) { - char *str; - const uint8_t *buf; - uint32_t buf_len; - const BSON *bson; - if (isArray) +DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) { + char *json; + uint32_t len; + const uint8_t *data; + BSON bson; + + bson_iter_document(iter, &len, &data); + + if (bson_init_static(&bson, data, len)) { - bson_iter_document (&iter, &buf_len, &buf); - bson_init_static (&bson, buf, buf_len); - str = bson_array_as_json(bson, NULL); + if((json = bson_as_json (bson, NULL))) + { + appendStringInfoString(output, json); + bson_free(json); + } + bson_destroy(bson); } - else +} + +void +DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { + char *json; + uint32_t len; + const uint8_t *data; + BSON bson; + + bson_iter_array(iter, &len, &data); + + if (bson_init_static(&bson, data, len)) { - bson_iter_array (&iter, &buf_len, &buf); - bson_init_static (&bson, buf, buf_len); - str = bson_as_json(bson, NULL); + if((json = bson_array_as_json (bson, NULL))) + { + appendStringInfoString(output, json); + bson_free(json); + } + bson_destroy(bson); } - appendStringInfoChar(output, *str); - bson_free (str); } #else void From 3a1a95205fecd99cd4b364486b68f972d77619b1 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 10:50:08 -0800 Subject: [PATCH 057/239] fixing segfault for json data types --- mongo_fdw.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 128c232..f1dd894 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1464,7 +1464,14 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) int value_len; const char *value; #ifdef META_DRIVER - value = BsonIterBinData(bsonIterator, &value_len); + switch (BsonIterType(bsonIterator)) + { + case BSON_TYPE_OID: + value = + break; + default: + value = BsonIterBinData(bsonIterator, &value_len); + } #else value_len = BsonIterBinLen(bsonIterator); value = BsonIterBinData(bsonIterator); @@ -1556,17 +1563,16 @@ 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)) { - if((json = bson_as_json (bson, NULL))) + if((json = bson_as_json (&bson, NULL))) { appendStringInfoString(output, json); bson_free(json); } - bson_destroy(bson); } } @@ -1581,12 +1587,11 @@ DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { if (bson_init_static(&bson, data, len)) { - if((json = bson_array_as_json (bson, NULL))) + if((json = bson_array_as_json (&bson, NULL))) { appendStringInfoString(output, json); bson_free(json); } - bson_destroy(bson); } } #else From a377975ec35381cda8f25e83ac5d4727e5923922 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 11:51:09 -0800 Subject: [PATCH 058/239] adding ability to represent _id as bytea and also query by bytea --- mongo_fdw.c | 12 +++++++++--- mongo_query.c | 13 +++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index f1dd894..0f58113 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1115,7 +1115,6 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, { const char *bsonKey = BsonIterKey(&bsonIterator); BSON_TYPE bsonType = BsonIterType(&bsonIterator); - ColumnMapping *columnMapping = NULL; Oid columnTypeId = InvalidOid; Oid columnArrayTypeId = InvalidOid; @@ -1252,6 +1251,12 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) { compatibleTypes = true; } +#ifdef META_DRIVER + if (bsonType == BSON_TYPE_OID) + { + compatibleTypes = true; + } +#endif break; } case NAMEOID: @@ -1371,7 +1376,6 @@ static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { Datum columnValue = 0; - switch(columnTypeId) { case INT2OID: @@ -1467,10 +1471,12 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) switch (BsonIterType(bsonIterator)) { case BSON_TYPE_OID: - value = + value = BsonIterOid(bsonIterator); + value_len = 12; break; default: value = BsonIterBinData(bsonIterator, &value_len); + break; } #else value_len = BsonIterBinLen(bsonIterator); diff --git a/mongo_query.c b/mongo_query.c index 8358903..e335259 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -466,7 +466,20 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu len = VARSIZE_4B(result) - VARHDRSZ; data = VARDATA_4B(result); } +#ifdef META_DRIVER + if (strcmp(keyName, "_id") == 0) + { + bson_oid_t oid; + bson_oid_init_from_data(&oid, data); + status = BsonAppendOid(queryDocument, keyName, &oid); + } + else + { + status = BsonAppendBinary(queryDocument, keyName, data, len); + } +#else status = BsonAppendBinary(queryDocument, keyName, data, len); +#endif break; } case NAMEOID: From e7f4d81f6992fcf1ac98d32001fa4f5e29e812d3 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 13:50:27 -0800 Subject: [PATCH 059/239] fixing whitespace --- mongo_fdw.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mongo_fdw.h b/mongo_fdw.h index b7dec43..416470a 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_fdw.h - * Foreign-data wrapper for remote MongoDB servers + * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * @@ -10,7 +10,7 @@ * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION - * mongo_fdw.h + * mongo_fdw.h * *------------------------------------------------------------------------- */ @@ -234,23 +234,23 @@ typedef struct MongoFdwOptions */ typedef struct MongoFdwModifyState { - Relation rel; /* relcache entry for the foreign table */ - List *target_attrs; /* list of target attribute numbers */ + 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 */ + int p_nums; /* number of parameters to transmit */ + FmgrInfo *p_flinfo; /* output conversion functions for them */ - struct HTAB *columnMappingHash; + struct HTAB *columnMappingHash; MONGO_CONN *mongoConnection; /* MongoDB connection */ - MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ + MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ BSON *queryDocument; /* Bson Document */ - MongoFdwOptions *options; + MongoFdwOptions *options; /* working memory context */ - MemoryContext temp_cxt; /* context for per-tuple temporary data */ + MemoryContext temp_cxt; /* context for per-tuple temporary data */ } MongoFdwModifyState; From d8bdbcc0121b84c62f5e986ac2279e940f9f17b8 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 14:54:37 -0800 Subject: [PATCH 060/239] fixing some compile errors --- mongo_fdw.c | 51 ++++++++++++++++++++++++++++----------------------- mongo_fdw.h | 6 +----- option.c | 2 +- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index dd1cf1a..a4d7163 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -142,7 +142,12 @@ static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); const char * EscapeJsonString(const char *string); +#ifdef META_DRIVER +void DumpJsonObject(StringInfo buffer, BSON_ITERATOR *iter); +void DumpJsonArray(StringInfo buffer, BSON_ITERATOR *iter); +#else void DumpJson(StringInfo buffer, const char *bsonData, bool isArray); +#endif /* the null action object used for pure validation */ static JsonSemAction nullSemAction = @@ -1340,7 +1345,7 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) } case JSONOID: { - if (bsonType == BSON_OBJECT || bsonType == BSON_ARRAY) + if (bsonType == BSON_TYPE_OBJECT || bsonType == BSON_TYPE_ARRAY) { compatibleTypes = true; } @@ -1565,12 +1570,12 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) bool is_array; StringInfo buffer = makeStringInfo(); - bson_type type = bson_iterator_type(bsonIterator); - if (type != BSON_ARRAY && type != BSON_OBJECT) + BSON_TYPE type = BSON_ITER_TYPE(bsonIterator); + if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT) { ereport(ERROR, (errmsg("cannot convert scolar to json"))); } - is_array = (BSON_ARRAY == type); + is_array = (BSON_TYPE_ARRAY == type); #ifdef META_DRIVER if (is_array) @@ -1652,7 +1657,7 @@ DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { } } } -#endif +#else void DumpJson(StringInfo output, const char *bsonData, bool isArray) { @@ -1697,80 +1702,80 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) switch (t) { - case BSON_DOUBLE: + case BSON_TYPE_DOUBLE: appendStringInfo(output, "%f", bson_iterator_double(&i)); break; - case BSON_STRING: + case BSON_TYPE_STRING: appendStringInfo(output, "\"%s\"", EscapeJsonString(bson_iterator_string(&i))); break; - case BSON_SYMBOL: + case BSON_TYPE_SYMBOL: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("`symbol` BSON type is deprecated and " "unsupported"), errhint("Symbol: %s", bson_iterator_string(&i)))); break; - case BSON_OID: + case BSON_TYPE_OID: { char oidhex[25]; bson_oid_to_string(bson_iterator_oid(&i), oidhex); appendStringInfo(output, "\"%s\"", oidhex); break; } - case BSON_BOOL: + case BSON_TYPE_BOOL: appendStringInfoString( output, bson_iterator_bool(&i) ? "true" : "false"); break; - case BSON_DATE: + case BSON_TYPE_DATE: appendStringInfo(output, "{\"$date\":%ld}", (long int)bson_iterator_date(&i)); break; - case BSON_BINDATA: + case BSON_TYPE_BINDATA: /* It's possible to encode the data with base64 here. */ ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `binary data` BSON type " "is not implemented"))); break; - case BSON_UNDEFINED: + case BSON_TYPE_UNDEFINED: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("`undefined` BSON type is deprecated " "and unsupported"))); break; - case BSON_NULL: + case BSON_TYPE_NULL: appendStringInfoString(output, "null"); break; - case BSON_REGEX: + case BSON_TYPE_REGEX: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `regex` BSON type is " "not implemented"), errhint("Regex: %s", bson_iterator_regex(&i)))); break; - case BSON_CODE: + case BSON_TYPE_CODE: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `code` BSON type is " "not implemented"), errhint("Code: %s", bson_iterator_code(&i)))); break; - case BSON_CODEWSCOPE: + case BSON_TYPE_CODEWSCOPE: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `code with scope` BSON " "type is not implemented"))); break; - case BSON_INT: + case BSON_TYPE_INT: appendStringInfo(output, "%d", bson_iterator_int(&i)); break; - case BSON_LONG: + case BSON_TYPE_LONG: appendStringInfo(output, "%lld", (uint64_t)bson_iterator_long(&i)); break; - case BSON_TIMESTAMP: + case BSON_TYPE_TIMESTAMP: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("internal `timestamp` BSON type is " "and unsupported"))); break; - case BSON_OBJECT: + case BSON_TYPE_DOCUMENT: DumpJson(output, bson_iterator_value(&i), false); break; - case BSON_ARRAY: + case BSON_TYPE_ARRAY: DumpJson(output, bson_iterator_value(&i), true); break; default: @@ -1781,7 +1786,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) } appendStringInfoChar(output, endSymbol); } - +#endif /* * EscapeJsonString escapes a string for safe inclusion in JSON. diff --git a/mongo_fdw.h b/mongo_fdw.h index 416470a..671ceba 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -275,11 +275,7 @@ extern void mongo_free_options(MongoFdwOptions *options); extern StringInfo mongo_option_names_string(Oid currentContextId); /* connection.c */ -#ifdef META_DRIVER -MONGO_CONN * mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions *opt, char *readPreference); -#else -MONGO_CONN * mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions *opt); -#endif +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); diff --git a/option.c b/option.c index 48f9682..04ebbc1 100644 --- a/option.c +++ b/option.c @@ -189,7 +189,7 @@ mongo_get_options(Oid foreignTableId) options->svr_password = svr_password; #ifdef META_DRIVER - mongoFdwOptions->readPreference = readPreference; + options->readPreference = readPreference; #endif return options; From 32f9e6ff9aa67cb844b7f2c9871f97431f95944d Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 15:01:13 -0800 Subject: [PATCH 061/239] fixing compile issues --- mongo_fdw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index a4d7163..6a32a5b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1079,11 +1079,6 @@ ForeignTableDocumentCount(Oid foreignTableId) /* resolve foreign table options; and connect to mongo server */ options = mongo_get_options(foreignTableId); -#ifdef META_DRIVER - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password, options->readPreference); -#else - mongoConnection = mongo_get_connection(options->addressName, options->portNumber, options->databaseName, options->username, options->password); -#endif /* * Get connection to the foreign server. Connection manager will * establish new connection if necessary. @@ -1345,7 +1340,7 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) } case JSONOID: { - if (bsonType == BSON_TYPE_OBJECT || bsonType == BSON_TYPE_ARRAY) + if (bsonType == BSON_TYPE_DOCUMENT || bsonType == BSON_TYPE_ARRAY) { compatibleTypes = true; } From c04e60f447981dc7ef76f8b722b2cc85ee4a92b5 Mon Sep 17 00:00:00 2001 From: Matthew Burghoffer Date: Tue, 4 Nov 2014 15:04:32 -0800 Subject: [PATCH 062/239] fixing compile issues for legacy drivers --- mongo_fdw.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 6a32a5b..30dc78c 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1700,7 +1700,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) case BSON_TYPE_DOUBLE: appendStringInfo(output, "%f", bson_iterator_double(&i)); break; - case BSON_TYPE_STRING: + case BSON_TYPE_UTF8: appendStringInfo(output, "\"%s\"", EscapeJsonString(bson_iterator_string(&i))); break; @@ -1721,7 +1721,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) appendStringInfoString( output, bson_iterator_bool(&i) ? "true" : "false"); break; - case BSON_TYPE_DATE: + case BSON_TYPE_DATE_TIME: appendStringInfo(output, "{\"$date\":%ld}", (long int)bson_iterator_date(&i)); break; @@ -1756,10 +1756,10 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) errmsg("support for `code with scope` BSON " "type is not implemented"))); break; - case BSON_TYPE_INT: + case BSON_TYPE_INT32: appendStringInfo(output, "%d", bson_iterator_int(&i)); break; - case BSON_TYPE_LONG: + case BSON_TYPE_INT64: appendStringInfo(output, "%lld", (uint64_t)bson_iterator_long(&i)); break; case BSON_TYPE_TIMESTAMP: From ce020678cf19aff28187d2cfb8d5255a752dd86b Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 5 Nov 2014 14:09:36 +0500 Subject: [PATCH 063/239] Fix compilation warning cause by "Merge pull request #12 from mjburghoffer/master" --- mongo_fdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 30dc78c..99737fe 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1518,6 +1518,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { int value_len; const char *value; + bytea *result; #ifdef META_DRIVER switch (BsonIterType(bsonIterator)) { @@ -1533,7 +1534,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) value_len = BsonIterBinLen(bsonIterator); value = BsonIterBinData(bsonIterator); #endif - bytea *result = (bytea *)palloc(value_len + VARHDRSZ); + result = (bytea *)palloc(value_len + VARHDRSZ); memcpy(VARDATA(result), value, value_len); SET_VARSIZE(result, value_len + VARHDRSZ); columnValue = PointerGetDatum(result); From d4b3cae38fd9a2bae632d91e633b7dadfb5fe1e0 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 6 Nov 2014 01:34:55 +0500 Subject: [PATCH 064/239] Fixing compiler warning while compiling using Mongo C Meta Driver. --- config.h | 22 ---------------------- mongo-c-driver | 2 +- mongo_fdw.c | 8 ++++---- mongo_query.c | 2 +- mongo_wrapper_meta.c | 14 +++++++------- 5 files changed, 13 insertions(+), 35 deletions(-) delete mode 100644 config.h diff --git a/config.h b/config.h deleted file mode 100644 index ee6ce48..0000000 --- a/config.h +++ /dev/null @@ -1,22 +0,0 @@ -/*------------------------------------------------------------------------- - * - * config.h - * Foreign-data wrapper for remote MongoDB servers - * - * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * - * Portions Copyright (c) 2012–2014 Citus Data, Inc. - * - * IDENTIFICATION - * config.h - * - *------------------------------------------------------------------------- - */ - -/* - * Define if you want to compile the MongoFDW with Meta C Driver, otherwise - * it will compile using MongoDB legacy C Driver -*/ -/* #define META_DRIVER */ diff --git a/mongo-c-driver b/mongo-c-driver index 8f27c0f..02aaad0 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 8f27c0fbc51ed3ab7920734b87e611bae8e13eaf +Subproject commit 02aaad0a705c9c53314d3579ca03c52bbab1fd67 diff --git a/mongo_fdw.c b/mongo_fdw.c index 99737fe..f64241f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1517,22 +1517,22 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) case BYTEAOID: { int value_len; - const char *value; + char *value; bytea *result; #ifdef META_DRIVER switch (BsonIterType(bsonIterator)) { case BSON_TYPE_OID: - value = BsonIterOid(bsonIterator); + value = (char*) BsonIterOid(bsonIterator); value_len = 12; break; default: - value = BsonIterBinData(bsonIterator, &value_len); + value = (char*)BsonIterBinData(bsonIterator, (uint32_t *)&value_len); break; } #else value_len = BsonIterBinLen(bsonIterator); - value = BsonIterBinData(bsonIterator); + value = (char*)BsonIterBinData(bsonIterator); #endif result = (bytea *)palloc(value_len + VARHDRSZ); memcpy(VARDATA(result), value, value_len); diff --git a/mongo_query.c b/mongo_query.c index d8a0876..945394b 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -470,7 +470,7 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu if (strcmp(keyName, "_id") == 0) { bson_oid_t oid; - bson_oid_init_from_data(&oid, data); + bson_oid_init_from_data(&oid, (const uint8_t *)data); status = BsonAppendOid(queryDocument, keyName, &oid); } else diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 1234354..7bb7e29 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -114,7 +114,7 @@ MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) c = mongoc_client_get_collection (conn, database, collection); - r = mongoc_collection_delete(c, MONGOC_DELETE_SINGLE_REMOVE, b, NULL, &error); + 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"), @@ -256,7 +256,7 @@ 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); + bson_iter_binary (it, &subtype, len, &binary); return (char*)binary; } @@ -369,7 +369,7 @@ BsonAppendUTF8(BSON *b, const char* key, char *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, v, len); + return bson_append_binary(b, key, (int)strlen(key), BSON_SUBTYPE_BINARY, (const uint8_t *)v, len); } bool @@ -414,9 +414,9 @@ BsonFinish(BSON* b) double MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) { - const BSON *command; + BSON *command; BSON *reply; - const BSON *doc; + BSON *doc; double count; mongoc_cursor_t *cursor; bool ret; @@ -431,11 +431,11 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, 0, command, NULL, NULL); - ret = mongoc_cursor_next(cursor, &doc); + ret = mongoc_cursor_next(cursor, (const BSON**)&doc); if (ret) { - bson_copy_to(doc, reply); bson_iter_t it; + bson_copy_to(doc, reply); if (bson_iter_init_find(&it, reply, "n")) count = BsonIterDouble(&it); } else { From 9fda4cd3703b1228a7bd449d8f81e17dc420b685 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 6 Nov 2014 01:35:32 +0500 Subject: [PATCH 065/239] Adding script to automate the process of compiling different drivers of MongoDB. --- autogen.sh | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100755 autogen.sh diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..2aaed4a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,91 @@ +#! /bin/bash + +#------------------------------------------------------------------------- +# +# autogen.sh +# Foreign-data wrapper for remote MongoDB servers +# +# Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group +# +# Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. +# +# IDENTIFICATION +# autogen.sh +# +#------------------------------------------------------------------------- + +if [ "$#" -ne 1 ]; then + echo "Usage: autogen.sh --[with-legacy | with-master]" + exit +fi + +### +# Pull the latest version of Monggo C Driver's master branch +# +function checkout_mongo_driver +{ + rm -rf mongo-c-driver + git clone https://github.com/mongodb/mongo-c-driver mongo-c-driver + git checkout master +} + +### +# Pull the legacy branch from the Mongo C Driver's +# +function checkout_legacy_branch +{ + cd mongo-c-driver + git checkout legacy + cd .. +} + +### +# Configure and install the Mongo C Driver and libbson +# +function install_mongoc_driver +{ + cd mongo-c-driver + ./autogen.sh + configure --with-libbson=system + make install + cd .. +} + +### +# Cleanup the system +# +function cleanup +{ + rm config.h + touch config.h +} + +### +# Create a config file and append #define META_DRIVER which will be +# used in case of Meta Driver (master branch) option. +# +function create_config +{ + echo "#ifdef __CONFIG__" >> config.h + echo "#define META_DRIVER" >> config.h + echo "#endif" >> config.h +} + +cleanup + +if [ "--with-legacy" = $1 ]; then + checkout_mongo_driver + checkout_legacy_branch + cp Makefile.legacy Makefile + echo "Done" +elif [ "--with-master" == $1 ]; then + checkout_mongo_driver + install_mongoc_driver + create_config + export PKG_CONFIG_PATH=mongo-c-driver/src/:mongo-c-driver/src/libbson/src + cp Makefile.meta Makefile + echo "Done" +else + echo "Usage: autogen.sh --[with-legacy | with-master]" +fi + From 502efca7b8093a4f0159cc86d322e4c8a0ffdf76 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 6 Nov 2014 23:59:10 +0500 Subject: [PATCH 066/239] Added missing file "Makefile.legacy" --- Makefile.legacy | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Makefile.legacy diff --git a/Makefile.legacy b/Makefile.legacy new file mode 100644 index 0000000..a65c28e --- /dev/null +++ b/Makefile.legacy @@ -0,0 +1,49 @@ +# mongo_fdw/Makefile +# +# Portions Copyright © 2004-2014, 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. +# + +MONGO_DRIVER = mongo-c-driver +MONGO_PATH = $(MONGO_DRIVER)/src +MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ + $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os + +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) + +EXTENSION = mongo_fdw +DATA = mongo_fdw--1.0.sql + +REGRESS = mongo_fdw +REGRESS_OPTS = --inputdir=test --outputdir=test \ + --load-extension=$(EXTENSION) + +$(MONGO_DRIVER)/%.os: + $(MAKE) -C $(MONGO_DRIVER) $*.os + +# +# 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), 9.3 9.4 9.5)) + $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) +endif From cc070a8d29d17e7307509a5f587450ac47f03fcc Mon Sep 17 00:00:00 2001 From: TruongSinh Tran-Nguyen Date: Fri, 7 Nov 2014 10:20:15 +0200 Subject: [PATCH 067/239] write JSON (object and array) --- .gitmodules | 3 +++ Makefile | 12 ++++++--- mongo_query.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8da9629..53af7f8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [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 diff --git a/Makefile b/Makefile index a65c28e..bb9d3d0 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,16 @@ MODULE_big = mongo_fdw # on another platform, change env_posix.os in MONGO_OBJS with the appropriate # environment object file. # - MONGO_DRIVER = mongo-c-driver MONGO_PATH = $(MONGO_DRIVER)/src MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os - -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) +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 +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql @@ -30,6 +32,8 @@ REGRESS_OPTS = --inputdir=test --outputdir=test \ $(MONGO_DRIVER)/%.os: $(MAKE) -C $(MONGO_DRIVER) $*.os +#$(LIBJSON)/json.o: +# $(MAKE) -C $(LIBJSON) # # Users need to specify their Postgres installation path through pg_config. For diff --git a/mongo_query.c b/mongo_query.c index c041b30..0121188 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -36,7 +36,9 @@ #include "utils/lsyscache.h" #include "utils/numeric.h" #include "utils/timestamp.h" - +#include "bson.h" +#include "json.h" +#include "bits.h" /* Local functions forward declarations */ static Expr * FindArgumentOfType(List *argumentList, NodeTag argumentType); @@ -48,6 +50,53 @@ static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); +bool json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ); + +bool json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ) { + bool status; + status = true; + if ( ! v ) { + bson_append_null( bb , k ); + return status; + } + + switch ( json_object_get_type( v ) ) { + case json_type_int: + bson_append_int( bb , k , json_object_get_int( v ) ); + break; + case json_type_boolean: + bson_append_bool( bb , k , json_object_get_boolean( v ) ); + break; + case json_type_double: + bson_append_double( bb , k , json_object_get_double( v ) ); + break; + case json_type_string: + bson_append_string( bb , k , json_object_get_string( v ) ); + break; + case json_type_object: + bson_append_start_object( bb , k ); + json_object_object_foreach( v, kk, vv ) { + json_to_bson_append_element( bb , kk , vv ); + } + bson_append_finish_object( bb ); + break; + case json_type_array: + bson_append_start_array( bb , k ); + int i; + char buf[10]; + for ( i=0; i Date: Fri, 7 Nov 2014 11:36:57 +0200 Subject: [PATCH 068/239] $date and $oid --- mongo_fdw.c | 2 +- mongo_query.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 6c880c5..5ce6764 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1638,7 +1638,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) { char oidhex[25]; bson_oid_to_string(bson_iterator_oid(&i), oidhex); - appendStringInfo(output, "\"%s\"", oidhex); + appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); break; } case BSON_BOOL: diff --git a/mongo_query.c b/mongo_query.c index 0121188..cb7abf2 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -74,12 +74,29 @@ bool json_to_bson_append_element( bson *bb , const char *k , struct json_object bson_append_string( bb , k , json_object_get_string( v ) ); break; case json_type_object: + { + struct json_object *joj = NULL; + joj = json_object_object_get( v, "$oid" ); + if (joj != NULL) { + bson_oid_t bsonObjectId; + memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); + BsonOidFromString(&bsonObjectId, 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; + } + bson_append_start_object( bb , k ); json_object_object_foreach( v, kk, vv ) { json_to_bson_append_element( bb , kk , vv ); } bson_append_finish_object( bb ); break; + } case json_type_array: bson_append_start_array( bb , k ); int i; From 921138939e0e82f136118490279bcee16cabf86c Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 11 Nov 2014 17:56:03 +0500 Subject: [PATCH 069/239] - Fix the compilation warnings introduced by this pull request Merge pull request #13 from the truongsinh / master - Disable the JSON Support for MongoDB's Meta (master branch) because it need to be implemented. - Fix the compilation errors in case of Meta Driver. - Fix the formatting issue introduce by the this pull request Merge pull request #13 from the truongsinh / master - README changes --- Makefile.legacy | 12 ++++--- Makefile.meta | 6 +++- README.md | 52 +++++++++++---------------- autogen.sh | 23 ++++++++++++ mongo-c-driver | 2 +- mongo_query.c | 83 ++++++-------------------------------------- mongo_wrapper.c | 78 +++++++++++++++++++++++++++++++++++++++++ mongo_wrapper.h | 6 ++++ mongo_wrapper_meta.c | 4 +++ 9 files changed, 157 insertions(+), 109 deletions(-) diff --git a/Makefile.legacy b/Makefile.legacy index a65c28e..bb9d3d0 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -12,14 +12,16 @@ MODULE_big = mongo_fdw # on another platform, change env_posix.os in MONGO_OBJS with the appropriate # environment object file. # - MONGO_DRIVER = mongo-c-driver MONGO_PATH = $(MONGO_DRIVER)/src MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os - -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) +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 +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql @@ -30,6 +32,8 @@ REGRESS_OPTS = --inputdir=test --outputdir=test \ $(MONGO_DRIVER)/%.os: $(MAKE) -C $(MONGO_DRIVER) $*.os +#$(LIBJSON)/json.o: +# $(MAKE) -C $(LIBJSON) # # Users need to specify their Postgres installation path through pg_config. For diff --git a/Makefile.meta b/Makefile.meta index 338e8ff..00aa088 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -12,9 +12,13 @@ MODULE_big = mongo_fdw # 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 MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) -PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -DMETA_DRIVER +PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) -DMETA_DRIVER SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o diff --git a/README.md b/README.md index 339fee3..b0aa28b 100644 --- a/README.md +++ b/README.md @@ -7,17 +7,11 @@ PostgreSQL Version **9.3** and greater. Installation ------------ -The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and -includes it as a git submodule. If you are cloning this repository for the -first time, be sure to pass the --recursive option to git clone in order to -initialize the driver submodule to a useable -state. +The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and includes it as a git submodule. If you are cloning this repository for the first time, be sure to pass the --recursive option to git clone in order to initialize the driver submodule to a useable state. -If have checked out this project before and for some reason your submodule is -not up-to-date, run git submodule update --init. +If checked out this project before and for some reason your submodule is not up-to-date, run git submodule update --init. -When you type `make`, the C driver's source code also gets automatically -compiled and linked. +When you type `make`, the C driver's source code also gets automaticallycompiled and linked. Note: Make sure you have permission to "/usr/local" (default installation location) folder. @@ -31,23 +25,15 @@ 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 insert/update and delete statements for the foreign tables using the mongo_fdw. +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 utilises the same mongo -database connection for all the queries in the same session. The previous version -would open a new mongodb connection for every query. -This is a performance enhancement. +The latest version comes with a connection pooler that utilizes the same mango 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. New MongoDB C Driver Support ---------------------------- -The third enhancement is to add a new [MongoDB][1]' C driver. The current implementation is -based on the legacy driver of MongoDB. But [MongoDB][1] is provided completely new library -for driver called MongoDB's Meta Driver. So I have added support of that driver. -Now compile time option is available to use legacy and Meta driver. I am sure there -are many other benefits of the new Mongo-C-driver that we are not leveraging but we -will adopt those as we learn more about the new C driver. +The third enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But [MongoDB][1] is provided completely new library for driver called MongoDB's Meta Driver. So I have added support of that driver. Now compile time option is available to use legacy and Meta driver. I am sure there are many other benefits of the new Mongo-C-driver that we are not leveraging but we will adopt those as we learn more about the new C driver. In order to use MongoDB driver 1.0.0+, take the following steps: @@ -56,6 +42,16 @@ In order to use MongoDB driver 1.0.0+, take the following steps: * ensure pkg-config / pkgconf is installed on your system. * run `make -f Makefile.meta && make -f Makefile.meta install` * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, then try running `ldconfig` + +Compilation script +----------------- +To avoid all the manual steps to compile and install differnet type of [MongoDB][1] drivers and supported libraries, there is a shell script to download and the install appropriate driver and libraries. + +Use [MongoDB][1]'s legacy branch driver + * sudo -s ./autogen --with-legacy + +Use [MongoDB][1]'s master branch driver + * sudo -s ./autogen --with-master Usage ----- @@ -76,9 +72,7 @@ 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`. +`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`. Examples with [MongoDB][1]'s equivalent statments. @@ -186,15 +180,13 @@ Limitations Contributing ------------ -Have a fix for a bug or an idea for a great new feature? Great! Check out the -contribution guidelines [here][4]. For all other types of questions or comments -about the wrapper please contact us at `mongo_fdw` `@` `enterprisedb.com`. +Have a fix for a bug or an idea for a great new feature? Great! Check out the contribution guidelines [here][4]. For all other types of questions or comments about the wrapper please contact us at `mongo_fdw` `@` `enterprisedb.com`. Support ------- This project will be modified to maintain compatibility with new PostgreSQL -releases. The project owners set aside a day every month to look over open +releases. The project owners to set aside a day every month to look over open issues and support emails, but are not engaged in active feature development. Reported bugs will be addressed by apparent severity. @@ -206,10 +198,8 @@ Portions Copyright © 2004-2014, 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. +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`][5] file for full details. diff --git a/autogen.sh b/autogen.sh index 2aaed4a..66e5a09 100755 --- a/autogen.sh +++ b/autogen.sh @@ -38,6 +38,27 @@ function checkout_legacy_branch git checkout legacy cd .. } +## +# Pull the json-c library +# +function checkout_json_lib +{ + rm -rf json-c + git clone https://github.com/json-c/json-c.git +} + + +## +# Compile and instal json-c library +# +function install_json_lib +{ + cd json-c + sh ./autogen.sh + ./configure + make install + cd .. +} ### # Configure and install the Mongo C Driver and libbson @@ -75,7 +96,9 @@ cleanup if [ "--with-legacy" = $1 ]; then checkout_mongo_driver + checkout_json_lib checkout_legacy_branch + install_json_lib cp Makefile.legacy Makefile echo "Done" elif [ "--with-master" == $1 ]; then diff --git a/mongo-c-driver b/mongo-c-driver index 02aaad0..8310f4d 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 02aaad0a705c9c53314d3579ca03c52bbab1fd67 +Subproject commit 8310f4d79613b87b63260ca600e54c59fc871a32 diff --git a/mongo_query.c b/mongo_query.c index fe4acd6..21b2a42 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -23,6 +23,11 @@ #else #include "mongo.h" #endif + +#include +#include +#include + #include "mongo_fdw.h" #include "mongo_query.h" @@ -36,9 +41,6 @@ #include "utils/lsyscache.h" #include "utils/numeric.h" #include "utils/timestamp.h" -#include "bson.h" -#include "json.h" -#include "bits.h" /* Local functions forward declarations */ static Expr * FindArgumentOfType(List *argumentList, NodeTag argumentType); @@ -49,71 +51,6 @@ static List * ColumnOperatorList(Var *column, List *operatorList); static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); - -bool json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ); - -bool json_to_bson_append_element( bson *bb , const char *k , struct json_object *v ) { - bool status; - status = true; - if ( ! v ) { - bson_append_null( bb , k ); - return status; - } - - switch ( json_object_get_type( v ) ) { - case json_type_int: - bson_append_int( bb , k , json_object_get_int( v ) ); - break; - case json_type_boolean: - bson_append_bool( bb , k , json_object_get_boolean( v ) ); - break; - case json_type_double: - bson_append_double( bb , k , json_object_get_double( v ) ); - break; - case json_type_string: - bson_append_string( bb , k , json_object_get_string( v ) ); - break; - case json_type_object: - { - struct json_object *joj = NULL; - joj = json_object_object_get( v, "$oid" ); - if (joj != NULL) { - bson_oid_t bsonObjectId; - memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - BsonOidFromString(&bsonObjectId, 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; - } - - bson_append_start_object( bb , k ); - json_object_object_foreach( v, kk, vv ) { - json_to_bson_append_element( bb , kk , vv ); - } - bson_append_finish_object( bb ); - break; - } - case json_type_array: - bson_append_start_array( bb , k ); - int i; - char buf[10]; - for ( i=0; i +#include +#include + #include "mongo_fdw.h" #define QUAL_STRING_LEN 512 @@ -345,12 +349,86 @@ BsonAppendBson(BSON* b, char *key, BSON* c) } + + bool BsonFinish(BSON* b) { return (bson_finish(b) == MONGO_OK); } + +bool +JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) +{ + bool status; + status = true; + if (!v) + { + bson_append_null(bb, k); + return status; + } + + switch (json_object_get_type(v)) + { + case json_type_int: + bson_append_int(bb, k, json_object_get_int(v)); + break; + case json_type_boolean: + bson_append_bool(bb , k, json_object_get_boolean(v)); + break; + case json_type_double: + bson_append_double(bb, k, json_object_get_double(v)); + break; + case json_type_string: + bson_append_string(bb, k, json_object_get_string(v)); + break; + case json_type_object: + { + struct json_object *joj = NULL; + joj = json_object_object_get(v, "$oid"); + if (joj != NULL) + { + bson_oid_t bsonObjectId; + memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); + BsonOidFromString(&bsonObjectId, 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; + } + + bson_append_start_object(bb , k); + json_object_object_foreach(v, kk, vv) + { + json_to_bson_append_element(bb ,kk ,vv); + } + bson_append_finish_object(bb); + break; + } + case json_type_array: + bson_append_start_array(bb ,k); + int i; + char buf[10]; + for (i = 0; i +#include +#include + #ifdef META_DRIVER MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, char *readPreference); #else @@ -81,4 +85,6 @@ bool BsonAppendStartObject(BSON* b, char *key, BSON *r); bool BsonAppendFinishObject(BSON* b, BSON* r); bool BsonAppendBson(BSON* b, char *key, BSON* c); bool BsonFinish(BSON* b); +bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v); + #endif diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 7bb7e29..03d1d4f 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -408,6 +408,10 @@ BsonFinish(BSON* b) return true; } +bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) +{ + elog(ERROR, "JSON support for Meta Driver not implemented"); +} /* * Count the number of documents. */ From 7f266827279db21194f6270cac6c14cdc2301330 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 11 Nov 2014 18:26:19 +0500 Subject: [PATCH 070/239] README file changes --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b0aa28b..b836593 100644 --- a/README.md +++ b/README.md @@ -42,16 +42,20 @@ In order to use MongoDB driver 1.0.0+, take the following steps: * ensure pkg-config / pkgconf is installed on your system. * run `make -f Makefile.meta && make -f Makefile.meta install` * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, then try running `ldconfig` - + Compilation script ----------------- -To avoid all the manual steps to compile and install differnet type of [MongoDB][1] drivers and supported libraries, there is a shell script to download and the install appropriate driver and libraries. +Number of manual steps needs to be performed to compile and install different type of MongoDB drivers and supported 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 : + +Build with [MongoDB][1]'s legacy branch driver + * autogen.sh --with-legacy -Use [MongoDB][1]'s legacy branch driver - * sudo -s ./autogen --with-legacy +Build [MongoDB][1]'s master branch driver + * autogen.sh --with-master -Use [MongoDB][1]'s master branch driver - * sudo -s ./autogen --with-master +The script will do all the necessary steps to build with legacy and metadriver accordingly. Usage ----- From c66ef973dde2c8cf38dc88026c41c78ee36b00a0 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 14 Nov 2014 22:20:55 +0500 Subject: [PATCH 071/239] Fixed a unresolved symbol error, and some cosmetic changes. --- mongo-c-driver | 2 +- mongo_wrapper.c | 116 +++++++++++++++++++----------------- test/expected/mongo_fdw.out | 62 ------------------- test/sql/mongo_fdw.sql | 39 ------------ test/sql/mongo_fixture.json | 31 ---------- 5 files changed, 63 insertions(+), 187 deletions(-) delete mode 100644 test/expected/mongo_fdw.out delete mode 100644 test/sql/mongo_fdw.sql delete mode 100644 test/sql/mongo_fixture.json diff --git a/mongo-c-driver b/mongo-c-driver index 8310f4d..02aaad0 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 8310f4d79613b87b63260ca600e54c59fc871a32 +Subproject commit 02aaad0a705c9c53314d3579ca03c52bbab1fd67 diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 0515ea5..cca3ae1 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -362,70 +362,78 @@ bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) { bool status; + status = true; - if (!v) + if (!v) { - bson_append_null(bb, k); - return status; - } + bson_append_null(bb, k); + return status; + } - switch (json_object_get_type(v)) + switch (json_object_get_type(v)) { - case json_type_int: - bson_append_int(bb, k, json_object_get_int(v)); - break; - case json_type_boolean: - bson_append_bool(bb , k, json_object_get_boolean(v)); - break; - case json_type_double: - bson_append_double(bb, k, json_object_get_double(v)); - break; - case json_type_string: - bson_append_string(bb, k, json_object_get_string(v)); - break; - case json_type_object: - { - struct json_object *joj = NULL; - joj = json_object_object_get(v, "$oid"); - if (joj != NULL) - { - bson_oid_t bsonObjectId; - memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - BsonOidFromString(&bsonObjectId, 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; - } + case json_type_int: + bson_append_int(bb, k, json_object_get_int(v)); + break; + + case json_type_boolean: + bson_append_bool(bb , k, json_object_get_boolean(v)); + break; + + case json_type_double: + bson_append_double(bb, k, json_object_get_double(v)); + break; + + case json_type_string: + bson_append_string(bb, k, json_object_get_string(v)); + break; - bson_append_start_object(bb , k); - json_object_object_foreach(v, kk, vv) + case json_type_object: { - json_to_bson_append_element(bb ,kk ,vv); + struct json_object *joj = NULL; + joj = json_object_object_get(v, "$oid"); + + if (joj != NULL) + { + bson_oid_t bsonObjectId; + memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); + BsonOidFromString(&bsonObjectId, 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; + } + + bson_append_start_object(bb , k); + json_object_object_foreach(v, kk, vv) + { + JsonToBsonAppendElement(bb, kk, vv); + } + bson_append_finish_object(bb); + break; } - bson_append_finish_object(bb); - break; - } - case json_type_array: - bson_append_start_array(bb ,k); - int i; - char buf[10]; - for (i = 0; i Date: Sat, 15 Nov 2014 00:45:00 +0500 Subject: [PATCH 072/239] Disable JSON write support (Meta Driver only), because it is not working for Meta Driver. --- mongo-c-driver | 2 +- mongo_query.c | 2 +- mongo_wrapper.c | 5 +++++ mongo_wrapper.h | 1 + mongo_wrapper_meta.c | 8 ++++++++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/mongo-c-driver b/mongo-c-driver index 02aaad0..96d48e9 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 02aaad0a705c9c53314d3579ca03c52bbab1fd67 +Subproject commit 96d48e98e95fdb5066706f392057ff853c83c259 diff --git a/mongo_query.c b/mongo_query.c index 21b2a42..78a15df 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -602,7 +602,7 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu bool typeVarLength = false; getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - o = json_tokener_parse(outputString); + o = JsonTokenerPrase(outputString); if (is_error(o)) { diff --git a/mongo_wrapper.c b/mongo_wrapper.c index cca3ae1..088eb82 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -357,6 +357,11 @@ BsonFinish(BSON* b) return (bson_finish(b) == MONGO_OK); } +json_object* +JsonTokenerPrase(char * s) +{ + return json_tokener_parse(s); +} bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 4b00f5f..18e1dde 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -87,4 +87,5 @@ bool BsonAppendBson(BSON* b, char *key, BSON* c); bool BsonFinish(BSON* b); bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v); +json_object *JsonTokenerPrase(char * s); #endif diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 03d1d4f..7f72df0 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -412,6 +412,14 @@ bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) { elog(ERROR, "JSON support for Meta Driver not implemented"); } + +json_object* +JsonTokenerPrase(char * s) +{ + elog(ERROR, "JSON support for Meta Driver not implemented"); + return NULL; +} + /* * Count the number of documents. */ From a9261b6201c43652708c0a46ac1b33d9b091e239 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Wed, 25 Feb 2015 16:07:40 +0500 Subject: [PATCH 073/239] Fix a crash while fetching remote row count using MongoDB's Meta C Driver. --- README.md | 2 +- mongo-c-driver | 2 +- mongo_query.c | 2 +- mongo_wrapper_meta.c | 39 ++++++++++++++++++--------------------- 4 files changed, 21 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index b836593..0c051e4 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ db.warehouse.find({"warehouse_id" : 1}).pretty() -- insert row in table -INSERT INTO warehouse values (0, 1, 'UPS', to_date('2014-12-12T07:12:10Z')); +INSERT INTO warehouse values (0, 1, 'UPS', '2014-12-12T07:12:10Z'); db.warehouse.insert ( diff --git a/mongo-c-driver b/mongo-c-driver index 96d48e9..2929c2d 160000 --- a/mongo-c-driver +++ b/mongo-c-driver @@ -1 +1 @@ -Subproject commit 96d48e98e95fdb5066706f392057ff853c83c259 +Subproject commit 2929c2d2c856a57ecdfef4d61f8e479b6ef96463 diff --git a/mongo_query.c b/mongo_query.c index 78a15df..2d0b69b 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -393,7 +393,7 @@ AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) { - bool status; + bool status = false; if (isnull) { status = BsonAppendNull(queryDocument, keyName); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 7f72df0..fa5d7cc 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -426,12 +426,12 @@ JsonTokenerPrase(char * s) double MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collection, const BSON* b) { - BSON *command; - BSON *reply; - BSON *doc; - double count; - mongoc_cursor_t *cursor; - bool ret; + BSON *command = NULL; + BSON *reply = NULL; + BSON *doc = NULL; + double count = 0; + mongoc_cursor_t *cursor = NULL; + bool ret = false; command = BsonCreate(); reply = BsonCreate(); @@ -442,23 +442,20 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti BsonFinish(command); cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, 0, command, NULL, NULL); - - ret = mongoc_cursor_next(cursor, (const BSON**)&doc); - - if (ret) { - bson_iter_t it; - bson_copy_to(doc, reply); - if (bson_iter_init_find(&it, reply, "n")) - count = BsonIterDouble(&it); - } else { - count = -1; + if (cursor) + { + ret = mongoc_cursor_next(cursor, (const BSON**)&doc); + if (ret) + { + bson_iter_t it; + bson_copy_to(doc, reply); + if (bson_iter_init_find(&it, reply, "n")) + count = BsonIterDouble(&it); + BsonDestroy(doc); + } + mongoc_cursor_destroy(cursor); } - - mongoc_cursor_destroy(cursor); - BsonDestroy(reply); - BsonDestroy(doc); BsonDestroy(command); - return count; } From cdbd6b6af56256779a78659085cdc3f604f46808 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 26 Feb 2015 01:26:34 +0500 Subject: [PATCH 074/239] Error - (#22)Remove git dependency from autogen.sh, and download stable version of Mongo-C driver and json lib using wget. --- Makefile | 3 +-- autogen.sh | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index bb9d3d0..67e1a68 100644 --- a/Makefile +++ b/Makefile @@ -27,8 +27,7 @@ EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql REGRESS = mongo_fdw -REGRESS_OPTS = --inputdir=test --outputdir=test \ - --load-extension=$(EXTENSION) +REGRESS_OPTS = --load-extension=$(EXTENSION) $(MONGO_DRIVER)/%.os: $(MAKE) -C $(MONGO_DRIVER) $*.os diff --git a/autogen.sh b/autogen.sh index 66e5a09..a8c045f 100755 --- a/autogen.sh +++ b/autogen.sh @@ -25,8 +25,10 @@ fi function checkout_mongo_driver { rm -rf mongo-c-driver - git clone https://github.com/mongodb/mongo-c-driver mongo-c-driver - git checkout master + wget https://github.com/mongodb/mongo-c-driver/releases/download/1.1.0/mongo-c-driver-1.1.0.tar.gz + tar -zxvf mongo-c-driver-1.1.0.tar.gz + mv mongo-c-driver-1.1.0 mongo-c-driver + rm -rf mongo-c-driver-1.1.0.tar.gz } ### @@ -34,17 +36,27 @@ function checkout_mongo_driver # function checkout_legacy_branch { - cd mongo-c-driver - git checkout legacy - cd .. + rm -rf mongo-c-driver + wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz + tar -zxvf v0.8.tar.gz + mv mongo-c-driver-0.8 mongo-c-driver + rm -rf v0.8.tar.gz } ## # Pull the json-c library # function checkout_json_lib { + echo $PWD rm -rf json-c - git clone https://github.com/json-c/json-c.git + wget https://github.com/json-c/json-c/archive/json-c-0.12-20140410.tar.gz + tar -zxvf json-c-0.12-20140410.tar.gz + mv json-c-json-c-0.12-20140410 json-c + cd json-c + patch -p1 < ../json_compilation_error.patch + cd .. + rm -rf json-c-0.12-20140410.tar.gz + echo $PWD } @@ -103,7 +115,9 @@ if [ "--with-legacy" = $1 ]; then echo "Done" elif [ "--with-master" == $1 ]; then checkout_mongo_driver + checkout_json_lib install_mongoc_driver + install_json_lib create_config export PKG_CONFIG_PATH=mongo-c-driver/src/:mongo-c-driver/src/libbson/src cp Makefile.meta Makefile From 3ce043d574f68c47a9740e5a17f06400d4497f2c Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 26 Feb 2015 01:32:22 +0500 Subject: [PATCH 075/239] Error - (#23) Regression test cases added --- data/mongo_fixture.json | 32 ++++ expected/mongo_fdw.out | 329 ++++++++++++++++++++++++++++++++++++++++ sql/mongo_fdw.sql | 73 +++++++++ 3 files changed, 434 insertions(+) create mode 100644 data/mongo_fixture.json create mode 100644 expected/mongo_fdw.out create mode 100644 sql/mongo_fdw.sql diff --git a/data/mongo_fixture.json b/data/mongo_fixture.json new file mode 100644 index 0000000..e54bdca --- /dev/null +++ b/data/mongo_fixture.json @@ -0,0 +1,32 @@ +[{ + "_id": {"$oid": "5381ccf9d6d81c8e8bf0434f"}, + "name": "Ukraine", + "population": 45590000, + "capital": "Kyiv", + "hdi": 0.74, + "lastElections": {"type": "presedential", "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": "presedential", "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 enamelled or anodised) wire, cable", + "Sunflower seeds, whether or not broken"] +}] diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out new file mode 100644 index 0000000..11a2fab --- /dev/null +++ b/expected/mongo_fdw.out @@ -0,0 +1,329 @@ +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json +CREATE USER MAPPING FOR postgres SERVER mongo_server; +CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); +CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); +INSERT INTO department VALUES(0, generate_series(1,100), 'dept - ' || generate_series(1,100)); +INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,100)); +SELECT count(*) FROM department; + count +------- + 100 +(1 row) + +SELECT count(*) FROM employee; + count +------- + 100 +(1 row) + +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; + QUERY PLAN +-------------------------------------------------------- + Limit + -> Hash Join + Hash Cond: (d.department_id = e.emp_dept_id) + -> Foreign Scan on department d + Foreign Namespace: testdb.department + -> Hash + -> Foreign Scan on employee e + Foreign Namespace: testdb.employee +(8 rows) + +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; + QUERY PLAN +------------------------------------------------------------------------- + Limit + -> Nested Loop + -> Nested Loop Semi Join + Join Filter: (d.department_id = department.department_id) + -> Foreign Scan on department d + Foreign Namespace: testdb.department + -> Materialize + -> Foreign Scan on department + Foreign Namespace: testdb.department + -> Materialize + -> Foreign Scan on employee e + Foreign Namespace: testdb.employee +(12 rows) + +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; + emp_id | emp_name | emp_dept_id | department_id | department_name +--------+----------+-------------+---------------+----------------- + 39 | emp - 39 | 39 | 39 | dept - 39 + 40 | emp - 40 | 40 | 40 | dept - 40 + 41 | emp - 41 | 41 | 41 | dept - 41 + 42 | emp - 42 | 42 | 42 | dept - 42 + 43 | emp - 43 | 43 | 43 | dept - 43 + 44 | emp - 44 | 44 | 44 | dept - 44 + 45 | emp - 45 | 45 | 45 | dept - 45 + 46 | emp - 46 | 46 | 46 | dept - 46 + 47 | emp - 47 | 47 | 47 | dept - 47 + 48 | emp - 48 | 48 | 48 | dept - 48 +(10 rows) + +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; + emp_id | emp_name | emp_dept_id | department_id | department_name +--------+----------+-------------+---------------+----------------- + 38 | emp - 38 | 38 | 39 | dept - 39 + 39 | emp - 39 | 39 | 39 | dept - 39 + 40 | emp - 40 | 40 | 39 | dept - 39 + 41 | emp - 41 | 41 | 39 | dept - 39 + 42 | emp - 42 | 42 | 39 | dept - 39 + 43 | emp - 43 | 43 | 39 | dept - 39 + 44 | emp - 44 | 44 | 39 | dept - 39 + 45 | emp - 45 | 45 | 39 | dept - 39 + 46 | emp - 46 | 46 | 39 | dept - 39 + 47 | emp - 47 | 47 | 39 | dept - 39 +(10 rows) + +DELETE FROM employee WHERE emp_id = 10; +UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; +SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; + emp_id | emp_name +--------+------------- + 20 | Updated emp +(1 row) + +SELECT emp_id , emp_name , emp_dept_id FROM employee LIMIT 10; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 + 43 | emp - 43 | 43 + 44 | emp - 44 | 44 + 45 | emp - 45 | 45 + 46 | emp - 46 | 46 + 47 | emp - 47 | 47 +(10 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1); + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 1 | emp - 1 | 1 +(1 row) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5); + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 1 | emp - 1 | 1 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 +(4 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000); + emp_id | emp_name | emp_dept_id +--------+----------+------------- +(0 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) LIMIT 5; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 +(5 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) LIMIT 5; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 +(5 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) LIMIT 5; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 +(5 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)); + emp_id | emp_name | emp_dept_id +--------+-------------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 + 43 | emp - 43 | 43 + 44 | emp - 44 | 44 + 45 | emp - 45 | 45 + 46 | emp - 46 | 46 + 47 | emp - 47 | 47 + 48 | emp - 48 | 48 + 49 | emp - 49 | 49 + 50 | emp - 50 | 50 + 51 | emp - 51 | 51 + 52 | emp - 52 | 52 + 53 | emp - 53 | 53 + 54 | emp - 54 | 54 + 55 | emp - 55 | 55 + 56 | emp - 56 | 56 + 57 | emp - 57 | 57 + 58 | emp - 58 | 58 + 59 | emp - 59 | 59 + 60 | emp - 60 | 60 + 61 | emp - 61 | 61 + 62 | emp - 62 | 62 + 63 | emp - 63 | 63 + 64 | emp - 64 | 64 + 65 | emp - 65 | 65 + 66 | emp - 66 | 66 + 67 | emp - 67 | 67 + 68 | emp - 68 | 68 + 69 | emp - 69 | 69 + 70 | emp - 70 | 70 + 71 | emp - 71 | 71 + 72 | emp - 72 | 72 + 73 | emp - 73 | 73 + 74 | emp - 74 | 74 + 75 | emp - 75 | 75 + 76 | emp - 76 | 76 + 77 | emp - 77 | 77 + 78 | emp - 78 | 78 + 79 | emp - 79 | 79 + 80 | emp - 80 | 80 + 81 | emp - 81 | 81 + 82 | emp - 82 | 82 + 83 | emp - 83 | 83 + 84 | emp - 84 | 84 + 85 | emp - 85 | 85 + 86 | emp - 86 | 86 + 87 | emp - 87 | 87 + 88 | emp - 88 | 88 + 89 | emp - 89 | 89 + 90 | emp - 90 | 90 + 91 | emp - 91 | 91 + 92 | emp - 92 | 92 + 93 | emp - 93 | 93 + 94 | emp - 94 | 94 + 95 | emp - 95 | 95 + 96 | emp - 96 | 96 + 97 | emp - 97 | 97 + 98 | emp - 98 | 98 + 99 | emp - 99 | 99 + 2 | emp - 2 | 2 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 + 6 | emp - 6 | 6 + 7 | emp - 7 | 7 + 8 | emp - 8 | 8 + 9 | emp - 9 | 9 + 11 | emp - 11 | 11 + 12 | emp - 12 | 12 + 13 | emp - 13 | 13 + 14 | emp - 14 | 14 + 15 | emp - 15 | 15 + 16 | emp - 16 | 16 + 17 | emp - 17 | 17 + 18 | emp - 18 | 18 + 19 | emp - 19 | 19 + 20 | Updated emp | 20 + 21 | emp - 21 | 21 + 22 | emp - 22 | 22 + 23 | emp - 23 | 23 + 24 | emp - 24 | 24 + 25 | emp - 25 | 25 + 26 | emp - 26 | 26 + 27 | emp - 27 | 27 + 28 | emp - 28 | 28 + 29 | emp - 29 | 29 + 30 | emp - 30 | 30 + 31 | emp - 31 | 31 + 32 | emp - 32 | 32 + 33 | emp - 33 | 33 + 34 | emp - 34 | 34 + 35 | emp - 35 | 35 + 36 | emp - 36 | 36 + 37 | emp - 37 | 37 + 100 | emp - 100 | 100 +(98 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') LIMIT 5; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 +(5 rows) + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') LIMIT 5; + emp_id | emp_name | emp_dept_id +--------+----------+------------- + 38 | emp - 38 | 38 + 39 | emp - 39 | 39 + 40 | emp - 40 | 40 + 41 | emp - 41 | 41 + 42 | emp - 42 | 42 +(5 rows) + +DELETE FROM employee; +DELETE FROM department; +CREATE FOREIGN TABLE countries ( +_id NAME, +name VARCHAR, +population INTEGER, +capital VARCHAR, +hdi FLOAT +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM countries; + _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) + +-- +-- Subfields and dates +CREATE FOREIGN TABLE country_elections ( +_id NAME, +"lastElections.type" VARCHAR, +"lastElections.date" TIMESTAMP +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections; + _id | lastElections.type | lastElections.date +--------------------------+--------------------+------------------------------ + 5381ccf9d6d81c8e8bf0434f | presedential | Sat Jan 10 10:51:01.504 1970 + 5381ccf9d6d81c8e8bf04350 | parliamentary | Fri Dec 26 22:44:00.128 1969 + 5381ccf9d6d81c8e8bf04351 | parliamentary | Wed Dec 10 05:00:43.904 1969 +(3 rows) + +-- +-- Arrays +CREATE FOREIGN TABLE main_exports ( +_id NAME, +"mainExports" TEXT[] +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM main_exports; + _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 enamelled or anodised) wire, cable","Sunflower seeds, whether or not broken"} +(3 rows) + +DROP FOREIGN TABLE department; +DROP FOREIGN TABLE employee; +DROP FOREIGN TABLE countries; +DROP FOREIGN TABLE country_elections; +DROP FOREIGN TABLE main_exports; +DROP USER MAPPING FOR postgres SERVER mongo_server; +DROP EXTENSION mongo_fdw CASCADE; +NOTICE: drop cascades to server mongo_server diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql new file mode 100644 index 0000000..cc80bae --- /dev/null +++ b/sql/mongo_fdw.sql @@ -0,0 +1,73 @@ +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json + +CREATE USER MAPPING FOR ibrar SERVER mongo_server; + +CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); +CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); + +INSERT INTO department VALUES(0, generate_series(1,100), 'dept - ' || generate_series(1,100)); +INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,100)); + +SELECT count(*) FROM department; +SELECT count(*) FROM employee; + +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; + +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; + +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; + +DELETE FROM employee WHERE emp_id = 10; + +UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; +SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; + +SELECT emp_id , emp_name , emp_dept_id FROM employee LIMIT 10; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000); + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) LIMIT 5; + +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') LIMIT 5; + +DELETE FROM employee; +DELETE FROM department; + +CREATE FOREIGN TABLE countries ( +_id NAME, +name VARCHAR, +population INTEGER, +capital VARCHAR, +hdi FLOAT +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM countries; +-- +-- Subfields and dates +CREATE FOREIGN TABLE country_elections ( +_id NAME, +"lastElections.type" VARCHAR, +"lastElections.date" TIMESTAMP +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM country_elections; +-- +-- Arrays +CREATE FOREIGN TABLE main_exports ( +_id NAME, +"mainExports" TEXT[] +) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); +SELECT * FROM main_exports; + +DROP FOREIGN TABLE department; +DROP FOREIGN TABLE employee; +DROP FOREIGN TABLE countries; +DROP FOREIGN TABLE country_elections; +DROP FOREIGN TABLE main_exports; +DROP USER MAPPING FOR ibrar SERVER mongo_server; +DROP EXTENSION mongo_fdw CASCADE; From 92c0f0b7223e5e68e9c53f7b8724c7233f24f3e8 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 26 Feb 2015 01:42:17 +0500 Subject: [PATCH 076/239] Error - (#23) Regression test cases for Meta Driver (Makefile change). --- Makefile | 25 ++++++++++--------------- Makefile.legacy | 3 +-- Makefile.meta | 3 +++ sql/mongo_fdw.sql | 4 ++-- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 67e1a68..a74e384 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# mongo_fdw/Makefile +# mongo_fdw/Makefile.meta # # Portions Copyright © 2004-2014, EnterpriseDB Corporation. # @@ -12,16 +12,16 @@ MODULE_big = mongo_fdw # on another platform, change env_posix.os in MONGO_OBJS with the appropriate # environment object file. # -MONGO_DRIVER = mongo-c-driver -MONGO_PATH = $(MONGO_DRIVER)/src -MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ - $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os 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 -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) -OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) +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 + +MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) +PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) -DMETA_DRIVER +SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) + +OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql @@ -29,11 +29,6 @@ DATA = mongo_fdw--1.0.sql REGRESS = mongo_fdw REGRESS_OPTS = --load-extension=$(EXTENSION) -$(MONGO_DRIVER)/%.os: - $(MAKE) -C $(MONGO_DRIVER) $*.os -#$(LIBJSON)/json.o: -# $(MAKE) -C $(LIBJSON) - # # 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 diff --git a/Makefile.legacy b/Makefile.legacy index bb9d3d0..67e1a68 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -27,8 +27,7 @@ EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql REGRESS = mongo_fdw -REGRESS_OPTS = --inputdir=test --outputdir=test \ - --load-extension=$(EXTENSION) +REGRESS_OPTS = --load-extension=$(EXTENSION) $(MONGO_DRIVER)/%.os: $(MAKE) -C $(MONGO_DRIVER) $*.os diff --git a/Makefile.meta b/Makefile.meta index 00aa088..a74e384 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -26,6 +26,9 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql +REGRESS = mongo_fdw +REGRESS_OPTS = --load-extension=$(EXTENSION) + # # 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 diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index cc80bae..9e30b0c 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -1,7 +1,7 @@ CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); \! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json -CREATE USER MAPPING FOR ibrar SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); @@ -69,5 +69,5 @@ DROP FOREIGN TABLE employee; DROP FOREIGN TABLE countries; DROP FOREIGN TABLE country_elections; DROP FOREIGN TABLE main_exports; -DROP USER MAPPING FOR ibrar SERVER mongo_server; +DROP USER MAPPING FOR postgres SERVER mongo_server; DROP EXTENSION mongo_fdw CASCADE; From 2e61e284baf2075172fdc431031fa8ab93008128 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 26 Feb 2015 02:02:25 +0500 Subject: [PATCH 077/239] Error - (#24) Removed error message in case of no rows. --- mongo_fdw.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index c1c78f8..7853f91 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -546,27 +546,6 @@ MongoIterateForeignScan(ForeignScanState *scanState) ExecStoreVirtualTuple(tupleSlot); } - else - { - #ifdef META_DRIVER - bson_error_t error; - if (mongoc_cursor_error (mongoCursor, &error)) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver error: %s", error.message))); - } - #else - mongo_cursor_error_t errorCode = mongoCursor->err; - if (errorCode != MONGO_CURSOR_EXHAUSTED) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); - } - #endif - } - return tupleSlot; } From b473d35ed2fcb88931b22a01d35a75b48282552a Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 26 Feb 2015 02:03:46 +0500 Subject: [PATCH 078/239] Error - (#23) Test case update --- Makefile | 25 +- expected/mongo_fdw.out | 1457 ++++++++++++++++++++++++++++++++++------ sql/mongo_fdw.sql | 34 +- 3 files changed, 1302 insertions(+), 214 deletions(-) diff --git a/Makefile b/Makefile index a74e384..67e1a68 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# mongo_fdw/Makefile.meta +# mongo_fdw/Makefile # # Portions Copyright © 2004-2014, EnterpriseDB Corporation. # @@ -12,16 +12,16 @@ MODULE_big = mongo_fdw # on another platform, change env_posix.os in MONGO_OBJS with the appropriate # environment object file. # +MONGO_DRIVER = mongo-c-driver +MONGO_PATH = $(MONGO_DRIVER)/src +MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ + $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os 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 - -MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) -PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) -DMETA_DRIVER -SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) - -OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o +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 +PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql @@ -29,6 +29,11 @@ DATA = mongo_fdw--1.0.sql REGRESS = mongo_fdw REGRESS_OPTS = --load-extension=$(EXTENSION) +$(MONGO_DRIVER)/%.os: + $(MAKE) -C $(MONGO_DRIVER) $*.os +#$(LIBJSON)/json.o: +# $(MAKE) -C $(LIBJSON) + # # 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 diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 11a2fab..80923f8 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -3,12 +3,12 @@ CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127. CREATE USER MAPPING FOR postgres SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); -INSERT INTO department VALUES(0, generate_series(1,100), 'dept - ' || generate_series(1,100)); -INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,100)); +INSERT INTO department VALUES(0, generate_series(1,10), 'dept - ' || generate_series(1,10)); +INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,10)); SELECT count(*) FROM department; count ------- - 100 + 10 (1 row) SELECT count(*) FROM employee; @@ -17,65 +17,1148 @@ SELECT count(*) FROM employee; 100 (1 row) -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; - QUERY PLAN --------------------------------------------------------- - Limit +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; + QUERY PLAN +---------------------------------------------------------- + Sort + Sort Key: e.emp_id -> Hash Join - Hash Cond: (d.department_id = e.emp_dept_id) - -> Foreign Scan on department d - Foreign Namespace: testdb.department + Hash Cond: (e.emp_dept_id = d.department_id) + -> Foreign Scan on employee e + Foreign Namespace: testdb.employee -> Hash - -> Foreign Scan on employee e - Foreign Namespace: testdb.employee -(8 rows) - -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; - QUERY PLAN -------------------------------------------------------------------------- - Limit - -> Nested Loop - -> Nested Loop Semi Join - Join Filter: (d.department_id = department.department_id) -> Foreign Scan on department d Foreign Namespace: testdb.department - -> Materialize +(9 rows) + +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; + QUERY PLAN +----------------------------------------------------------------- + Sort + Sort Key: e.emp_id + -> Hash Join + Hash Cond: (department.department_id = d.department_id) + -> Nested Loop + -> HashAggregate + Group Key: department.department_id -> Foreign Scan on department Foreign Namespace: testdb.department - -> Materialize -> Foreign Scan on employee e Foreign Namespace: testdb.employee -(12 rows) + -> Hash + -> Foreign Scan on department d + Foreign Namespace: testdb.department +(14 rows) -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; - emp_id | emp_name | emp_dept_id | department_id | department_name ---------+----------+-------------+---------------+----------------- - 39 | emp - 39 | 39 | 39 | dept - 39 - 40 | emp - 40 | 40 | 40 | dept - 40 - 41 | emp - 41 | 41 | 41 | dept - 41 - 42 | emp - 42 | 42 | 42 | dept - 42 - 43 | emp - 43 | 43 | 43 | dept - 43 - 44 | emp - 44 | 44 | 44 | dept - 44 - 45 | emp - 45 | 45 | 45 | dept - 45 - 46 | emp - 46 | 46 | 46 | dept - 46 - 47 | emp - 47 | 47 | 47 | dept - 47 - 48 | emp - 48 | 48 | 48 | dept - 48 -(10 rows) +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; + emp_id | emp_name | emp_dept_id | department_id | department_name +--------+-----------+-------------+---------------+----------------- + 1 | emp - 1 | 1 | 1 | dept - 1 + 2 | emp - 2 | 2 | 2 | dept - 2 + 3 | emp - 3 | 3 | 3 | dept - 3 + 4 | emp - 4 | 4 | 4 | dept - 4 + 5 | emp - 5 | 5 | 5 | dept - 5 + 6 | emp - 6 | 6 | 6 | dept - 6 + 7 | emp - 7 | 7 | 7 | dept - 7 + 8 | emp - 8 | 8 | 8 | dept - 8 + 9 | emp - 9 | 9 | 9 | dept - 9 + 10 | emp - 10 | 10 | 10 | dept - 10 + 11 | emp - 11 | 1 | 1 | dept - 1 + 12 | emp - 12 | 2 | 2 | dept - 2 + 13 | emp - 13 | 3 | 3 | dept - 3 + 14 | emp - 14 | 4 | 4 | dept - 4 + 15 | emp - 15 | 5 | 5 | dept - 5 + 16 | emp - 16 | 6 | 6 | dept - 6 + 17 | emp - 17 | 7 | 7 | dept - 7 + 18 | emp - 18 | 8 | 8 | dept - 8 + 19 | emp - 19 | 9 | 9 | dept - 9 + 20 | emp - 20 | 10 | 10 | dept - 10 + 21 | emp - 21 | 1 | 1 | dept - 1 + 22 | emp - 22 | 2 | 2 | dept - 2 + 23 | emp - 23 | 3 | 3 | dept - 3 + 24 | emp - 24 | 4 | 4 | dept - 4 + 25 | emp - 25 | 5 | 5 | dept - 5 + 26 | emp - 26 | 6 | 6 | dept - 6 + 27 | emp - 27 | 7 | 7 | dept - 7 + 28 | emp - 28 | 8 | 8 | dept - 8 + 29 | emp - 29 | 9 | 9 | dept - 9 + 30 | emp - 30 | 10 | 10 | dept - 10 + 31 | emp - 31 | 1 | 1 | dept - 1 + 32 | emp - 32 | 2 | 2 | dept - 2 + 33 | emp - 33 | 3 | 3 | dept - 3 + 34 | emp - 34 | 4 | 4 | dept - 4 + 35 | emp - 35 | 5 | 5 | dept - 5 + 36 | emp - 36 | 6 | 6 | dept - 6 + 37 | emp - 37 | 7 | 7 | dept - 7 + 38 | emp - 38 | 8 | 8 | dept - 8 + 39 | emp - 39 | 9 | 9 | dept - 9 + 40 | emp - 40 | 10 | 10 | dept - 10 + 41 | emp - 41 | 1 | 1 | dept - 1 + 42 | emp - 42 | 2 | 2 | dept - 2 + 43 | emp - 43 | 3 | 3 | dept - 3 + 44 | emp - 44 | 4 | 4 | dept - 4 + 45 | emp - 45 | 5 | 5 | dept - 5 + 46 | emp - 46 | 6 | 6 | dept - 6 + 47 | emp - 47 | 7 | 7 | dept - 7 + 48 | emp - 48 | 8 | 8 | dept - 8 + 49 | emp - 49 | 9 | 9 | dept - 9 + 50 | emp - 50 | 10 | 10 | dept - 10 + 51 | emp - 51 | 1 | 1 | dept - 1 + 52 | emp - 52 | 2 | 2 | dept - 2 + 53 | emp - 53 | 3 | 3 | dept - 3 + 54 | emp - 54 | 4 | 4 | dept - 4 + 55 | emp - 55 | 5 | 5 | dept - 5 + 56 | emp - 56 | 6 | 6 | dept - 6 + 57 | emp - 57 | 7 | 7 | dept - 7 + 58 | emp - 58 | 8 | 8 | dept - 8 + 59 | emp - 59 | 9 | 9 | dept - 9 + 60 | emp - 60 | 10 | 10 | dept - 10 + 61 | emp - 61 | 1 | 1 | dept - 1 + 62 | emp - 62 | 2 | 2 | dept - 2 + 63 | emp - 63 | 3 | 3 | dept - 3 + 64 | emp - 64 | 4 | 4 | dept - 4 + 65 | emp - 65 | 5 | 5 | dept - 5 + 66 | emp - 66 | 6 | 6 | dept - 6 + 67 | emp - 67 | 7 | 7 | dept - 7 + 68 | emp - 68 | 8 | 8 | dept - 8 + 69 | emp - 69 | 9 | 9 | dept - 9 + 70 | emp - 70 | 10 | 10 | dept - 10 + 71 | emp - 71 | 1 | 1 | dept - 1 + 72 | emp - 72 | 2 | 2 | dept - 2 + 73 | emp - 73 | 3 | 3 | dept - 3 + 74 | emp - 74 | 4 | 4 | dept - 4 + 75 | emp - 75 | 5 | 5 | dept - 5 + 76 | emp - 76 | 6 | 6 | dept - 6 + 77 | emp - 77 | 7 | 7 | dept - 7 + 78 | emp - 78 | 8 | 8 | dept - 8 + 79 | emp - 79 | 9 | 9 | dept - 9 + 80 | emp - 80 | 10 | 10 | dept - 10 + 81 | emp - 81 | 1 | 1 | dept - 1 + 82 | emp - 82 | 2 | 2 | dept - 2 + 83 | emp - 83 | 3 | 3 | dept - 3 + 84 | emp - 84 | 4 | 4 | dept - 4 + 85 | emp - 85 | 5 | 5 | dept - 5 + 86 | emp - 86 | 6 | 6 | dept - 6 + 87 | emp - 87 | 7 | 7 | dept - 7 + 88 | emp - 88 | 8 | 8 | dept - 8 + 89 | emp - 89 | 9 | 9 | dept - 9 + 90 | emp - 90 | 10 | 10 | dept - 10 + 91 | emp - 91 | 1 | 1 | dept - 1 + 92 | emp - 92 | 2 | 2 | dept - 2 + 93 | emp - 93 | 3 | 3 | dept - 3 + 94 | emp - 94 | 4 | 4 | dept - 4 + 95 | emp - 95 | 5 | 5 | dept - 5 + 96 | emp - 96 | 6 | 6 | dept - 6 + 97 | emp - 97 | 7 | 7 | dept - 7 + 98 | emp - 98 | 8 | 8 | dept - 8 + 99 | emp - 99 | 9 | 9 | dept - 9 + 100 | emp - 100 | 10 | 10 | dept - 10 +(100 rows) -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; - emp_id | emp_name | emp_dept_id | department_id | department_name ---------+----------+-------------+---------------+----------------- - 38 | emp - 38 | 38 | 39 | dept - 39 - 39 | emp - 39 | 39 | 39 | dept - 39 - 40 | emp - 40 | 40 | 39 | dept - 39 - 41 | emp - 41 | 41 | 39 | dept - 39 - 42 | emp - 42 | 42 | 39 | dept - 39 - 43 | emp - 43 | 43 | 39 | dept - 39 - 44 | emp - 44 | 44 | 39 | dept - 39 - 45 | emp - 45 | 45 | 39 | dept - 39 - 46 | emp - 46 | 46 | 39 | dept - 39 - 47 | emp - 47 | 47 | 39 | dept - 39 -(10 rows) +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; + emp_id | emp_name | emp_dept_id | department_id | department_name +--------+-----------+-------------+---------------+----------------- + 1 | emp - 1 | 1 | 10 | dept - 10 + 1 | emp - 1 | 1 | 6 | dept - 6 + 1 | emp - 1 | 1 | 1 | dept - 1 + 1 | emp - 1 | 1 | 3 | dept - 3 + 1 | emp - 1 | 1 | 8 | dept - 8 + 1 | emp - 1 | 1 | 5 | dept - 5 + 1 | emp - 1 | 1 | 9 | dept - 9 + 1 | emp - 1 | 1 | 4 | dept - 4 + 1 | emp - 1 | 1 | 2 | dept - 2 + 1 | emp - 1 | 1 | 7 | dept - 7 + 2 | emp - 2 | 2 | 8 | dept - 8 + 2 | emp - 2 | 2 | 7 | dept - 7 + 2 | emp - 2 | 2 | 5 | dept - 5 + 2 | emp - 2 | 2 | 1 | dept - 1 + 2 | emp - 2 | 2 | 3 | dept - 3 + 2 | emp - 2 | 2 | 2 | dept - 2 + 2 | emp - 2 | 2 | 4 | dept - 4 + 2 | emp - 2 | 2 | 9 | dept - 9 + 2 | emp - 2 | 2 | 10 | dept - 10 + 2 | emp - 2 | 2 | 6 | dept - 6 + 3 | emp - 3 | 3 | 10 | dept - 10 + 3 | emp - 3 | 3 | 1 | dept - 1 + 3 | emp - 3 | 3 | 7 | dept - 7 + 3 | emp - 3 | 3 | 6 | dept - 6 + 3 | emp - 3 | 3 | 4 | dept - 4 + 3 | emp - 3 | 3 | 8 | dept - 8 + 3 | emp - 3 | 3 | 5 | dept - 5 + 3 | emp - 3 | 3 | 3 | dept - 3 + 3 | emp - 3 | 3 | 9 | dept - 9 + 3 | emp - 3 | 3 | 2 | dept - 2 + 4 | emp - 4 | 4 | 3 | dept - 3 + 4 | emp - 4 | 4 | 1 | dept - 1 + 4 | emp - 4 | 4 | 2 | dept - 2 + 4 | emp - 4 | 4 | 8 | dept - 8 + 4 | emp - 4 | 4 | 7 | dept - 7 + 4 | emp - 4 | 4 | 9 | dept - 9 + 4 | emp - 4 | 4 | 4 | dept - 4 + 4 | emp - 4 | 4 | 6 | dept - 6 + 4 | emp - 4 | 4 | 10 | dept - 10 + 4 | emp - 4 | 4 | 5 | dept - 5 + 5 | emp - 5 | 5 | 6 | dept - 6 + 5 | emp - 5 | 5 | 3 | dept - 3 + 5 | emp - 5 | 5 | 7 | dept - 7 + 5 | emp - 5 | 5 | 9 | dept - 9 + 5 | emp - 5 | 5 | 2 | dept - 2 + 5 | emp - 5 | 5 | 5 | dept - 5 + 5 | emp - 5 | 5 | 1 | dept - 1 + 5 | emp - 5 | 5 | 8 | dept - 8 + 5 | emp - 5 | 5 | 4 | dept - 4 + 5 | emp - 5 | 5 | 10 | dept - 10 + 6 | emp - 6 | 6 | 1 | dept - 1 + 6 | emp - 6 | 6 | 7 | dept - 7 + 6 | emp - 6 | 6 | 5 | dept - 5 + 6 | emp - 6 | 6 | 2 | dept - 2 + 6 | emp - 6 | 6 | 9 | dept - 9 + 6 | emp - 6 | 6 | 4 | dept - 4 + 6 | emp - 6 | 6 | 10 | dept - 10 + 6 | emp - 6 | 6 | 6 | dept - 6 + 6 | emp - 6 | 6 | 8 | dept - 8 + 6 | emp - 6 | 6 | 3 | dept - 3 + 7 | emp - 7 | 7 | 4 | dept - 4 + 7 | emp - 7 | 7 | 7 | dept - 7 + 7 | emp - 7 | 7 | 2 | dept - 2 + 7 | emp - 7 | 7 | 5 | dept - 5 + 7 | emp - 7 | 7 | 3 | dept - 3 + 7 | emp - 7 | 7 | 10 | dept - 10 + 7 | emp - 7 | 7 | 6 | dept - 6 + 7 | emp - 7 | 7 | 9 | dept - 9 + 7 | emp - 7 | 7 | 1 | dept - 1 + 7 | emp - 7 | 7 | 8 | dept - 8 + 8 | emp - 8 | 8 | 10 | dept - 10 + 8 | emp - 8 | 8 | 4 | dept - 4 + 8 | emp - 8 | 8 | 2 | dept - 2 + 8 | emp - 8 | 8 | 3 | dept - 3 + 8 | emp - 8 | 8 | 6 | dept - 6 + 8 | emp - 8 | 8 | 8 | dept - 8 + 8 | emp - 8 | 8 | 5 | dept - 5 + 8 | emp - 8 | 8 | 7 | dept - 7 + 8 | emp - 8 | 8 | 9 | dept - 9 + 8 | emp - 8 | 8 | 1 | dept - 1 + 9 | emp - 9 | 9 | 10 | dept - 10 + 9 | emp - 9 | 9 | 4 | dept - 4 + 9 | emp - 9 | 9 | 5 | dept - 5 + 9 | emp - 9 | 9 | 1 | dept - 1 + 9 | emp - 9 | 9 | 6 | dept - 6 + 9 | emp - 9 | 9 | 9 | dept - 9 + 9 | emp - 9 | 9 | 8 | dept - 8 + 9 | emp - 9 | 9 | 3 | dept - 3 + 9 | emp - 9 | 9 | 2 | dept - 2 + 9 | emp - 9 | 9 | 7 | dept - 7 + 10 | emp - 10 | 10 | 4 | dept - 4 + 10 | emp - 10 | 10 | 2 | dept - 2 + 10 | emp - 10 | 10 | 7 | dept - 7 + 10 | emp - 10 | 10 | 10 | dept - 10 + 10 | emp - 10 | 10 | 9 | dept - 9 + 10 | emp - 10 | 10 | 6 | dept - 6 + 10 | emp - 10 | 10 | 5 | dept - 5 + 10 | emp - 10 | 10 | 1 | dept - 1 + 10 | emp - 10 | 10 | 3 | dept - 3 + 10 | emp - 10 | 10 | 8 | dept - 8 + 11 | emp - 11 | 1 | 8 | dept - 8 + 11 | emp - 11 | 1 | 2 | dept - 2 + 11 | emp - 11 | 1 | 9 | dept - 9 + 11 | emp - 11 | 1 | 10 | dept - 10 + 11 | emp - 11 | 1 | 6 | dept - 6 + 11 | emp - 11 | 1 | 1 | dept - 1 + 11 | emp - 11 | 1 | 3 | dept - 3 + 11 | emp - 11 | 1 | 4 | dept - 4 + 11 | emp - 11 | 1 | 5 | dept - 5 + 11 | emp - 11 | 1 | 7 | dept - 7 + 12 | emp - 12 | 2 | 4 | dept - 4 + 12 | emp - 12 | 2 | 5 | dept - 5 + 12 | emp - 12 | 2 | 8 | dept - 8 + 12 | emp - 12 | 2 | 7 | dept - 7 + 12 | emp - 12 | 2 | 3 | dept - 3 + 12 | emp - 12 | 2 | 10 | dept - 10 + 12 | emp - 12 | 2 | 6 | dept - 6 + 12 | emp - 12 | 2 | 2 | dept - 2 + 12 | emp - 12 | 2 | 1 | dept - 1 + 12 | emp - 12 | 2 | 9 | dept - 9 + 13 | emp - 13 | 3 | 3 | dept - 3 + 13 | emp - 13 | 3 | 2 | dept - 2 + 13 | emp - 13 | 3 | 5 | dept - 5 + 13 | emp - 13 | 3 | 1 | dept - 1 + 13 | emp - 13 | 3 | 8 | dept - 8 + 13 | emp - 13 | 3 | 7 | dept - 7 + 13 | emp - 13 | 3 | 10 | dept - 10 + 13 | emp - 13 | 3 | 9 | dept - 9 + 13 | emp - 13 | 3 | 6 | dept - 6 + 13 | emp - 13 | 3 | 4 | dept - 4 + 14 | emp - 14 | 4 | 8 | dept - 8 + 14 | emp - 14 | 4 | 9 | dept - 9 + 14 | emp - 14 | 4 | 3 | dept - 3 + 14 | emp - 14 | 4 | 6 | dept - 6 + 14 | emp - 14 | 4 | 1 | dept - 1 + 14 | emp - 14 | 4 | 4 | dept - 4 + 14 | emp - 14 | 4 | 10 | dept - 10 + 14 | emp - 14 | 4 | 5 | dept - 5 + 14 | emp - 14 | 4 | 7 | dept - 7 + 14 | emp - 14 | 4 | 2 | dept - 2 + 15 | emp - 15 | 5 | 9 | dept - 9 + 15 | emp - 15 | 5 | 8 | dept - 8 + 15 | emp - 15 | 5 | 10 | dept - 10 + 15 | emp - 15 | 5 | 4 | dept - 4 + 15 | emp - 15 | 5 | 2 | dept - 2 + 15 | emp - 15 | 5 | 3 | dept - 3 + 15 | emp - 15 | 5 | 6 | dept - 6 + 15 | emp - 15 | 5 | 7 | dept - 7 + 15 | emp - 15 | 5 | 5 | dept - 5 + 15 | emp - 15 | 5 | 1 | dept - 1 + 16 | emp - 16 | 6 | 10 | dept - 10 + 16 | emp - 16 | 6 | 3 | dept - 3 + 16 | emp - 16 | 6 | 2 | dept - 2 + 16 | emp - 16 | 6 | 9 | dept - 9 + 16 | emp - 16 | 6 | 7 | dept - 7 + 16 | emp - 16 | 6 | 1 | dept - 1 + 16 | emp - 16 | 6 | 8 | dept - 8 + 16 | emp - 16 | 6 | 5 | dept - 5 + 16 | emp - 16 | 6 | 6 | dept - 6 + 16 | emp - 16 | 6 | 4 | dept - 4 + 17 | emp - 17 | 7 | 5 | dept - 5 + 17 | emp - 17 | 7 | 6 | dept - 6 + 17 | emp - 17 | 7 | 4 | dept - 4 + 17 | emp - 17 | 7 | 8 | dept - 8 + 17 | emp - 17 | 7 | 7 | dept - 7 + 17 | emp - 17 | 7 | 9 | dept - 9 + 17 | emp - 17 | 7 | 1 | dept - 1 + 17 | emp - 17 | 7 | 2 | dept - 2 + 17 | emp - 17 | 7 | 10 | dept - 10 + 17 | emp - 17 | 7 | 3 | dept - 3 + 18 | emp - 18 | 8 | 4 | dept - 4 + 18 | emp - 18 | 8 | 1 | dept - 1 + 18 | emp - 18 | 8 | 3 | dept - 3 + 18 | emp - 18 | 8 | 2 | dept - 2 + 18 | emp - 18 | 8 | 8 | dept - 8 + 18 | emp - 18 | 8 | 7 | dept - 7 + 18 | emp - 18 | 8 | 5 | dept - 5 + 18 | emp - 18 | 8 | 10 | dept - 10 + 18 | emp - 18 | 8 | 9 | dept - 9 + 18 | emp - 18 | 8 | 6 | dept - 6 + 19 | emp - 19 | 9 | 1 | dept - 1 + 19 | emp - 19 | 9 | 9 | dept - 9 + 19 | emp - 19 | 9 | 2 | dept - 2 + 19 | emp - 19 | 9 | 8 | dept - 8 + 19 | emp - 19 | 9 | 6 | dept - 6 + 19 | emp - 19 | 9 | 4 | dept - 4 + 19 | emp - 19 | 9 | 10 | dept - 10 + 19 | emp - 19 | 9 | 3 | dept - 3 + 19 | emp - 19 | 9 | 5 | dept - 5 + 19 | emp - 19 | 9 | 7 | dept - 7 + 20 | emp - 20 | 10 | 4 | dept - 4 + 20 | emp - 20 | 10 | 2 | dept - 2 + 20 | emp - 20 | 10 | 9 | dept - 9 + 20 | emp - 20 | 10 | 7 | dept - 7 + 20 | emp - 20 | 10 | 1 | dept - 1 + 20 | emp - 20 | 10 | 3 | dept - 3 + 20 | emp - 20 | 10 | 6 | dept - 6 + 20 | emp - 20 | 10 | 8 | dept - 8 + 20 | emp - 20 | 10 | 10 | dept - 10 + 20 | emp - 20 | 10 | 5 | dept - 5 + 21 | emp - 21 | 1 | 5 | dept - 5 + 21 | emp - 21 | 1 | 6 | dept - 6 + 21 | emp - 21 | 1 | 4 | dept - 4 + 21 | emp - 21 | 1 | 1 | dept - 1 + 21 | emp - 21 | 1 | 9 | dept - 9 + 21 | emp - 21 | 1 | 3 | dept - 3 + 21 | emp - 21 | 1 | 10 | dept - 10 + 21 | emp - 21 | 1 | 7 | dept - 7 + 21 | emp - 21 | 1 | 2 | dept - 2 + 21 | emp - 21 | 1 | 8 | dept - 8 + 22 | emp - 22 | 2 | 1 | dept - 1 + 22 | emp - 22 | 2 | 4 | dept - 4 + 22 | emp - 22 | 2 | 3 | dept - 3 + 22 | emp - 22 | 2 | 5 | dept - 5 + 22 | emp - 22 | 2 | 6 | dept - 6 + 22 | emp - 22 | 2 | 8 | dept - 8 + 22 | emp - 22 | 2 | 10 | dept - 10 + 22 | emp - 22 | 2 | 7 | dept - 7 + 22 | emp - 22 | 2 | 9 | dept - 9 + 22 | emp - 22 | 2 | 2 | dept - 2 + 23 | emp - 23 | 3 | 5 | dept - 5 + 23 | emp - 23 | 3 | 9 | dept - 9 + 23 | emp - 23 | 3 | 1 | dept - 1 + 23 | emp - 23 | 3 | 6 | dept - 6 + 23 | emp - 23 | 3 | 8 | dept - 8 + 23 | emp - 23 | 3 | 7 | dept - 7 + 23 | emp - 23 | 3 | 10 | dept - 10 + 23 | emp - 23 | 3 | 3 | dept - 3 + 23 | emp - 23 | 3 | 2 | dept - 2 + 23 | emp - 23 | 3 | 4 | dept - 4 + 24 | emp - 24 | 4 | 4 | dept - 4 + 24 | emp - 24 | 4 | 9 | dept - 9 + 24 | emp - 24 | 4 | 7 | dept - 7 + 24 | emp - 24 | 4 | 5 | dept - 5 + 24 | emp - 24 | 4 | 2 | dept - 2 + 24 | emp - 24 | 4 | 8 | dept - 8 + 24 | emp - 24 | 4 | 3 | dept - 3 + 24 | emp - 24 | 4 | 1 | dept - 1 + 24 | emp - 24 | 4 | 6 | dept - 6 + 24 | emp - 24 | 4 | 10 | dept - 10 + 25 | emp - 25 | 5 | 5 | dept - 5 + 25 | emp - 25 | 5 | 10 | dept - 10 + 25 | emp - 25 | 5 | 8 | dept - 8 + 25 | emp - 25 | 5 | 7 | dept - 7 + 25 | emp - 25 | 5 | 3 | dept - 3 + 25 | emp - 25 | 5 | 6 | dept - 6 + 25 | emp - 25 | 5 | 2 | dept - 2 + 25 | emp - 25 | 5 | 9 | dept - 9 + 25 | emp - 25 | 5 | 1 | dept - 1 + 25 | emp - 25 | 5 | 4 | dept - 4 + 26 | emp - 26 | 6 | 8 | dept - 8 + 26 | emp - 26 | 6 | 10 | dept - 10 + 26 | emp - 26 | 6 | 6 | dept - 6 + 26 | emp - 26 | 6 | 4 | dept - 4 + 26 | emp - 26 | 6 | 7 | dept - 7 + 26 | emp - 26 | 6 | 5 | dept - 5 + 26 | emp - 26 | 6 | 3 | dept - 3 + 26 | emp - 26 | 6 | 1 | dept - 1 + 26 | emp - 26 | 6 | 9 | dept - 9 + 26 | emp - 26 | 6 | 2 | dept - 2 + 27 | emp - 27 | 7 | 4 | dept - 4 + 27 | emp - 27 | 7 | 7 | dept - 7 + 27 | emp - 27 | 7 | 2 | dept - 2 + 27 | emp - 27 | 7 | 6 | dept - 6 + 27 | emp - 27 | 7 | 8 | dept - 8 + 27 | emp - 27 | 7 | 3 | dept - 3 + 27 | emp - 27 | 7 | 1 | dept - 1 + 27 | emp - 27 | 7 | 10 | dept - 10 + 27 | emp - 27 | 7 | 9 | dept - 9 + 27 | emp - 27 | 7 | 5 | dept - 5 + 28 | emp - 28 | 8 | 5 | dept - 5 + 28 | emp - 28 | 8 | 2 | dept - 2 + 28 | emp - 28 | 8 | 9 | dept - 9 + 28 | emp - 28 | 8 | 4 | dept - 4 + 28 | emp - 28 | 8 | 7 | dept - 7 + 28 | emp - 28 | 8 | 3 | dept - 3 + 28 | emp - 28 | 8 | 1 | dept - 1 + 28 | emp - 28 | 8 | 8 | dept - 8 + 28 | emp - 28 | 8 | 6 | dept - 6 + 28 | emp - 28 | 8 | 10 | dept - 10 + 29 | emp - 29 | 9 | 10 | dept - 10 + 29 | emp - 29 | 9 | 9 | dept - 9 + 29 | emp - 29 | 9 | 6 | dept - 6 + 29 | emp - 29 | 9 | 4 | dept - 4 + 29 | emp - 29 | 9 | 3 | dept - 3 + 29 | emp - 29 | 9 | 8 | dept - 8 + 29 | emp - 29 | 9 | 5 | dept - 5 + 29 | emp - 29 | 9 | 7 | dept - 7 + 29 | emp - 29 | 9 | 2 | dept - 2 + 29 | emp - 29 | 9 | 1 | dept - 1 + 30 | emp - 30 | 10 | 3 | dept - 3 + 30 | emp - 30 | 10 | 9 | dept - 9 + 30 | emp - 30 | 10 | 1 | dept - 1 + 30 | emp - 30 | 10 | 6 | dept - 6 + 30 | emp - 30 | 10 | 5 | dept - 5 + 30 | emp - 30 | 10 | 10 | dept - 10 + 30 | emp - 30 | 10 | 8 | dept - 8 + 30 | emp - 30 | 10 | 7 | dept - 7 + 30 | emp - 30 | 10 | 4 | dept - 4 + 30 | emp - 30 | 10 | 2 | dept - 2 + 31 | emp - 31 | 1 | 1 | dept - 1 + 31 | emp - 31 | 1 | 2 | dept - 2 + 31 | emp - 31 | 1 | 8 | dept - 8 + 31 | emp - 31 | 1 | 5 | dept - 5 + 31 | emp - 31 | 1 | 6 | dept - 6 + 31 | emp - 31 | 1 | 10 | dept - 10 + 31 | emp - 31 | 1 | 3 | dept - 3 + 31 | emp - 31 | 1 | 7 | dept - 7 + 31 | emp - 31 | 1 | 9 | dept - 9 + 31 | emp - 31 | 1 | 4 | dept - 4 + 32 | emp - 32 | 2 | 10 | dept - 10 + 32 | emp - 32 | 2 | 9 | dept - 9 + 32 | emp - 32 | 2 | 8 | dept - 8 + 32 | emp - 32 | 2 | 2 | dept - 2 + 32 | emp - 32 | 2 | 6 | dept - 6 + 32 | emp - 32 | 2 | 1 | dept - 1 + 32 | emp - 32 | 2 | 7 | dept - 7 + 32 | emp - 32 | 2 | 3 | dept - 3 + 32 | emp - 32 | 2 | 5 | dept - 5 + 32 | emp - 32 | 2 | 4 | dept - 4 + 33 | emp - 33 | 3 | 10 | dept - 10 + 33 | emp - 33 | 3 | 5 | dept - 5 + 33 | emp - 33 | 3 | 8 | dept - 8 + 33 | emp - 33 | 3 | 3 | dept - 3 + 33 | emp - 33 | 3 | 6 | dept - 6 + 33 | emp - 33 | 3 | 7 | dept - 7 + 33 | emp - 33 | 3 | 2 | dept - 2 + 33 | emp - 33 | 3 | 1 | dept - 1 + 33 | emp - 33 | 3 | 4 | dept - 4 + 33 | emp - 33 | 3 | 9 | dept - 9 + 34 | emp - 34 | 4 | 9 | dept - 9 + 34 | emp - 34 | 4 | 6 | dept - 6 + 34 | emp - 34 | 4 | 8 | dept - 8 + 34 | emp - 34 | 4 | 4 | dept - 4 + 34 | emp - 34 | 4 | 3 | dept - 3 + 34 | emp - 34 | 4 | 5 | dept - 5 + 34 | emp - 34 | 4 | 10 | dept - 10 + 34 | emp - 34 | 4 | 7 | dept - 7 + 34 | emp - 34 | 4 | 1 | dept - 1 + 34 | emp - 34 | 4 | 2 | dept - 2 + 35 | emp - 35 | 5 | 4 | dept - 4 + 35 | emp - 35 | 5 | 9 | dept - 9 + 35 | emp - 35 | 5 | 5 | dept - 5 + 35 | emp - 35 | 5 | 2 | dept - 2 + 35 | emp - 35 | 5 | 3 | dept - 3 + 35 | emp - 35 | 5 | 7 | dept - 7 + 35 | emp - 35 | 5 | 8 | dept - 8 + 35 | emp - 35 | 5 | 10 | dept - 10 + 35 | emp - 35 | 5 | 6 | dept - 6 + 35 | emp - 35 | 5 | 1 | dept - 1 + 36 | emp - 36 | 6 | 7 | dept - 7 + 36 | emp - 36 | 6 | 3 | dept - 3 + 36 | emp - 36 | 6 | 8 | dept - 8 + 36 | emp - 36 | 6 | 1 | dept - 1 + 36 | emp - 36 | 6 | 6 | dept - 6 + 36 | emp - 36 | 6 | 10 | dept - 10 + 36 | emp - 36 | 6 | 4 | dept - 4 + 36 | emp - 36 | 6 | 9 | dept - 9 + 36 | emp - 36 | 6 | 2 | dept - 2 + 36 | emp - 36 | 6 | 5 | dept - 5 + 37 | emp - 37 | 7 | 2 | dept - 2 + 37 | emp - 37 | 7 | 4 | dept - 4 + 37 | emp - 37 | 7 | 10 | dept - 10 + 37 | emp - 37 | 7 | 7 | dept - 7 + 37 | emp - 37 | 7 | 6 | dept - 6 + 37 | emp - 37 | 7 | 9 | dept - 9 + 37 | emp - 37 | 7 | 3 | dept - 3 + 37 | emp - 37 | 7 | 8 | dept - 8 + 37 | emp - 37 | 7 | 5 | dept - 5 + 37 | emp - 37 | 7 | 1 | dept - 1 + 38 | emp - 38 | 8 | 1 | dept - 1 + 38 | emp - 38 | 8 | 8 | dept - 8 + 38 | emp - 38 | 8 | 4 | dept - 4 + 38 | emp - 38 | 8 | 3 | dept - 3 + 38 | emp - 38 | 8 | 6 | dept - 6 + 38 | emp - 38 | 8 | 7 | dept - 7 + 38 | emp - 38 | 8 | 2 | dept - 2 + 38 | emp - 38 | 8 | 5 | dept - 5 + 38 | emp - 38 | 8 | 9 | dept - 9 + 38 | emp - 38 | 8 | 10 | dept - 10 + 39 | emp - 39 | 9 | 6 | dept - 6 + 39 | emp - 39 | 9 | 1 | dept - 1 + 39 | emp - 39 | 9 | 5 | dept - 5 + 39 | emp - 39 | 9 | 7 | dept - 7 + 39 | emp - 39 | 9 | 2 | dept - 2 + 39 | emp - 39 | 9 | 3 | dept - 3 + 39 | emp - 39 | 9 | 9 | dept - 9 + 39 | emp - 39 | 9 | 4 | dept - 4 + 39 | emp - 39 | 9 | 10 | dept - 10 + 39 | emp - 39 | 9 | 8 | dept - 8 + 40 | emp - 40 | 10 | 3 | dept - 3 + 40 | emp - 40 | 10 | 5 | dept - 5 + 40 | emp - 40 | 10 | 1 | dept - 1 + 40 | emp - 40 | 10 | 10 | dept - 10 + 40 | emp - 40 | 10 | 4 | dept - 4 + 40 | emp - 40 | 10 | 9 | dept - 9 + 40 | emp - 40 | 10 | 7 | dept - 7 + 40 | emp - 40 | 10 | 2 | dept - 2 + 40 | emp - 40 | 10 | 8 | dept - 8 + 40 | emp - 40 | 10 | 6 | dept - 6 + 41 | emp - 41 | 1 | 7 | dept - 7 + 41 | emp - 41 | 1 | 5 | dept - 5 + 41 | emp - 41 | 1 | 6 | dept - 6 + 41 | emp - 41 | 1 | 10 | dept - 10 + 41 | emp - 41 | 1 | 1 | dept - 1 + 41 | emp - 41 | 1 | 8 | dept - 8 + 41 | emp - 41 | 1 | 3 | dept - 3 + 41 | emp - 41 | 1 | 4 | dept - 4 + 41 | emp - 41 | 1 | 9 | dept - 9 + 41 | emp - 41 | 1 | 2 | dept - 2 + 42 | emp - 42 | 2 | 10 | dept - 10 + 42 | emp - 42 | 2 | 4 | dept - 4 + 42 | emp - 42 | 2 | 7 | dept - 7 + 42 | emp - 42 | 2 | 1 | dept - 1 + 42 | emp - 42 | 2 | 3 | dept - 3 + 42 | emp - 42 | 2 | 8 | dept - 8 + 42 | emp - 42 | 2 | 5 | dept - 5 + 42 | emp - 42 | 2 | 2 | dept - 2 + 42 | emp - 42 | 2 | 9 | dept - 9 + 42 | emp - 42 | 2 | 6 | dept - 6 + 43 | emp - 43 | 3 | 9 | dept - 9 + 43 | emp - 43 | 3 | 3 | dept - 3 + 43 | emp - 43 | 3 | 4 | dept - 4 + 43 | emp - 43 | 3 | 2 | dept - 2 + 43 | emp - 43 | 3 | 10 | dept - 10 + 43 | emp - 43 | 3 | 1 | dept - 1 + 43 | emp - 43 | 3 | 7 | dept - 7 + 43 | emp - 43 | 3 | 8 | dept - 8 + 43 | emp - 43 | 3 | 6 | dept - 6 + 43 | emp - 43 | 3 | 5 | dept - 5 + 44 | emp - 44 | 4 | 3 | dept - 3 + 44 | emp - 44 | 4 | 9 | dept - 9 + 44 | emp - 44 | 4 | 5 | dept - 5 + 44 | emp - 44 | 4 | 10 | dept - 10 + 44 | emp - 44 | 4 | 6 | dept - 6 + 44 | emp - 44 | 4 | 8 | dept - 8 + 44 | emp - 44 | 4 | 7 | dept - 7 + 44 | emp - 44 | 4 | 4 | dept - 4 + 44 | emp - 44 | 4 | 2 | dept - 2 + 44 | emp - 44 | 4 | 1 | dept - 1 + 45 | emp - 45 | 5 | 9 | dept - 9 + 45 | emp - 45 | 5 | 3 | dept - 3 + 45 | emp - 45 | 5 | 7 | dept - 7 + 45 | emp - 45 | 5 | 1 | dept - 1 + 45 | emp - 45 | 5 | 5 | dept - 5 + 45 | emp - 45 | 5 | 2 | dept - 2 + 45 | emp - 45 | 5 | 10 | dept - 10 + 45 | emp - 45 | 5 | 6 | dept - 6 + 45 | emp - 45 | 5 | 8 | dept - 8 + 45 | emp - 45 | 5 | 4 | dept - 4 + 46 | emp - 46 | 6 | 3 | dept - 3 + 46 | emp - 46 | 6 | 4 | dept - 4 + 46 | emp - 46 | 6 | 6 | dept - 6 + 46 | emp - 46 | 6 | 2 | dept - 2 + 46 | emp - 46 | 6 | 1 | dept - 1 + 46 | emp - 46 | 6 | 7 | dept - 7 + 46 | emp - 46 | 6 | 8 | dept - 8 + 46 | emp - 46 | 6 | 9 | dept - 9 + 46 | emp - 46 | 6 | 5 | dept - 5 + 46 | emp - 46 | 6 | 10 | dept - 10 + 47 | emp - 47 | 7 | 8 | dept - 8 + 47 | emp - 47 | 7 | 10 | dept - 10 + 47 | emp - 47 | 7 | 5 | dept - 5 + 47 | emp - 47 | 7 | 7 | dept - 7 + 47 | emp - 47 | 7 | 9 | dept - 9 + 47 | emp - 47 | 7 | 2 | dept - 2 + 47 | emp - 47 | 7 | 1 | dept - 1 + 47 | emp - 47 | 7 | 6 | dept - 6 + 47 | emp - 47 | 7 | 4 | dept - 4 + 47 | emp - 47 | 7 | 3 | dept - 3 + 48 | emp - 48 | 8 | 1 | dept - 1 + 48 | emp - 48 | 8 | 6 | dept - 6 + 48 | emp - 48 | 8 | 10 | dept - 10 + 48 | emp - 48 | 8 | 8 | dept - 8 + 48 | emp - 48 | 8 | 3 | dept - 3 + 48 | emp - 48 | 8 | 5 | dept - 5 + 48 | emp - 48 | 8 | 9 | dept - 9 + 48 | emp - 48 | 8 | 4 | dept - 4 + 48 | emp - 48 | 8 | 7 | dept - 7 + 48 | emp - 48 | 8 | 2 | dept - 2 + 49 | emp - 49 | 9 | 7 | dept - 7 + 49 | emp - 49 | 9 | 6 | dept - 6 + 49 | emp - 49 | 9 | 9 | dept - 9 + 49 | emp - 49 | 9 | 10 | dept - 10 + 49 | emp - 49 | 9 | 3 | dept - 3 + 49 | emp - 49 | 9 | 4 | dept - 4 + 49 | emp - 49 | 9 | 2 | dept - 2 + 49 | emp - 49 | 9 | 8 | dept - 8 + 49 | emp - 49 | 9 | 1 | dept - 1 + 49 | emp - 49 | 9 | 5 | dept - 5 + 50 | emp - 50 | 10 | 1 | dept - 1 + 50 | emp - 50 | 10 | 3 | dept - 3 + 50 | emp - 50 | 10 | 8 | dept - 8 + 50 | emp - 50 | 10 | 7 | dept - 7 + 50 | emp - 50 | 10 | 6 | dept - 6 + 50 | emp - 50 | 10 | 4 | dept - 4 + 50 | emp - 50 | 10 | 10 | dept - 10 + 50 | emp - 50 | 10 | 5 | dept - 5 + 50 | emp - 50 | 10 | 9 | dept - 9 + 50 | emp - 50 | 10 | 2 | dept - 2 + 51 | emp - 51 | 1 | 1 | dept - 1 + 51 | emp - 51 | 1 | 5 | dept - 5 + 51 | emp - 51 | 1 | 8 | dept - 8 + 51 | emp - 51 | 1 | 3 | dept - 3 + 51 | emp - 51 | 1 | 7 | dept - 7 + 51 | emp - 51 | 1 | 9 | dept - 9 + 51 | emp - 51 | 1 | 6 | dept - 6 + 51 | emp - 51 | 1 | 10 | dept - 10 + 51 | emp - 51 | 1 | 2 | dept - 2 + 51 | emp - 51 | 1 | 4 | dept - 4 + 52 | emp - 52 | 2 | 9 | dept - 9 + 52 | emp - 52 | 2 | 3 | dept - 3 + 52 | emp - 52 | 2 | 8 | dept - 8 + 52 | emp - 52 | 2 | 4 | dept - 4 + 52 | emp - 52 | 2 | 2 | dept - 2 + 52 | emp - 52 | 2 | 7 | dept - 7 + 52 | emp - 52 | 2 | 1 | dept - 1 + 52 | emp - 52 | 2 | 5 | dept - 5 + 52 | emp - 52 | 2 | 10 | dept - 10 + 52 | emp - 52 | 2 | 6 | dept - 6 + 53 | emp - 53 | 3 | 6 | dept - 6 + 53 | emp - 53 | 3 | 5 | dept - 5 + 53 | emp - 53 | 3 | 9 | dept - 9 + 53 | emp - 53 | 3 | 8 | dept - 8 + 53 | emp - 53 | 3 | 1 | dept - 1 + 53 | emp - 53 | 3 | 10 | dept - 10 + 53 | emp - 53 | 3 | 3 | dept - 3 + 53 | emp - 53 | 3 | 4 | dept - 4 + 53 | emp - 53 | 3 | 2 | dept - 2 + 53 | emp - 53 | 3 | 7 | dept - 7 + 54 | emp - 54 | 4 | 7 | dept - 7 + 54 | emp - 54 | 4 | 3 | dept - 3 + 54 | emp - 54 | 4 | 8 | dept - 8 + 54 | emp - 54 | 4 | 2 | dept - 2 + 54 | emp - 54 | 4 | 4 | dept - 4 + 54 | emp - 54 | 4 | 5 | dept - 5 + 54 | emp - 54 | 4 | 9 | dept - 9 + 54 | emp - 54 | 4 | 1 | dept - 1 + 54 | emp - 54 | 4 | 10 | dept - 10 + 54 | emp - 54 | 4 | 6 | dept - 6 + 55 | emp - 55 | 5 | 1 | dept - 1 + 55 | emp - 55 | 5 | 4 | dept - 4 + 55 | emp - 55 | 5 | 3 | dept - 3 + 55 | emp - 55 | 5 | 10 | dept - 10 + 55 | emp - 55 | 5 | 6 | dept - 6 + 55 | emp - 55 | 5 | 7 | dept - 7 + 55 | emp - 55 | 5 | 2 | dept - 2 + 55 | emp - 55 | 5 | 8 | dept - 8 + 55 | emp - 55 | 5 | 5 | dept - 5 + 55 | emp - 55 | 5 | 9 | dept - 9 + 56 | emp - 56 | 6 | 10 | dept - 10 + 56 | emp - 56 | 6 | 2 | dept - 2 + 56 | emp - 56 | 6 | 9 | dept - 9 + 56 | emp - 56 | 6 | 4 | dept - 4 + 56 | emp - 56 | 6 | 3 | dept - 3 + 56 | emp - 56 | 6 | 6 | dept - 6 + 56 | emp - 56 | 6 | 1 | dept - 1 + 56 | emp - 56 | 6 | 8 | dept - 8 + 56 | emp - 56 | 6 | 5 | dept - 5 + 56 | emp - 56 | 6 | 7 | dept - 7 + 57 | emp - 57 | 7 | 6 | dept - 6 + 57 | emp - 57 | 7 | 9 | dept - 9 + 57 | emp - 57 | 7 | 3 | dept - 3 + 57 | emp - 57 | 7 | 1 | dept - 1 + 57 | emp - 57 | 7 | 10 | dept - 10 + 57 | emp - 57 | 7 | 5 | dept - 5 + 57 | emp - 57 | 7 | 2 | dept - 2 + 57 | emp - 57 | 7 | 4 | dept - 4 + 57 | emp - 57 | 7 | 7 | dept - 7 + 57 | emp - 57 | 7 | 8 | dept - 8 + 58 | emp - 58 | 8 | 9 | dept - 9 + 58 | emp - 58 | 8 | 4 | dept - 4 + 58 | emp - 58 | 8 | 8 | dept - 8 + 58 | emp - 58 | 8 | 3 | dept - 3 + 58 | emp - 58 | 8 | 6 | dept - 6 + 58 | emp - 58 | 8 | 7 | dept - 7 + 58 | emp - 58 | 8 | 5 | dept - 5 + 58 | emp - 58 | 8 | 2 | dept - 2 + 58 | emp - 58 | 8 | 10 | dept - 10 + 58 | emp - 58 | 8 | 1 | dept - 1 + 59 | emp - 59 | 9 | 1 | dept - 1 + 59 | emp - 59 | 9 | 4 | dept - 4 + 59 | emp - 59 | 9 | 5 | dept - 5 + 59 | emp - 59 | 9 | 6 | dept - 6 + 59 | emp - 59 | 9 | 10 | dept - 10 + 59 | emp - 59 | 9 | 9 | dept - 9 + 59 | emp - 59 | 9 | 7 | dept - 7 + 59 | emp - 59 | 9 | 8 | dept - 8 + 59 | emp - 59 | 9 | 2 | dept - 2 + 59 | emp - 59 | 9 | 3 | dept - 3 + 60 | emp - 60 | 10 | 10 | dept - 10 + 60 | emp - 60 | 10 | 6 | dept - 6 + 60 | emp - 60 | 10 | 1 | dept - 1 + 60 | emp - 60 | 10 | 9 | dept - 9 + 60 | emp - 60 | 10 | 4 | dept - 4 + 60 | emp - 60 | 10 | 8 | dept - 8 + 60 | emp - 60 | 10 | 2 | dept - 2 + 60 | emp - 60 | 10 | 5 | dept - 5 + 60 | emp - 60 | 10 | 7 | dept - 7 + 60 | emp - 60 | 10 | 3 | dept - 3 + 61 | emp - 61 | 1 | 1 | dept - 1 + 61 | emp - 61 | 1 | 4 | dept - 4 + 61 | emp - 61 | 1 | 7 | dept - 7 + 61 | emp - 61 | 1 | 3 | dept - 3 + 61 | emp - 61 | 1 | 6 | dept - 6 + 61 | emp - 61 | 1 | 10 | dept - 10 + 61 | emp - 61 | 1 | 8 | dept - 8 + 61 | emp - 61 | 1 | 2 | dept - 2 + 61 | emp - 61 | 1 | 5 | dept - 5 + 61 | emp - 61 | 1 | 9 | dept - 9 + 62 | emp - 62 | 2 | 5 | dept - 5 + 62 | emp - 62 | 2 | 1 | dept - 1 + 62 | emp - 62 | 2 | 4 | dept - 4 + 62 | emp - 62 | 2 | 10 | dept - 10 + 62 | emp - 62 | 2 | 3 | dept - 3 + 62 | emp - 62 | 2 | 9 | dept - 9 + 62 | emp - 62 | 2 | 7 | dept - 7 + 62 | emp - 62 | 2 | 6 | dept - 6 + 62 | emp - 62 | 2 | 8 | dept - 8 + 62 | emp - 62 | 2 | 2 | dept - 2 + 63 | emp - 63 | 3 | 4 | dept - 4 + 63 | emp - 63 | 3 | 6 | dept - 6 + 63 | emp - 63 | 3 | 5 | dept - 5 + 63 | emp - 63 | 3 | 8 | dept - 8 + 63 | emp - 63 | 3 | 1 | dept - 1 + 63 | emp - 63 | 3 | 2 | dept - 2 + 63 | emp - 63 | 3 | 9 | dept - 9 + 63 | emp - 63 | 3 | 3 | dept - 3 + 63 | emp - 63 | 3 | 7 | dept - 7 + 63 | emp - 63 | 3 | 10 | dept - 10 + 64 | emp - 64 | 4 | 4 | dept - 4 + 64 | emp - 64 | 4 | 9 | dept - 9 + 64 | emp - 64 | 4 | 1 | dept - 1 + 64 | emp - 64 | 4 | 2 | dept - 2 + 64 | emp - 64 | 4 | 8 | dept - 8 + 64 | emp - 64 | 4 | 6 | dept - 6 + 64 | emp - 64 | 4 | 7 | dept - 7 + 64 | emp - 64 | 4 | 3 | dept - 3 + 64 | emp - 64 | 4 | 5 | dept - 5 + 64 | emp - 64 | 4 | 10 | dept - 10 + 65 | emp - 65 | 5 | 9 | dept - 9 + 65 | emp - 65 | 5 | 10 | dept - 10 + 65 | emp - 65 | 5 | 7 | dept - 7 + 65 | emp - 65 | 5 | 1 | dept - 1 + 65 | emp - 65 | 5 | 2 | dept - 2 + 65 | emp - 65 | 5 | 6 | dept - 6 + 65 | emp - 65 | 5 | 5 | dept - 5 + 65 | emp - 65 | 5 | 8 | dept - 8 + 65 | emp - 65 | 5 | 3 | dept - 3 + 65 | emp - 65 | 5 | 4 | dept - 4 + 66 | emp - 66 | 6 | 4 | dept - 4 + 66 | emp - 66 | 6 | 8 | dept - 8 + 66 | emp - 66 | 6 | 7 | dept - 7 + 66 | emp - 66 | 6 | 9 | dept - 9 + 66 | emp - 66 | 6 | 6 | dept - 6 + 66 | emp - 66 | 6 | 1 | dept - 1 + 66 | emp - 66 | 6 | 3 | dept - 3 + 66 | emp - 66 | 6 | 10 | dept - 10 + 66 | emp - 66 | 6 | 5 | dept - 5 + 66 | emp - 66 | 6 | 2 | dept - 2 + 67 | emp - 67 | 7 | 7 | dept - 7 + 67 | emp - 67 | 7 | 3 | dept - 3 + 67 | emp - 67 | 7 | 2 | dept - 2 + 67 | emp - 67 | 7 | 10 | dept - 10 + 67 | emp - 67 | 7 | 8 | dept - 8 + 67 | emp - 67 | 7 | 5 | dept - 5 + 67 | emp - 67 | 7 | 9 | dept - 9 + 67 | emp - 67 | 7 | 1 | dept - 1 + 67 | emp - 67 | 7 | 6 | dept - 6 + 67 | emp - 67 | 7 | 4 | dept - 4 + 68 | emp - 68 | 8 | 2 | dept - 2 + 68 | emp - 68 | 8 | 1 | dept - 1 + 68 | emp - 68 | 8 | 5 | dept - 5 + 68 | emp - 68 | 8 | 3 | dept - 3 + 68 | emp - 68 | 8 | 10 | dept - 10 + 68 | emp - 68 | 8 | 6 | dept - 6 + 68 | emp - 68 | 8 | 4 | dept - 4 + 68 | emp - 68 | 8 | 9 | dept - 9 + 68 | emp - 68 | 8 | 8 | dept - 8 + 68 | emp - 68 | 8 | 7 | dept - 7 + 69 | emp - 69 | 9 | 6 | dept - 6 + 69 | emp - 69 | 9 | 5 | dept - 5 + 69 | emp - 69 | 9 | 9 | dept - 9 + 69 | emp - 69 | 9 | 4 | dept - 4 + 69 | emp - 69 | 9 | 8 | dept - 8 + 69 | emp - 69 | 9 | 1 | dept - 1 + 69 | emp - 69 | 9 | 10 | dept - 10 + 69 | emp - 69 | 9 | 2 | dept - 2 + 69 | emp - 69 | 9 | 3 | dept - 3 + 69 | emp - 69 | 9 | 7 | dept - 7 + 70 | emp - 70 | 10 | 10 | dept - 10 + 70 | emp - 70 | 10 | 3 | dept - 3 + 70 | emp - 70 | 10 | 9 | dept - 9 + 70 | emp - 70 | 10 | 4 | dept - 4 + 70 | emp - 70 | 10 | 6 | dept - 6 + 70 | emp - 70 | 10 | 8 | dept - 8 + 70 | emp - 70 | 10 | 5 | dept - 5 + 70 | emp - 70 | 10 | 7 | dept - 7 + 70 | emp - 70 | 10 | 1 | dept - 1 + 70 | emp - 70 | 10 | 2 | dept - 2 + 71 | emp - 71 | 1 | 10 | dept - 10 + 71 | emp - 71 | 1 | 8 | dept - 8 + 71 | emp - 71 | 1 | 6 | dept - 6 + 71 | emp - 71 | 1 | 2 | dept - 2 + 71 | emp - 71 | 1 | 4 | dept - 4 + 71 | emp - 71 | 1 | 9 | dept - 9 + 71 | emp - 71 | 1 | 3 | dept - 3 + 71 | emp - 71 | 1 | 5 | dept - 5 + 71 | emp - 71 | 1 | 1 | dept - 1 + 71 | emp - 71 | 1 | 7 | dept - 7 + 72 | emp - 72 | 2 | 2 | dept - 2 + 72 | emp - 72 | 2 | 10 | dept - 10 + 72 | emp - 72 | 2 | 1 | dept - 1 + 72 | emp - 72 | 2 | 8 | dept - 8 + 72 | emp - 72 | 2 | 6 | dept - 6 + 72 | emp - 72 | 2 | 4 | dept - 4 + 72 | emp - 72 | 2 | 9 | dept - 9 + 72 | emp - 72 | 2 | 7 | dept - 7 + 72 | emp - 72 | 2 | 3 | dept - 3 + 72 | emp - 72 | 2 | 5 | dept - 5 + 73 | emp - 73 | 3 | 5 | dept - 5 + 73 | emp - 73 | 3 | 2 | dept - 2 + 73 | emp - 73 | 3 | 1 | dept - 1 + 73 | emp - 73 | 3 | 9 | dept - 9 + 73 | emp - 73 | 3 | 8 | dept - 8 + 73 | emp - 73 | 3 | 6 | dept - 6 + 73 | emp - 73 | 3 | 10 | dept - 10 + 73 | emp - 73 | 3 | 3 | dept - 3 + 73 | emp - 73 | 3 | 4 | dept - 4 + 73 | emp - 73 | 3 | 7 | dept - 7 + 74 | emp - 74 | 4 | 3 | dept - 3 + 74 | emp - 74 | 4 | 6 | dept - 6 + 74 | emp - 74 | 4 | 5 | dept - 5 + 74 | emp - 74 | 4 | 2 | dept - 2 + 74 | emp - 74 | 4 | 10 | dept - 10 + 74 | emp - 74 | 4 | 1 | dept - 1 + 74 | emp - 74 | 4 | 9 | dept - 9 + 74 | emp - 74 | 4 | 7 | dept - 7 + 74 | emp - 74 | 4 | 4 | dept - 4 + 74 | emp - 74 | 4 | 8 | dept - 8 + 75 | emp - 75 | 5 | 8 | dept - 8 + 75 | emp - 75 | 5 | 10 | dept - 10 + 75 | emp - 75 | 5 | 4 | dept - 4 + 75 | emp - 75 | 5 | 3 | dept - 3 + 75 | emp - 75 | 5 | 5 | dept - 5 + 75 | emp - 75 | 5 | 6 | dept - 6 + 75 | emp - 75 | 5 | 7 | dept - 7 + 75 | emp - 75 | 5 | 2 | dept - 2 + 75 | emp - 75 | 5 | 1 | dept - 1 + 75 | emp - 75 | 5 | 9 | dept - 9 + 76 | emp - 76 | 6 | 10 | dept - 10 + 76 | emp - 76 | 6 | 2 | dept - 2 + 76 | emp - 76 | 6 | 8 | dept - 8 + 76 | emp - 76 | 6 | 3 | dept - 3 + 76 | emp - 76 | 6 | 4 | dept - 4 + 76 | emp - 76 | 6 | 5 | dept - 5 + 76 | emp - 76 | 6 | 7 | dept - 7 + 76 | emp - 76 | 6 | 1 | dept - 1 + 76 | emp - 76 | 6 | 6 | dept - 6 + 76 | emp - 76 | 6 | 9 | dept - 9 + 77 | emp - 77 | 7 | 7 | dept - 7 + 77 | emp - 77 | 7 | 5 | dept - 5 + 77 | emp - 77 | 7 | 8 | dept - 8 + 77 | emp - 77 | 7 | 6 | dept - 6 + 77 | emp - 77 | 7 | 10 | dept - 10 + 77 | emp - 77 | 7 | 3 | dept - 3 + 77 | emp - 77 | 7 | 9 | dept - 9 + 77 | emp - 77 | 7 | 4 | dept - 4 + 77 | emp - 77 | 7 | 1 | dept - 1 + 77 | emp - 77 | 7 | 2 | dept - 2 + 78 | emp - 78 | 8 | 2 | dept - 2 + 78 | emp - 78 | 8 | 5 | dept - 5 + 78 | emp - 78 | 8 | 1 | dept - 1 + 78 | emp - 78 | 8 | 8 | dept - 8 + 78 | emp - 78 | 8 | 3 | dept - 3 + 78 | emp - 78 | 8 | 7 | dept - 7 + 78 | emp - 78 | 8 | 4 | dept - 4 + 78 | emp - 78 | 8 | 10 | dept - 10 + 78 | emp - 78 | 8 | 9 | dept - 9 + 78 | emp - 78 | 8 | 6 | dept - 6 + 79 | emp - 79 | 9 | 9 | dept - 9 + 79 | emp - 79 | 9 | 3 | dept - 3 + 79 | emp - 79 | 9 | 4 | dept - 4 + 79 | emp - 79 | 9 | 5 | dept - 5 + 79 | emp - 79 | 9 | 8 | dept - 8 + 79 | emp - 79 | 9 | 6 | dept - 6 + 79 | emp - 79 | 9 | 2 | dept - 2 + 79 | emp - 79 | 9 | 1 | dept - 1 + 79 | emp - 79 | 9 | 10 | dept - 10 + 79 | emp - 79 | 9 | 7 | dept - 7 + 80 | emp - 80 | 10 | 10 | dept - 10 + 80 | emp - 80 | 10 | 1 | dept - 1 + 80 | emp - 80 | 10 | 8 | dept - 8 + 80 | emp - 80 | 10 | 3 | dept - 3 + 80 | emp - 80 | 10 | 2 | dept - 2 + 80 | emp - 80 | 10 | 9 | dept - 9 + 80 | emp - 80 | 10 | 5 | dept - 5 + 80 | emp - 80 | 10 | 7 | dept - 7 + 80 | emp - 80 | 10 | 4 | dept - 4 + 80 | emp - 80 | 10 | 6 | dept - 6 + 81 | emp - 81 | 1 | 4 | dept - 4 + 81 | emp - 81 | 1 | 9 | dept - 9 + 81 | emp - 81 | 1 | 2 | dept - 2 + 81 | emp - 81 | 1 | 1 | dept - 1 + 81 | emp - 81 | 1 | 3 | dept - 3 + 81 | emp - 81 | 1 | 8 | dept - 8 + 81 | emp - 81 | 1 | 7 | dept - 7 + 81 | emp - 81 | 1 | 5 | dept - 5 + 81 | emp - 81 | 1 | 10 | dept - 10 + 81 | emp - 81 | 1 | 6 | dept - 6 + 82 | emp - 82 | 2 | 5 | dept - 5 + 82 | emp - 82 | 2 | 8 | dept - 8 + 82 | emp - 82 | 2 | 9 | dept - 9 + 82 | emp - 82 | 2 | 6 | dept - 6 + 82 | emp - 82 | 2 | 10 | dept - 10 + 82 | emp - 82 | 2 | 2 | dept - 2 + 82 | emp - 82 | 2 | 4 | dept - 4 + 82 | emp - 82 | 2 | 1 | dept - 1 + 82 | emp - 82 | 2 | 3 | dept - 3 + 82 | emp - 82 | 2 | 7 | dept - 7 + 83 | emp - 83 | 3 | 2 | dept - 2 + 83 | emp - 83 | 3 | 4 | dept - 4 + 83 | emp - 83 | 3 | 6 | dept - 6 + 83 | emp - 83 | 3 | 10 | dept - 10 + 83 | emp - 83 | 3 | 5 | dept - 5 + 83 | emp - 83 | 3 | 8 | dept - 8 + 83 | emp - 83 | 3 | 1 | dept - 1 + 83 | emp - 83 | 3 | 9 | dept - 9 + 83 | emp - 83 | 3 | 7 | dept - 7 + 83 | emp - 83 | 3 | 3 | dept - 3 + 84 | emp - 84 | 4 | 7 | dept - 7 + 84 | emp - 84 | 4 | 5 | dept - 5 + 84 | emp - 84 | 4 | 10 | dept - 10 + 84 | emp - 84 | 4 | 2 | dept - 2 + 84 | emp - 84 | 4 | 9 | dept - 9 + 84 | emp - 84 | 4 | 6 | dept - 6 + 84 | emp - 84 | 4 | 3 | dept - 3 + 84 | emp - 84 | 4 | 4 | dept - 4 + 84 | emp - 84 | 4 | 1 | dept - 1 + 84 | emp - 84 | 4 | 8 | dept - 8 + 85 | emp - 85 | 5 | 6 | dept - 6 + 85 | emp - 85 | 5 | 10 | dept - 10 + 85 | emp - 85 | 5 | 2 | dept - 2 + 85 | emp - 85 | 5 | 4 | dept - 4 + 85 | emp - 85 | 5 | 1 | dept - 1 + 85 | emp - 85 | 5 | 7 | dept - 7 + 85 | emp - 85 | 5 | 5 | dept - 5 + 85 | emp - 85 | 5 | 3 | dept - 3 + 85 | emp - 85 | 5 | 9 | dept - 9 + 85 | emp - 85 | 5 | 8 | dept - 8 + 86 | emp - 86 | 6 | 6 | dept - 6 + 86 | emp - 86 | 6 | 8 | dept - 8 + 86 | emp - 86 | 6 | 7 | dept - 7 + 86 | emp - 86 | 6 | 5 | dept - 5 + 86 | emp - 86 | 6 | 3 | dept - 3 + 86 | emp - 86 | 6 | 4 | dept - 4 + 86 | emp - 86 | 6 | 1 | dept - 1 + 86 | emp - 86 | 6 | 2 | dept - 2 + 86 | emp - 86 | 6 | 10 | dept - 10 + 86 | emp - 86 | 6 | 9 | dept - 9 + 87 | emp - 87 | 7 | 2 | dept - 2 + 87 | emp - 87 | 7 | 6 | dept - 6 + 87 | emp - 87 | 7 | 7 | dept - 7 + 87 | emp - 87 | 7 | 8 | dept - 8 + 87 | emp - 87 | 7 | 4 | dept - 4 + 87 | emp - 87 | 7 | 5 | dept - 5 + 87 | emp - 87 | 7 | 1 | dept - 1 + 87 | emp - 87 | 7 | 3 | dept - 3 + 87 | emp - 87 | 7 | 9 | dept - 9 + 87 | emp - 87 | 7 | 10 | dept - 10 + 88 | emp - 88 | 8 | 4 | dept - 4 + 88 | emp - 88 | 8 | 10 | dept - 10 + 88 | emp - 88 | 8 | 6 | dept - 6 + 88 | emp - 88 | 8 | 9 | dept - 9 + 88 | emp - 88 | 8 | 1 | dept - 1 + 88 | emp - 88 | 8 | 7 | dept - 7 + 88 | emp - 88 | 8 | 2 | dept - 2 + 88 | emp - 88 | 8 | 5 | dept - 5 + 88 | emp - 88 | 8 | 8 | dept - 8 + 88 | emp - 88 | 8 | 3 | dept - 3 + 89 | emp - 89 | 9 | 8 | dept - 8 + 89 | emp - 89 | 9 | 7 | dept - 7 + 89 | emp - 89 | 9 | 9 | dept - 9 + 89 | emp - 89 | 9 | 3 | dept - 3 + 89 | emp - 89 | 9 | 6 | dept - 6 + 89 | emp - 89 | 9 | 10 | dept - 10 + 89 | emp - 89 | 9 | 4 | dept - 4 + 89 | emp - 89 | 9 | 5 | dept - 5 + 89 | emp - 89 | 9 | 1 | dept - 1 + 89 | emp - 89 | 9 | 2 | dept - 2 + 90 | emp - 90 | 10 | 9 | dept - 9 + 90 | emp - 90 | 10 | 3 | dept - 3 + 90 | emp - 90 | 10 | 1 | dept - 1 + 90 | emp - 90 | 10 | 5 | dept - 5 + 90 | emp - 90 | 10 | 8 | dept - 8 + 90 | emp - 90 | 10 | 7 | dept - 7 + 90 | emp - 90 | 10 | 10 | dept - 10 + 90 | emp - 90 | 10 | 4 | dept - 4 + 90 | emp - 90 | 10 | 2 | dept - 2 + 90 | emp - 90 | 10 | 6 | dept - 6 + 91 | emp - 91 | 1 | 10 | dept - 10 + 91 | emp - 91 | 1 | 2 | dept - 2 + 91 | emp - 91 | 1 | 9 | dept - 9 + 91 | emp - 91 | 1 | 4 | dept - 4 + 91 | emp - 91 | 1 | 5 | dept - 5 + 91 | emp - 91 | 1 | 3 | dept - 3 + 91 | emp - 91 | 1 | 7 | dept - 7 + 91 | emp - 91 | 1 | 8 | dept - 8 + 91 | emp - 91 | 1 | 1 | dept - 1 + 91 | emp - 91 | 1 | 6 | dept - 6 + 92 | emp - 92 | 2 | 6 | dept - 6 + 92 | emp - 92 | 2 | 4 | dept - 4 + 92 | emp - 92 | 2 | 10 | dept - 10 + 92 | emp - 92 | 2 | 1 | dept - 1 + 92 | emp - 92 | 2 | 8 | dept - 8 + 92 | emp - 92 | 2 | 5 | dept - 5 + 92 | emp - 92 | 2 | 7 | dept - 7 + 92 | emp - 92 | 2 | 2 | dept - 2 + 92 | emp - 92 | 2 | 9 | dept - 9 + 92 | emp - 92 | 2 | 3 | dept - 3 + 93 | emp - 93 | 3 | 4 | dept - 4 + 93 | emp - 93 | 3 | 3 | dept - 3 + 93 | emp - 93 | 3 | 8 | dept - 8 + 93 | emp - 93 | 3 | 1 | dept - 1 + 93 | emp - 93 | 3 | 2 | dept - 2 + 93 | emp - 93 | 3 | 10 | dept - 10 + 93 | emp - 93 | 3 | 6 | dept - 6 + 93 | emp - 93 | 3 | 5 | dept - 5 + 93 | emp - 93 | 3 | 9 | dept - 9 + 93 | emp - 93 | 3 | 7 | dept - 7 + 94 | emp - 94 | 4 | 4 | dept - 4 + 94 | emp - 94 | 4 | 8 | dept - 8 + 94 | emp - 94 | 4 | 9 | dept - 9 + 94 | emp - 94 | 4 | 7 | dept - 7 + 94 | emp - 94 | 4 | 10 | dept - 10 + 94 | emp - 94 | 4 | 3 | dept - 3 + 94 | emp - 94 | 4 | 2 | dept - 2 + 94 | emp - 94 | 4 | 6 | dept - 6 + 94 | emp - 94 | 4 | 1 | dept - 1 + 94 | emp - 94 | 4 | 5 | dept - 5 + 95 | emp - 95 | 5 | 7 | dept - 7 + 95 | emp - 95 | 5 | 6 | dept - 6 + 95 | emp - 95 | 5 | 2 | dept - 2 + 95 | emp - 95 | 5 | 8 | dept - 8 + 95 | emp - 95 | 5 | 9 | dept - 9 + 95 | emp - 95 | 5 | 4 | dept - 4 + 95 | emp - 95 | 5 | 1 | dept - 1 + 95 | emp - 95 | 5 | 5 | dept - 5 + 95 | emp - 95 | 5 | 10 | dept - 10 + 95 | emp - 95 | 5 | 3 | dept - 3 + 96 | emp - 96 | 6 | 2 | dept - 2 + 96 | emp - 96 | 6 | 4 | dept - 4 + 96 | emp - 96 | 6 | 6 | dept - 6 + 96 | emp - 96 | 6 | 7 | dept - 7 + 96 | emp - 96 | 6 | 10 | dept - 10 + 96 | emp - 96 | 6 | 8 | dept - 8 + 96 | emp - 96 | 6 | 1 | dept - 1 + 96 | emp - 96 | 6 | 9 | dept - 9 + 96 | emp - 96 | 6 | 5 | dept - 5 + 96 | emp - 96 | 6 | 3 | dept - 3 + 97 | emp - 97 | 7 | 1 | dept - 1 + 97 | emp - 97 | 7 | 2 | dept - 2 + 97 | emp - 97 | 7 | 4 | dept - 4 + 97 | emp - 97 | 7 | 7 | dept - 7 + 97 | emp - 97 | 7 | 10 | dept - 10 + 97 | emp - 97 | 7 | 6 | dept - 6 + 97 | emp - 97 | 7 | 8 | dept - 8 + 97 | emp - 97 | 7 | 5 | dept - 5 + 97 | emp - 97 | 7 | 9 | dept - 9 + 97 | emp - 97 | 7 | 3 | dept - 3 + 98 | emp - 98 | 8 | 2 | dept - 2 + 98 | emp - 98 | 8 | 5 | dept - 5 + 98 | emp - 98 | 8 | 8 | dept - 8 + 98 | emp - 98 | 8 | 9 | dept - 9 + 98 | emp - 98 | 8 | 10 | dept - 10 + 98 | emp - 98 | 8 | 7 | dept - 7 + 98 | emp - 98 | 8 | 3 | dept - 3 + 98 | emp - 98 | 8 | 6 | dept - 6 + 98 | emp - 98 | 8 | 4 | dept - 4 + 98 | emp - 98 | 8 | 1 | dept - 1 + 99 | emp - 99 | 9 | 10 | dept - 10 + 99 | emp - 99 | 9 | 8 | dept - 8 + 99 | emp - 99 | 9 | 3 | dept - 3 + 99 | emp - 99 | 9 | 4 | dept - 4 + 99 | emp - 99 | 9 | 1 | dept - 1 + 99 | emp - 99 | 9 | 9 | dept - 9 + 99 | emp - 99 | 9 | 5 | dept - 5 + 99 | emp - 99 | 9 | 2 | dept - 2 + 99 | emp - 99 | 9 | 7 | dept - 7 + 99 | emp - 99 | 9 | 6 | dept - 6 + 100 | emp - 100 | 10 | 5 | dept - 5 + 100 | emp - 100 | 10 | 8 | dept - 8 + 100 | emp - 100 | 10 | 6 | dept - 6 + 100 | emp - 100 | 10 | 3 | dept - 3 + 100 | emp - 100 | 10 | 9 | dept - 9 + 100 | emp - 100 | 10 | 10 | dept - 10 + 100 | emp - 100 | 10 | 1 | dept - 1 + 100 | emp - 100 | 10 | 2 | dept - 2 + 100 | emp - 100 | 10 | 4 | dept - 4 + 100 | emp - 100 | 10 | 7 | dept - 7 +(1000 rows) DELETE FROM employee WHERE emp_id = 10; UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; @@ -85,28 +1168,28 @@ SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; 20 | Updated emp (1 row) -SELECT emp_id , emp_name , emp_dept_id FROM employee LIMIT 10; +SELECT emp_id , emp_name , emp_dept_id FROM employee ORDER by emp_id LIMIT 10; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 - 43 | emp - 43 | 43 - 44 | emp - 44 | 44 - 45 | emp - 45 | 45 - 46 | emp - 46 | 46 - 47 | emp - 47 | 47 + 1 | emp - 1 | 1 + 2 | emp - 2 | 2 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 + 6 | emp - 6 | 6 + 7 | emp - 7 | 7 + 8 | emp - 8 | 8 + 9 | emp - 9 | 9 + 11 | emp - 11 | 1 (10 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1) ORDER by emp_id; emp_id | emp_name | emp_dept_id --------+----------+------------- 1 | emp - 1 | 1 (1 row) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5) ORDER by emp_id; emp_id | emp_name | emp_dept_id --------+----------+------------- 1 | emp - 1 | 1 @@ -115,106 +1198,44 @@ SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5); 5 | emp - 5 | 5 (4 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000) ORDER by emp_id; emp_id | emp_name | emp_dept_id --------+----------+------------- (0 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) ORDER by emp_id LIMIT 5; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 + 2 | emp - 2 | 2 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 + 6 | emp - 6 | 6 (5 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) ORDER by emp_id LIMIT 5; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 + 2 | emp - 2 | 2 + 6 | emp - 6 | 6 + 7 | emp - 7 | 7 + 8 | emp - 8 | 8 + 9 | emp - 9 | 9 (5 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) ORDER by emp_id LIMIT 5; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 + 1 | emp - 1 | 1 + 2 | emp - 2 | 2 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 (5 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)); +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)) ORDER by emp_id; emp_id | emp_name | emp_dept_id --------+-------------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 - 43 | emp - 43 | 43 - 44 | emp - 44 | 44 - 45 | emp - 45 | 45 - 46 | emp - 46 | 46 - 47 | emp - 47 | 47 - 48 | emp - 48 | 48 - 49 | emp - 49 | 49 - 50 | emp - 50 | 50 - 51 | emp - 51 | 51 - 52 | emp - 52 | 52 - 53 | emp - 53 | 53 - 54 | emp - 54 | 54 - 55 | emp - 55 | 55 - 56 | emp - 56 | 56 - 57 | emp - 57 | 57 - 58 | emp - 58 | 58 - 59 | emp - 59 | 59 - 60 | emp - 60 | 60 - 61 | emp - 61 | 61 - 62 | emp - 62 | 62 - 63 | emp - 63 | 63 - 64 | emp - 64 | 64 - 65 | emp - 65 | 65 - 66 | emp - 66 | 66 - 67 | emp - 67 | 67 - 68 | emp - 68 | 68 - 69 | emp - 69 | 69 - 70 | emp - 70 | 70 - 71 | emp - 71 | 71 - 72 | emp - 72 | 72 - 73 | emp - 73 | 73 - 74 | emp - 74 | 74 - 75 | emp - 75 | 75 - 76 | emp - 76 | 76 - 77 | emp - 77 | 77 - 78 | emp - 78 | 78 - 79 | emp - 79 | 79 - 80 | emp - 80 | 80 - 81 | emp - 81 | 81 - 82 | emp - 82 | 82 - 83 | emp - 83 | 83 - 84 | emp - 84 | 84 - 85 | emp - 85 | 85 - 86 | emp - 86 | 86 - 87 | emp - 87 | 87 - 88 | emp - 88 | 88 - 89 | emp - 89 | 89 - 90 | emp - 90 | 90 - 91 | emp - 91 | 91 - 92 | emp - 92 | 92 - 93 | emp - 93 | 93 - 94 | emp - 94 | 94 - 95 | emp - 95 | 95 - 96 | emp - 96 | 96 - 97 | emp - 97 | 97 - 98 | emp - 98 | 98 - 99 | emp - 99 | 99 2 | emp - 2 | 2 3 | emp - 3 | 3 4 | emp - 4 | 4 @@ -223,54 +1244,116 @@ SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT 7 | emp - 7 | 7 8 | emp - 8 | 8 9 | emp - 9 | 9 - 11 | emp - 11 | 11 - 12 | emp - 12 | 12 - 13 | emp - 13 | 13 - 14 | emp - 14 | 14 - 15 | emp - 15 | 15 - 16 | emp - 16 | 16 - 17 | emp - 17 | 17 - 18 | emp - 18 | 18 - 19 | emp - 19 | 19 - 20 | Updated emp | 20 - 21 | emp - 21 | 21 - 22 | emp - 22 | 22 - 23 | emp - 23 | 23 - 24 | emp - 24 | 24 - 25 | emp - 25 | 25 - 26 | emp - 26 | 26 - 27 | emp - 27 | 27 - 28 | emp - 28 | 28 - 29 | emp - 29 | 29 - 30 | emp - 30 | 30 - 31 | emp - 31 | 31 - 32 | emp - 32 | 32 - 33 | emp - 33 | 33 - 34 | emp - 34 | 34 - 35 | emp - 35 | 35 - 36 | emp - 36 | 36 - 37 | emp - 37 | 37 - 100 | emp - 100 | 100 + 11 | emp - 11 | 1 + 12 | emp - 12 | 2 + 13 | emp - 13 | 3 + 14 | emp - 14 | 4 + 15 | emp - 15 | 5 + 16 | emp - 16 | 6 + 17 | emp - 17 | 7 + 18 | emp - 18 | 8 + 19 | emp - 19 | 9 + 20 | Updated emp | 10 + 21 | emp - 21 | 1 + 22 | emp - 22 | 2 + 23 | emp - 23 | 3 + 24 | emp - 24 | 4 + 25 | emp - 25 | 5 + 26 | emp - 26 | 6 + 27 | emp - 27 | 7 + 28 | emp - 28 | 8 + 29 | emp - 29 | 9 + 30 | emp - 30 | 10 + 31 | emp - 31 | 1 + 32 | emp - 32 | 2 + 33 | emp - 33 | 3 + 34 | emp - 34 | 4 + 35 | emp - 35 | 5 + 36 | emp - 36 | 6 + 37 | emp - 37 | 7 + 38 | emp - 38 | 8 + 39 | emp - 39 | 9 + 40 | emp - 40 | 10 + 41 | emp - 41 | 1 + 42 | emp - 42 | 2 + 43 | emp - 43 | 3 + 44 | emp - 44 | 4 + 45 | emp - 45 | 5 + 46 | emp - 46 | 6 + 47 | emp - 47 | 7 + 48 | emp - 48 | 8 + 49 | emp - 49 | 9 + 50 | emp - 50 | 10 + 51 | emp - 51 | 1 + 52 | emp - 52 | 2 + 53 | emp - 53 | 3 + 54 | emp - 54 | 4 + 55 | emp - 55 | 5 + 56 | emp - 56 | 6 + 57 | emp - 57 | 7 + 58 | emp - 58 | 8 + 59 | emp - 59 | 9 + 60 | emp - 60 | 10 + 61 | emp - 61 | 1 + 62 | emp - 62 | 2 + 63 | emp - 63 | 3 + 64 | emp - 64 | 4 + 65 | emp - 65 | 5 + 66 | emp - 66 | 6 + 67 | emp - 67 | 7 + 68 | emp - 68 | 8 + 69 | emp - 69 | 9 + 70 | emp - 70 | 10 + 71 | emp - 71 | 1 + 72 | emp - 72 | 2 + 73 | emp - 73 | 3 + 74 | emp - 74 | 4 + 75 | emp - 75 | 5 + 76 | emp - 76 | 6 + 77 | emp - 77 | 7 + 78 | emp - 78 | 8 + 79 | emp - 79 | 9 + 80 | emp - 80 | 10 + 81 | emp - 81 | 1 + 82 | emp - 82 | 2 + 83 | emp - 83 | 3 + 84 | emp - 84 | 4 + 85 | emp - 85 | 5 + 86 | emp - 86 | 6 + 87 | emp - 87 | 7 + 88 | emp - 88 | 8 + 89 | emp - 89 | 9 + 90 | emp - 90 | 10 + 91 | emp - 91 | 1 + 92 | emp - 92 | 2 + 93 | emp - 93 | 3 + 94 | emp - 94 | 4 + 95 | emp - 95 | 5 + 96 | emp - 96 | 6 + 97 | emp - 97 | 7 + 98 | emp - 98 | 8 + 99 | emp - 99 | 9 + 100 | emp - 100 | 10 (98 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') ORDER by emp_id LIMIT 5; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 + 6 | emp - 6 | 6 + 7 | emp - 7 | 7 (5 rows) -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') ORDER by emp_id LIMIT 5; emp_id | emp_name | emp_dept_id --------+----------+------------- - 38 | emp - 38 | 38 - 39 | emp - 39 | 39 - 40 | emp - 40 | 40 - 41 | emp - 41 | 41 - 42 | emp - 42 | 42 + 1 | emp - 1 | 1 + 2 | emp - 2 | 2 + 3 | emp - 3 | 3 + 4 | emp - 4 | 4 + 5 | emp - 5 | 5 (5 rows) DELETE FROM employee; diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index 9e30b0c..6c57bb2 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -1,41 +1,41 @@ CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); \! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json -CREATE USER MAPPING FOR postgres SERVER mongo_server; +CREATE USER MAPPING FOR ibrar SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); -INSERT INTO department VALUES(0, generate_series(1,100), 'dept - ' || generate_series(1,100)); -INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,100)); +INSERT INTO department VALUES(0, generate_series(1,10), 'dept - ' || generate_series(1,10)); +INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,10)); SELECT count(*) FROM department; SELECT count(*) FROM employee; -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; +EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id LIMIT 10; -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) LIMIT 10; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; DELETE FROM employee WHERE emp_id = 10; UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; -SELECT emp_id , emp_name , emp_dept_id FROM employee LIMIT 10; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1); -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5); -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000); +SELECT emp_id , emp_name , emp_dept_id FROM employee ORDER by emp_id LIMIT 10; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1) ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5) ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) ORDER by emp_id LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) ORDER by emp_id LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) ORDER by emp_id LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)); -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)) ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') ORDER by emp_id LIMIT 5; +SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') ORDER by emp_id LIMIT 5; DELETE FROM employee; DELETE FROM department; From 0550351d83ee7b8852b03fc0fe8d6b9343247b74 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 27 Feb 2015 00:59:51 +0500 Subject: [PATCH 079/239] JSON-C compilation error patch. --- json_compilation_error.patch | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 json_compilation_error.patch diff --git a/json_compilation_error.patch b/json_compilation_error.patch new file mode 100644 index 0000000..601f0a1 --- /dev/null +++ b/json_compilation_error.patch @@ -0,0 +1,17 @@ +*** json-c-json-c-0.12-20140410/json_tokener.c 2014-04-11 07:35:45.000000000 +0500 +--- json-c/json_tokener.c 2015-02-26 01:16:01.729596399 +0500 +*************** +*** 352,363 **** + + case json_tokener_state_inf: /* aka starts with 'i' */ + { +- int size; + int size_inf; + int is_negative = 0; + + printbuf_memappend_fast(tok->pb, &c, 1); +- size = json_min(tok->st_pos+1, json_null_str_len); + size_inf = json_min(tok->st_pos+1, json_inf_str_len); + char *infbuf = tok->pb->buf; + if (*infbuf == '-') +--- 352,361 ---- From 94b149eecb1b11ffc4ca090a191f37a3eb5f84fb Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 27 Feb 2015 18:47:37 +0500 Subject: [PATCH 080/239] Specify database in connection URI for meta driver. --- mongo_wrapper_meta.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index fa5d7cc..9e8d853 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -31,13 +31,13 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch char* uri = NULL; if (user && password && readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/?readPreference=%s", user, password, host, port, readPreference); + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s", user, password, host, port, databaseName, readPreference); else if (user && password) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/", user, password, host, port); + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s", user, password, host, port, databaseName); else if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%hu/?readPreference=%s", host, port, readPreference); + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s", host, port, databaseName, readPreference); else - uri = bson_strdup_printf ("mongodb://%s:%hu/", host, port); + uri = bson_strdup_printf ("mongodb://%s:%hu/%s", host, port, databaseName); client = mongoc_client_new(uri); From 7a717b509d8395a1a4fa030e0e828d98a685c7f7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 25 Aug 2015 16:13:29 +0200 Subject: [PATCH 081/239] Fix autogen.sh to help building mongo-c The script assumed that the current working dir, `.` is in the PATH. Now a full relative path is used to allow executing configure on systems where this is not the case. --- autogen.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index a8c045f..a34400a 100755 --- a/autogen.sh +++ b/autogen.sh @@ -79,7 +79,7 @@ function install_mongoc_driver { cd mongo-c-driver ./autogen.sh - configure --with-libbson=system + ./configure --with-libbson=system make install cd .. } From 0ca08708ffa8a51f138c2ebbad1e8f31b7a3a14e Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 25 Aug 2015 21:32:17 +0500 Subject: [PATCH 082/239] Error - (#10) Adding JSON support while using Mongo Meta driver. JSON datatype was previously only supported with Mongo's legacy driver. The wrapper for meta driver was not providing implementation of JsonToBsonAppendElement function. This patch adds complete implementation of this function. --- Makefile.meta | 7 ++-- mongo_fdw.c | 12 +++++-- mongo_wrapper_meta.c | 78 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/Makefile.meta b/Makefile.meta index a74e384..fbd483c 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -21,7 +21,8 @@ MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) -DMETA_DRIVER SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) -OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o +OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o $(LIBJSON_OBJS) + EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql @@ -42,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) - $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) + $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) endif diff --git a/mongo_fdw.c b/mongo_fdw.c index 7853f91..e3208a3 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -373,7 +373,11 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, NIL, /* no expressions to evaluate */ - foreignPrivateList); + foreignPrivateList +#if PG_VERSION_NUM >= 90500 + ,NIL +#endif + ); return foreignScan; } @@ -632,7 +636,11 @@ MongoPlanForeignModify(PlannerInfo *root, } else if (operation == CMD_UPDATE) { - Bitmapset *tmpset = bms_copy(rte->modifiedCols); +#if PG_VERSION_NUM >= 90500 + Bitmapset *tmpset = bms_copy(rte->updatedCols); +#else + Bitmapset *tmpset = bms_copy(rte->modifiedCols); +#endif AttrNumber col; while ((col = bms_first_member(tmpset)) >= 0) diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 9e8d853..8f12180 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -410,14 +410,86 @@ BsonFinish(BSON* b) bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) { - elog(ERROR, "JSON support for Meta Driver not implemented"); + bool status; + + 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 = NULL; + 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[10]; + BSON t; + BsonAppendStartArray(bb ,k, &t); + for (i = 0; i Date: Fri, 11 Sep 2015 17:14:30 +0500 Subject: [PATCH 083/239] Error (#36) Fix a crash while using nested field --- autogen.sh | 1 - mongo_fdw.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index a34400a..9a942de 100755 --- a/autogen.sh +++ b/autogen.sh @@ -78,7 +78,6 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver - ./autogen.sh ./configure --with-libbson=system make install cd .. diff --git a/mongo_fdw.c b/mongo_fdw.c index e3208a3..d3105f7 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1185,7 +1185,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, } /* recurse into nested objects */ - if (bsonType == BSON_TYPE_DOCUMENT && columnTypeId != JSONOID) + if (bsonType == BSON_TYPE_DOCUMENT && columnTypeId != JSONOID && columnTypeId != 0) { BSON subObject; BsonIterSubObject(&bsonIterator, &subObject); From 179374e62f7dbc909d6870129cd5a6c747d6e5ef Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 20 Oct 2015 19:52:50 +0500 Subject: [PATCH 084/239] Error ((#38) Nested field has column type 0, so we should recurse the FillTupleSlot function. --- mongo_fdw.c | 13 ++++++++----- sql/mongo_fdw.sql | 5 +++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index d3105f7..c9d6ff6 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1185,13 +1185,16 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, } /* recurse into nested objects */ - if (bsonType == BSON_TYPE_DOCUMENT && columnTypeId != JSONOID && columnTypeId != 0) + if (bsonType == BSON_TYPE_DOCUMENT) { - BSON subObject; - BsonIterSubObject(&bsonIterator, &subObject); - FillTupleSlot(&subObject, bsonFullKey, + if (columnTypeId != JSONOID) + { + BSON subObject; + BsonIterSubObject(&bsonIterator, &subObject); + FillTupleSlot(&subObject, bsonFullKey, columnMappingHash, columnValues, columnNulls); - continue; + continue; + } } /* if no corresponding column or null BSON value, continue */ diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index 6c57bb2..fc5bd54 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -1,7 +1,8 @@ +\c postgres postgres +CREATE EXTENSION mongo_fdw; CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); \! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json - -CREATE USER MAPPING FOR ibrar SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); From b14fc7ddcac8fa744a3156cad014e0337594bd93 Mon Sep 17 00:00:00 2001 From: Brendan MacDonell Date: Fri, 27 Nov 2015 17:09:13 -0800 Subject: [PATCH 085/239] Implement BsonIterSubObject for the meta driver. Fixes #34 and #38. --- mongo_wrapper_meta.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 8f12180..a437ef2 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -212,7 +212,11 @@ BsonIterInit(BSON_ITERATOR *it, BSON *b) bool BsonIterSubObject(BSON_ITERATOR *it, BSON *b) { - /* TODO: Need to see the Meta Driver equalient for "bson_iterator_subobject" */ + const uint8_t *buffer; + uint32_t len; + + bson_iter_document(it, &len, &buffer); + bson_init_static(b, buffer, len); return true; } From 4078a8962e64fc6faeda9d7288bb8019962e2fa5 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 3 Dec 2015 13:43:47 +0500 Subject: [PATCH 086/239] Added PostgreSQL 9.5 support. --- autogen.sh | 2 +- mongo_fdw.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 9a942de..e2fabbe 100755 --- a/autogen.sh +++ b/autogen.sh @@ -78,7 +78,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver - ./configure --with-libbson=system + ./configure --with-libbson=auto make install cd .. } diff --git a/mongo_fdw.c b/mongo_fdw.c index c9d6ff6..4e23878 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -376,6 +376,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, foreignPrivateList #if PG_VERSION_NUM >= 90500 ,NIL + ,NIL #endif ); From a4ce1670c96260c9234787087d54f5d5f4a8d9dc Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 18 Jan 2016 18:06:34 +0500 Subject: [PATCH 087/239] Fix PostgreSQL 9.5 build and remove some compilation warnings. --- expected/mongo_fdw.out | 12 +++++++----- mongo_fdw.c | 31 ++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 80923f8..3a74d5e 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -1,3 +1,5 @@ +\c postgres postgres +CREATE EXTENSION mongo_fdw; CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); \! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json CREATE USER MAPPING FOR postgres SERVER mongo_server; @@ -1381,11 +1383,11 @@ _id NAME, "lastElections.date" TIMESTAMP ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); SELECT * FROM country_elections; - _id | lastElections.type | lastElections.date ---------------------------+--------------------+------------------------------ - 5381ccf9d6d81c8e8bf0434f | presedential | Sat Jan 10 10:51:01.504 1970 - 5381ccf9d6d81c8e8bf04350 | parliamentary | Fri Dec 26 22:44:00.128 1969 - 5381ccf9d6d81c8e8bf04351 | parliamentary | Wed Dec 10 05:00:43.904 1969 + _id | lastElections.type | lastElections.date +--------------------------+--------------------+-------------------------- + 5381ccf9d6d81c8e8bf0434f | presedential | Sun May 25 00:00:00 2014 + 5381ccf9d6d81c8e8bf04350 | parliamentary | Sun Oct 09 00:00:00 2011 + 5381ccf9d6d81c8e8bf04351 | parliamentary | Sun Nov 28 00:00:00 2010 (3 rows) -- diff --git a/mongo_fdw.c b/mongo_fdw.c index 4e23878..bd59ba0 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -74,9 +74,16 @@ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId); -static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId, ForeignPath *bestPath, - List *targetList, List *restrictionClauses); + +static ForeignScan * +MongoGetForeignPlan(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *best_path, + List *targetlist, + List *restrictionClauses, + Plan *outer_plan); + static void MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState); static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); @@ -319,6 +326,9 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) startupCost, totalCost, NIL, /* no pathkeys */ NULL, /* no outer rel either */ +#if PG_VERSION_NUM >= 90500 + NULL, /* no extra plan */ +#endif NIL); /* no fdw_private data */ /* add foreign path as the only possible path */ @@ -332,8 +342,14 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) * scan, but that decision isn't deterministic or visible to us. */ static ForeignScan * -MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, - ForeignPath *bestPath, List *targetList, List *restrictionClauses) +MongoGetForeignPlan(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *best_path, + List *targetList, + List *restrictionClauses, + Plan *outer_plan) + { Index scanRangeTableIndex = baserel->relid; ForeignScan *foreignScan = NULL; @@ -358,7 +374,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, * the MongoDB server-side, so we instead filter out columns on our side. */ opExpressionList = ApplicableOpExpressionList(baserel); - queryDocument = QueryDocument(foreignTableId, opExpressionList); + queryDocument = QueryDocument(foreigntableid, opExpressionList); /* we don't need to serialize column list as lists are copiable */ columnList = ColumnList(baserel); @@ -377,6 +393,7 @@ MongoGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId, #if PG_VERSION_NUM >= 90500 ,NIL ,NIL + ,NIL #endif ); @@ -1752,7 +1769,7 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) appendStringInfo(output, "%d", bson_iterator_int(&i)); break; case BSON_TYPE_INT64: - appendStringInfo(output, "%lld", (uint64_t)bson_iterator_long(&i)); + appendStringInfo(output, "%lu", (uint64_t)bson_iterator_long(&i)); break; case BSON_TYPE_TIMESTAMP: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), From 632e78c303024d3a3cbd1a1c1fc5c27784260836 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 28 Jan 2016 20:20:05 +0500 Subject: [PATCH 088/239] Error (#32) full document retrieval. This feature allows the user to retrieve the full mongo document in a single JSON column. The user would need to add a __doc json column to the foreign table and mongo_fdw will fetch the entire mongo document in this column.This feature only supported in MongoC Meta driver. --- mongo_fdw.c | 181 +++++++++++++++++-------------------------- mongo_wrapper.c | 73 +++++++++++++++-- mongo_wrapper.h | 21 ++++- mongo_wrapper_meta.c | 95 ++++++++++++++++++++++- 4 files changed, 247 insertions(+), 123 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index bd59ba0..3dcbd7f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -149,12 +149,8 @@ static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); const char * EscapeJsonString(const char *string); -#ifdef META_DRIVER -void DumpJsonObject(StringInfo buffer, BSON_ITERATOR *iter); -void DumpJsonArray(StringInfo buffer, BSON_ITERATOR *iter); -#else -void DumpJson(StringInfo buffer, const char *bsonData, bool isArray); -#endif + +void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); /* the null action object used for pure validation */ static JsonSemAction nullSemAction = @@ -393,7 +389,7 @@ MongoGetForeignPlan(PlannerInfo *root, #if PG_VERSION_NUM >= 90500 ,NIL ,NIL - ,NIL + ,NULL #endif ); @@ -1162,15 +1158,39 @@ static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) { - BSON_ITERATOR bsonIterator = { NULL, 0 }; - BsonIterInit(&bsonIterator, (BSON*)bsonDocument); + ColumnMapping* columnMapping = NULL; + bool handleFound = false; + void* hashKey = NULL; + BSON_ITERATOR bsonIterator = { NULL, 0 }; + + if (BsonIterInit(&bsonIterator, (BSON*)bsonDocument) == false) + elog(ERROR, "failed to init"); + + hashKey = "__doc"; + columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, + HASH_FIND, &handleFound); + + if (columnMapping != NULL && handleFound == true && columnValues[columnMapping->columnIndex] == 0) + { + JsonLexContext* lex = NULL; + text* result = NULL; + Datum columnValue = 0; + char *str; + + str = BsonAsJson(bsonDocument); + result = cstring_to_text_with_len(str, strlen(str)); + lex = makeJsonLexContext(result, false); + pg_parse_json(lex, &nullSemAction); + columnValue = PointerGetDatum(result); + columnValues[columnMapping->columnIndex] = columnValue; + columnNulls[columnMapping->columnIndex] = false; + } + while (BsonIterNext(&bsonIterator)) { const char *bsonKey = BsonIterKey(&bsonIterator); BSON_TYPE bsonType = BsonIterType(&bsonIterator); - - ColumnMapping *columnMapping = NULL; Oid columnTypeId = InvalidOid; Oid columnArrayTypeId = InvalidOid; bool compatibleTypes = false; @@ -1178,6 +1198,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, const char *bsonFullKey = NULL; void *hashKey = NULL; + columnMapping = NULL; if (bsonDocumentKey != NULL) { /* @@ -1197,7 +1218,8 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, hashKey = (void *) bsonFullKey; columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, HASH_FIND, &handleFound); - if (columnMapping != NULL) { + if (columnMapping != NULL) + { columnTypeId = columnMapping->columnTypeId; columnArrayTypeId = columnMapping->columnArrayTypeId; } @@ -1232,6 +1254,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, compatibleTypes = ColumnTypesCompatible(bsonType, columnTypeId); } + /* if types are incompatible, leave this column null */ if (!compatibleTypes) { @@ -1570,33 +1593,21 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) case JSONOID: { JsonLexContext *lex; - text *result; - bool is_array; - StringInfo buffer = makeStringInfo(); + 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 scolar to json"))); - } - is_array = (BSON_TYPE_ARRAY == type); #ifdef META_DRIVER - if (is_array) - { - DumpJsonArray(buffer, bsonIterator); - } - else - { - DumpJsonObject(buffer, bsonIterator); - } + /* Convert BSON to JSON value */ + BsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); #else - DumpJson(buffer, bson_iterator_value(bsonIterator), is_array); + /* Convert BSON to JSON value */ + BsonToJsonString(buffer, *bsonIterator, BSON_TYPE_ARRAY == type); #endif - result = cstring_to_text_with_len(buffer->data, buffer->len); - - /* validate it */ lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); columnValue = PointerGetDatum(result); @@ -1614,125 +1625,74 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) return columnValue; } - -/* - * DumpJson 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/ - */ -#ifdef META_DRIVER -void -DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) { - char *json; - uint32_t len; - const uint8_t *data; - BSON bson; - - bson_iter_document(iter, &len, &data); - - if (bson_init_static(&bson, data, len)) - { - if((json = bson_as_json (&bson, NULL))) - { - appendStringInfoString(output, json); - bson_free(json); - } - } -} - void -DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { - char *json; - uint32_t len; - const uint8_t *data; - BSON bson; - - bson_iter_array(iter, &len, &data); - - if (bson_init_static(&bson, data, len)) - { - if((json = bson_array_as_json (&bson, NULL))) - { - appendStringInfoString(output, json); - bson_free(json); - } - } -} -#else -void -DumpJson(StringInfo output, const char *bsonData, bool isArray) +BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) { - bson_iterator i; - const char *key; + const char *key = NULL; bool isFirstElement; - char beginSymbol, endSymbol; - bson_type t; - - - bson_iterator_from_buffer(&i, bsonData); + char beginSymbol = '{'; + char endSymbol = '}'; + BSON_TYPE t; if (isArray) { beginSymbol = '['; endSymbol = ']'; } - else + +#ifndef META_DRIVER { - beginSymbol = '{'; - endSymbol = '}'; + char *bsonData = bson_iterator_value(&i); + bson_iterator_from_buffer(&i, bsonData); } +#endif appendStringInfoChar(output, beginSymbol); isFirstElement = true; - while (bson_iterator_next(&i)) + while (BsonIterNext(&i)) { if (!isFirstElement) - { appendStringInfoChar(output, ','); - } - t = bson_iterator_type(&i); - if (t == 0) break; - key = bson_iterator_key(&i); + t = BsonIterType(&i); + if (t == 0) + break; + + key = BsonIterKey(&i); if (!isArray) - { appendStringInfo(output, "\"%s\":", key); - } switch (t) { case BSON_TYPE_DOUBLE: - appendStringInfo(output, "%f", bson_iterator_double(&i)); + appendStringInfo(output, "%f", BsonIterDouble(&i)); break; case BSON_TYPE_UTF8: appendStringInfo(output, "\"%s\"", - EscapeJsonString(bson_iterator_string(&i))); + EscapeJsonString(BsonIterString(&i))); break; case BSON_TYPE_SYMBOL: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("`symbol` BSON type is deprecated and " "unsupported"), - errhint("Symbol: %s", bson_iterator_string(&i)))); + errhint("Symbol: %s", BsonIterString(&i)))); break; case BSON_TYPE_OID: { char oidhex[25]; - bson_oid_to_string(bson_iterator_oid(&i), oidhex); + BsonOidToString(BsonIterOid(&i), (char**)&oidhex); appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); break; } case BSON_TYPE_BOOL: appendStringInfoString( - output, bson_iterator_bool(&i) ? "true" : "false"); + output, BsonIterBool(&i) ? "true" : "false"); break; case BSON_TYPE_DATE_TIME: appendStringInfo(output, "{\"$date\":%ld}", - (long int)bson_iterator_date(&i)); + (long int)BsonIterDate(&i)); break; case BSON_TYPE_BINDATA: /* It's possible to encode the data with base64 here. */ @@ -1752,13 +1712,13 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `regex` BSON type is " "not implemented"), - errhint("Regex: %s", bson_iterator_regex(&i)))); + errhint("Regex: %s", BsonIterRegex(&i)))); break; case BSON_TYPE_CODE: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for `code` BSON type is " "not implemented"), - errhint("Code: %s", bson_iterator_code(&i)))); + errhint("Code: %s", BsonIterCode(&i)))); break; case BSON_TYPE_CODEWSCOPE: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), @@ -1766,10 +1726,10 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) "type is not implemented"))); break; case BSON_TYPE_INT32: - appendStringInfo(output, "%d", bson_iterator_int(&i)); + appendStringInfo(output, "%d", BsonIterInt32(&i)); break; case BSON_TYPE_INT64: - appendStringInfo(output, "%lu", (uint64_t)bson_iterator_long(&i)); + appendStringInfo(output, "%lu", (uint64_t)BsonIterInt64(&i)); break; case BSON_TYPE_TIMESTAMP: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), @@ -1777,20 +1737,19 @@ DumpJson(StringInfo output, const char *bsonData, bool isArray) "and unsupported"))); break; case BSON_TYPE_DOCUMENT: - DumpJson(output, bson_iterator_value(&i), false); + BsonToJsonString(output, i, false); break; case BSON_TYPE_ARRAY: - DumpJson(output, bson_iterator_value(&i), true); + BsonToJsonString(output, i, true); break; default: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("unsupported BSON type: %d", t))); + errmsg("unsupported BSON type: %x", t))); } isFirstElement = false; } appendStringInfoChar(output, endSymbol); } -#endif /* * EscapeJsonString escapes a string for safe inclusion in JSON. diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 088eb82..08da8c2 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -215,7 +215,9 @@ int BsonIterBinLen(BSON_ITERATOR *it) { return bson_iterator_bin_len(it); } -const bson_oid_t * + + +bson_oid_t * BsonIterOid(BSON_ITERATOR *it) { return bson_iterator_oid(it); @@ -229,12 +231,6 @@ BsonIterDate(BSON_ITERATOR *it) } -const char* -BsonIterKey(BSON_ITERATOR *it) -{ - return bson_iterator_key(it); -} - int BsonIterType(BSON_ITERATOR *it) { @@ -448,3 +444,66 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti return mongo_count(conn, database, collection, b); } + +void BsonIteratorFromBuffer(BSON_ITERATOR * i, const char * buffer) +{ + bson_iterator_from_buffer(i, buffer); +} + +void BsonOidToString(bson_oid_t *o, char* str[25]) +{ + bson_oid_to_string (o, str); +} + +const char* +BsonIterCode(BSON_ITERATOR *i) +{ + return bson_iterator_code (i); +} + +const char* +BsonIterRegex(BSON_ITERATOR *i) +{ + return bson_iterator_regex(i); +} + +const char* +BsonIterKey(BSON_ITERATOR *i) +{ + return bson_iterator_key(i); +} + +const char* +BsonIterValue(BSON_ITERATOR *i) +{ + return bson_iterator_value(i); +} + +void +BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) +{ + if (isArray) + DumpJsonArray(output, iter); + else + DumpJsonObject(output, iter); +} + +void +DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) +{ + +} + +void +DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) +{ + +} + +char* +BsonAsJson(const BSON* bsonDocument) +{ + elog (ERROR, "Full document retrival only available in MongoC meta driver"); +} + + diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 18e1dde..b39962f 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -61,13 +61,20 @@ const char* BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); const char* BsonIterBinData(BSON_ITERATOR *it); int BsonIterBinLen(BSON_ITERATOR *it); #endif -const bson_oid_t * BsonIterOid(BSON_ITERATOR *it); +bson_oid_t * BsonIterOid(BSON_ITERATOR *it); time_t BsonIterDate(BSON_ITERATOR *it); -const char* BsonIterKey(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(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 char* 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); @@ -86,6 +93,14 @@ bool BsonAppendFinishObject(BSON* b, BSON* r); bool BsonAppendBson(BSON* b, char *key, BSON* c); bool BsonFinish(BSON* b); 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 diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index a437ef2..46f6273 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -18,7 +18,6 @@ #include "postgres.h" #include - #include "mongo_wrapper.h" /* @@ -264,7 +263,7 @@ BsonIterBinData(BSON_ITERATOR *it, uint32_t *len) return (char*)binary; } -const bson_oid_t * +bson_oid_t * BsonIterOid(BSON_ITERATOR *it) { return bson_iter_oid(it); @@ -535,3 +534,95 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti BsonDestroy(command); return count; } + +void +BsonIteratorFromBuffer(BSON_ITERATOR *i, const char * buffer) +{ + +} + +void +BsonOidToString(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 char* +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); +} + +/* + * DumpJson 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) +{ + char *json; + uint32_t len; + const uint8_t *data = NULL; + BSON bson; + + bson_iter_document(iter, &len, &data); + if (bson_init_static(&bson, data, len)) + { + json = bson_as_json (&bson, NULL); + if (json != NULL) + { + appendStringInfoString(output, json); + bson_free(json); + } + } +} + +void +DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) +{ + char *json; + uint32_t len; + const uint8_t *data; + BSON bson; + + bson_iter_array(iter, &len, &data); + if (bson_init_static(&bson, data, len)) + { + if((json = bson_array_as_json (&bson, NULL))) + { + appendStringInfoString(output, json); + bson_free(json); + } + } +} + +char* +BsonAsJson(const BSON* bsonDocument) +{ + return bson_as_json(bsonDocument, NULL); +} From c79723035612d4a51ed9aedae0dd22f7ee5b87a6 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 28 Jan 2016 20:21:48 +0500 Subject: [PATCH 089/239] New Mongo'C Driver (mongo-c-driver-1.3.1.tar.gz) support in autogen.sh script. --- autogen.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/autogen.sh b/autogen.sh index e2fabbe..9cd1b61 100755 --- a/autogen.sh +++ b/autogen.sh @@ -25,10 +25,10 @@ fi function checkout_mongo_driver { rm -rf mongo-c-driver - wget https://github.com/mongodb/mongo-c-driver/releases/download/1.1.0/mongo-c-driver-1.1.0.tar.gz - tar -zxvf mongo-c-driver-1.1.0.tar.gz - mv mongo-c-driver-1.1.0 mongo-c-driver - rm -rf mongo-c-driver-1.1.0.tar.gz + wget https://github.com/mongodb/mongo-c-driver/releases/download/1.3.1/mongo-c-driver-1.3.1.tar.gz + tar -zxvf mongo-c-driver-1.3.1.tar.gz + mv mongo-c-driver-1.3.1 mongo-c-driver + rm -rf mongo-c-driver-1.3.1.tar.gz } ### @@ -106,7 +106,6 @@ function create_config cleanup if [ "--with-legacy" = $1 ]; then - checkout_mongo_driver checkout_json_lib checkout_legacy_branch install_json_lib From 42c0f7bf88c79827ea96f673940c3061c0cda854 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Fri, 29 Jan 2016 19:00:20 +0500 Subject: [PATCH 090/239] Error (#32) Disable INSERT and UPDATE on __doc system column. --- mongo_fdw.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 3dcbd7f..47fc94d 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -807,6 +807,9 @@ MongoExecForeignInsert(EState *estate, if (typoid != NAMEOID) elog(ERROR, "type of first column of MongoDB's foreign table must be \"NAME\""); + if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "__doc") == 0) + continue; + if (attnum == 1) { /* @@ -934,8 +937,11 @@ MongoExecForeignUpdate(EState *estate, Datum value; bool isnull; - if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) - continue; + if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + continue; + + if (strcmp("__doc", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + elog(ERROR, "system column '__doc' update is not supported"); value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER @@ -1164,7 +1170,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, BSON_ITERATOR bsonIterator = { NULL, 0 }; if (BsonIterInit(&bsonIterator, (BSON*)bsonDocument) == false) - elog(ERROR, "failed to init"); + elog(ERROR, "failed to initialize BSON iterator"); hashKey = "__doc"; columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, From 4b2947b1b570cee10f837b023c21ce2977aa45d2 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 23 May 2016 19:50:56 +0500 Subject: [PATCH 091/239] README update --- README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0c051e4..778f747 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please note that this version of `mongo_fdw` only works with -PostgreSQL Version **9.3** and greater. +Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4 and 9.5. Work is underway for certification with 9.6Beta. Installation ------------ @@ -189,16 +188,13 @@ Have a fix for a bug or an idea for a great new feature? Great! Check out the co Support ------- -This project will be modified to maintain compatibility with new PostgreSQL -releases. The project owners to set aside a day every month to look over open -issues and support emails, but are not engaged in active feature development. -Reported bugs will be addressed by apparent severity. +This project will be modified to maintain compatibility with new PostgreSQL and EDB Postgres Advanced Server releases. -As with many open source projects, you may be able to obtain support via the public mailing list (`mongo_fdw` `@` `enterprisedb.com`). If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support mongo_fdw. +If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support mongo_fdw. License ------- -Portions Copyright © 2004-2014, EnterpriseDB Corporation. +Portions Copyright © 2004-2016, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. From 28f73697337a8367b2a768b2ece7b6884767e970 Mon Sep 17 00:00:00 2001 From: gael Magnan Date: Tue, 25 Oct 2016 11:18:37 +0200 Subject: [PATCH 092/239] Added support for ssl in meta driver, also cleaned a duplicated line. --- README.md | 9 ++++++++- autogen.sh | 3 +-- connection.c | 4 ++-- mongo_fdw.c | 17 ++++++++--------- mongo_fdw.h | 23 ++++++++++++++++++++++- mongo_wrapper.h | 3 ++- mongo_wrapper_meta.c | 25 +++++++++++++++++++------ option.c | 22 +++++++++++++++++++++- 8 files changed, 83 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 778f747..69227e7 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,14 @@ The following parameters can be set on a MongoDB foreign server object: * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` * **`port`**: the port number of the MongoDB server. Defaults to `27017` - * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary' + * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary` + * **`ssl`**: false [default], true to enable ssl (meta driver only). See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to understand the options. + * **`pem_file`**: SSL option; + * **`pem_pwd`**: SSL option; + * **`ca_file`**: SSL option; + * **`ca_dir`**: SSL option; + * **`crl_file`**: SSL option; + * **`weak_cert_validation`**: SSL option; The following parameters can be set on a MongoDB foreign table object: diff --git a/autogen.sh b/autogen.sh index 9cd1b61..55b0cfe 100755 --- a/autogen.sh +++ b/autogen.sh @@ -78,7 +78,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver - ./configure --with-libbson=auto + ./configure --with-libbson=auto --enable-ssl make install cd .. } @@ -123,4 +123,3 @@ elif [ "--with-master" == $1 ]; then else echo "Usage: autogen.sh --[with-legacy | with-master]" fi - diff --git a/connection.c b/connection.c index 2c6e911..bbd81aa 100644 --- a/connection.c +++ b/connection.c @@ -99,7 +99,8 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * if (entry->conn == NULL) { #ifdef META_DRIVER - entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->readPreference); + entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->readPreference, + opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); #else entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); #endif @@ -146,4 +147,3 @@ mongo_release_connection(MONGO_CONN *conn) * cleanup on the backend exit. */ } - diff --git a/mongo_fdw.c b/mongo_fdw.c index 47fc94d..909b3ea 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -189,7 +189,6 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->IterateForeignScan = MongoIterateForeignScan; fdwRoutine->ReScanForeignScan = MongoReScanForeignScan; fdwRoutine->EndForeignScan = MongoEndForeignScan; - fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; /* support for insert / update / delete */ fdwRoutine->ExecForeignInsert = MongoExecForeignInsert; @@ -286,7 +285,7 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) 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 @@ -328,7 +327,7 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) NIL); /* no fdw_private data */ /* add foreign path as the only possible path */ - add_path(baserel, foreignPath); + add_path(baserel, foreignPath); } @@ -936,7 +935,7 @@ MongoExecForeignUpdate(EState *estate, int attnum = lfirst_int(lc); Datum value; bool isnull; - + if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) continue; @@ -1157,7 +1156,7 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) * 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 + * The bsonDocumentKey parameter is used for recursion, and should always be * passed as NULL. */ static void @@ -1176,7 +1175,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, HASH_FIND, &handleFound); - if (columnMapping != NULL && handleFound == true && columnValues[columnMapping->columnIndex] == 0) + if (columnMapping != NULL && handleFound == true && columnValues[columnMapping->columnIndex] == 0) { JsonLexContext* lex = NULL; text* result = NULL; @@ -1563,7 +1562,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) value = (char*) BsonIterOid(bsonIterator); value_len = 12; break; - default: + default: value = (char*)BsonIterBinData(bsonIterator, (uint32_t *)&value_len); break; } @@ -1978,7 +1977,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, randomState = anl_init_selection_state(targetRowCount); columnValues = (Datum *) palloc0(columnCount * sizeof(Datum)); - columnNulls = (bool *) palloc0(columnCount * sizeof(bool)); + columnNulls = (bool *) palloc0(columnCount * sizeof(bool)); for (;;) { @@ -2075,7 +2074,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, /* clean up */ MemoryContextDelete(tupleContext); MongoFreeScanState(fmstate); - + pfree(columnValues); pfree(columnNulls); diff --git a/mongo_fdw.h b/mongo_fdw.h index 671ceba..e18d8c1 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -150,6 +150,13 @@ #define OPTION_NAME_PASSWORD "password" #ifdef META_DRIVER #define OPTION_NAME_READ_PREFERENCE "read_preference" +#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" #endif /* Default values for option parameters */ @@ -181,7 +188,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 7; +static const uint32 ValidOptionCount = 14; #else static const uint32 ValidOptionCount = 6; #endif @@ -193,6 +200,13 @@ static const MongoValidOption ValidOptionArray[] = #ifdef META_DRIVER { OPTION_NAME_READ_PREFERENCE, 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 }, #endif /* foreign table options */ @@ -221,6 +235,13 @@ typedef struct MongoFdwOptions char *svr_password; #ifdef META_DRIVER char *readPreference; + bool ssl; + char *pem_file; + char *pem_pwd; + char *ca_file; + char *ca_dir; + char *crl_file; + bool weak_cert_validation; #endif } MongoFdwOptions; diff --git a/mongo_wrapper.h b/mongo_wrapper.h index b39962f..d0b3872 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -31,7 +31,8 @@ #include #ifdef META_DRIVER -MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, char *readPreference); +MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, char *readPreference, + bool ssl, char *pem_file, char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation); #else MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); #endif diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 46f6273..4abc915 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -24,22 +24,35 @@ * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN* -MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, char *readPreference) +MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, char *readPreference, bool ssl, char *pem_file, + char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) { MONGO_CONN *client = NULL; char* uri = NULL; if (user && password && readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s", user, password, host, port, databaseName, readPreference); + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); else if (user && password) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s", user, password, host, port, databaseName); + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); else if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s", host, port, databaseName, readPreference); + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); else - uri = bson_strdup_printf ("mongodb://%s:%hu/%s", host, port, databaseName); + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s", host, port, databaseName, ssl ? "true" : "false"); client = mongoc_client_new(uri); + if (ssl) { + mongoc_ssl_opt_t *ssl_opts = (mongoc_ssl_opt_t*) malloc(sizeof(mongoc_ssl_opt_t)); + ssl_opts->pem_file = pem_file; + ssl_opts->pem_pwd = pem_pwd; + ssl_opts->ca_file = ca_file; + ssl_opts->ca_dir = ca_dir; + ssl_opts->crl_file = crl_file; + ssl_opts->weak_cert_validation = weak_cert_validation; + mongoc_client_set_ssl_opts (client, ssl_opts); + free(ssl_opts); + } + bson_free(uri); if (client == NULL) @@ -365,7 +378,7 @@ BsonAppendDouble(BSON *b, const char* key, double v) bool BsonAppendUTF8(BSON *b, const char* key, char *v) { - + return bson_append_utf8(b, key, strlen(key), v, strlen(v)); } diff --git a/option.c b/option.c index 04ebbc1..4972352 100644 --- a/option.c +++ b/option.c @@ -154,8 +154,22 @@ mongo_get_options(Oid foreignTableId) char *svr_password= NULL; #ifdef META_DRIVER char *readPreference = NULL; + bool ssl = false; + char *pem_file = NULL; + char *pem_pwd = NULL; + char *ca_file = NULL; + char *ca_dir = NULL; + char *crl_file = NULL; + bool weak_cert_validation = false; readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); + ssl = mongo_get_option_value(foreignTableId, OPTION_NAME_SSL); + pem_file = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_FILE); + pem_pwd = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_PWD); + ca_file = mongo_get_option_value(foreignTableId, OPTION_NAME_CA_FILE); + ca_dir = mongo_get_option_value(foreignTableId, OPTION_NAME_CA_DIR); + crl_file = mongo_get_option_value(foreignTableId, OPTION_NAME_CRL_FILE); + weak_cert_validation = mongo_get_option_value(foreignTableId, OPTION_NAME_WEAK_CERT); #endif addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); @@ -190,6 +204,13 @@ mongo_get_options(Oid foreignTableId) #ifdef META_DRIVER options->readPreference = readPreference; + options->ssl = ssl; + options->pem_file = pem_file; + options->pem_pwd = pem_pwd; + options->ca_file = ca_file; + options->ca_dir = ca_dir; + options->crl_file = crl_file; + options->weak_cert_validation = weak_cert_validation; #endif return options; @@ -242,4 +263,3 @@ mongo_get_option_value(Oid foreignTableId, const char *optionName) } return optionValue; } - From df043d74cb04ed549e5db265ef461fa3930bea8b Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Thu, 15 Dec 2016 20:40:56 +0500 Subject: [PATCH 093/239] Issue - (#67) PostgreSQL 9.6 support. --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- README.md | 2 +- mongo_fdw.c | 18 ++++++++++++------ mongo_query.c | 11 +++++++++-- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 67e1a68..2e173d8 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) - $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) + $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 67e1a68..2e173d8 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5)) - $(error PostgreSQL 9.3, 9.4 or 9.5 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) + $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) endif diff --git a/README.md b/README.md index 778f747..ef3c096 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4 and 9.5. Work is underway for certification with 9.6Beta. +Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5 and 9.6. Work is underway for certification with 9.6Beta. Installation ------------ diff --git a/mongo_fdw.c b/mongo_fdw.c index 47fc94d..01e6783 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -318,14 +318,20 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) } /* create a foreign path node */ - foreignPath = (Path *) create_foreignscan_path(root, baserel, baserel->rows, - startupCost, totalCost, - NIL, /* no pathkeys */ - NULL, /* no outer rel either */ + + create_foreignscan_path(root, baserel, +#if PG_VERSION_NUM >= 90600 + NULL, /* default pathtarget */ +#endif + baserel->rows, + startupCost, + totalCost, + NIL, /* no pathkeys */ + NULL, /* no outer rel either */ #if PG_VERSION_NUM >= 90500 - NULL, /* no extra plan */ + NULL, /* no extra plan */ #endif - NIL); /* no fdw_private data */ + NULL); /* no fdw_private data */ /* add foreign path as the only possible path */ add_path(baserel, foreignPath); diff --git a/mongo_query.c b/mongo_query.c index 2d0b69b..6111e34 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -644,7 +644,12 @@ ColumnList(RelOptInfo *baserel) List *neededColumnList = NIL; AttrNumber columnIndex = 1; AttrNumber columnCount = baserel->max_attr; + +#if PG_VERSION_NUM >= 90600 + List *targetColumnList = baserel->reltarget->exprs; +#else List *targetColumnList = baserel->reltargetlist; +#endif List *restrictInfoList = baserel->baserestrictinfo; ListCell *restrictInfoCell = NULL; @@ -660,8 +665,10 @@ ColumnList(RelOptInfo *baserel) /* recursively pull up any columns used in the restriction clause */ clauseColumnList = pull_var_clause(restrictClause, - PVC_RECURSE_AGGREGATES, - PVC_RECURSE_PLACEHOLDERS); +#if PG_VERSION_NUM < 90600 + PVC_RECURSE_AGGREGATES, +#endif + PVC_RECURSE_PLACEHOLDERS); neededColumnList = list_union(neededColumnList, clauseColumnList); } From d04a7e6bdd8e57bc66dd63ce83ca8e4537714bf1 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Sat, 17 Dec 2016 20:16:32 +0500 Subject: [PATCH 094/239] Issue - (#67) PostgreSQL 9.6 support. --- mongo_fdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 01e6783..c4c570a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -319,7 +319,7 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) /* create a foreign path node */ - create_foreignscan_path(root, baserel, + foreignPath = (Path *) create_foreignscan_path(root, baserel, #if PG_VERSION_NUM >= 90600 NULL, /* default pathtarget */ #endif From e0e11ee97116655e9110d9e1b3908b52efaafe9f Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 20 Dec 2016 00:50:37 +0500 Subject: [PATCH 095/239] README update. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef3c096..ffde977 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5 and 9.6. Work is underway for certification with 9.6Beta. +Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5 and 9.6. Installation ------------ From 17687ce65e0c09b354e88ebd5f4a44ac7c83d909 Mon Sep 17 00:00:00 2001 From: Abbas Butt Date: Mon, 13 Feb 2017 09:53:38 -0800 Subject: [PATCH 096/239] Issue (#72) Provide support for JSONB as type of special __doc column The patch supports other popular types too like text, varchar etc and stops types that can crash like tsquery or produce strange results like numeric. Test cases are added as a part of the patch. --- expected/mongo_fdw.out | 41 ++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 43 ++++++++++++++++++++++++++++++++++++++++++ sql/mongo_fdw.sql | 23 ++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 3a74d5e..0757c57 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -1404,6 +1404,47 @@ SELECT * FROM main_exports; 5381ccf9d6d81c8e8bf04351 | {"Wine of fresh grapes, including fortified wines","Insulated (including enamelled or anodised) wire, cable","Sunflower seeds, whether or not broken"} (3 rows) +-- __doc tests +-- the collection warehouse must contain the following data +-- use testdb; +-- db.warehouse.insert ({"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); +-- db.warehouse.insert({"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); +CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +SELECT * FROM test_json; + __doc +--------------------------------------------------------------------------------------------------------------------------------------------------------- + { "_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 } } +(2 rows) + +SELECT * FROM test_jsonb; + __doc +--------------------------------------------------------------------------------------------------------------------------------------------- + {"_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}} +(2 rows) + +SELECT * FROM test_text; + __doc +--------------------------------------------------------------------------------------------------------------------------------------------------------- + { "_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 } } +(2 rows) + +SELECT * FROM test_varchar; + __doc +--------------------------------------------------------------------------------------------------------------------------------------------------------- + { "_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 } } +(2 rows) + +DROP FOREIGN TABLE test_json; +DROP FOREIGN TABLE test_jsonb; +DROP FOREIGN TABLE test_text; +DROP FOREIGN TABLE test_varchar; DROP FOREIGN TABLE department; DROP FOREIGN TABLE employee; DROP FOREIGN TABLE countries; diff --git a/mongo_fdw.c b/mongo_fdw.c index d94867e..3ff57de 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -64,6 +64,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/jsonapi.h" +#include "utils/jsonb.h" #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" #endif @@ -1193,8 +1194,50 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, lex = makeJsonLexContext(result, false); 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("un-supported type for column __doc"), + errhint("Column type: %u", (uint32) columnMapping->columnTypeId))); + break; + } + columnValues[columnMapping->columnIndex] = columnValue; columnNulls[columnMapping->columnIndex] = false; + + return; } diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index fc5bd54..ba6e4b2 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -65,6 +65,29 @@ _id NAME, ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); SELECT * FROM main_exports; +-- __doc tests + +-- the collection warehouse must contain the following data +-- use testdb; +-- db.warehouse.insert ({"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); +-- db.warehouse.insert({"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); + + +CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); +CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); + +SELECT * FROM test_json; +SELECT * FROM test_jsonb; +SELECT * FROM test_text; +SELECT * FROM test_varchar; + +DROP FOREIGN TABLE test_json; +DROP FOREIGN TABLE test_jsonb; +DROP FOREIGN TABLE test_text; +DROP FOREIGN TABLE test_varchar; + DROP FOREIGN TABLE department; DROP FOREIGN TABLE employee; DROP FOREIGN TABLE countries; From 265f72fac47ce2398219c1cd17104362429a68cb Mon Sep 17 00:00:00 2001 From: Abbas Butt Date: Tue, 21 Feb 2017 04:35:29 -0800 Subject: [PATCH 097/239] Issue (#64) Add support for push down of parameterized where clause Test cases are attached with the patch. --- expected/mongo_fdw.out | 176 +++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 6 +- mongo_fdw.h | 3 +- mongo_query.c | 45 ++++++++++- sql/mongo_fdw.sql | 51 ++++++++++++ 5 files changed, 273 insertions(+), 8 deletions(-) diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 0757c57..8ab994c 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -1441,6 +1441,182 @@ SELECT * FROM test_varchar; { "_id" : { "$oid" : "58a1ebbaf543ec0b9054585a" }, "warehouse_id" : 2, "warehouse_name" : "Laptop", "warehouse_created" : { "$date" : 1447229590000 } } (2 rows) +-- where clause push down test +CREATE FOREIGN TABLE test_numbers(_id NAME, a int, b text) SERVER mongo_server OPTIONS (database 'testdb', collection 'test_numbers'); +insert into test_numbers values('1', 1, 'One'); +insert into test_numbers values('2', 2, 'Two'); +insert into test_numbers values('3', 3, 'Three'); +insert into test_numbers values('4', 4, 'Four'); +insert into test_numbers values('5', 5, 'Five'); +insert into test_numbers values('6', 6, 'Six'); +insert into test_numbers values('7', 7, 'Seven'); +insert into test_numbers values('8', 8, 'Eight'); +insert into test_numbers values('9', 9, 'Nine'); +insert into test_numbers values('10', 10, 'Ten'); +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 test_numbers where a=x; + raise notice 'Found Item %', n; + end loop; + return; +END +$$ LANGUAGE plpgsql; +SELECT test_param_where(); +NOTICE: Found Item One +NOTICE: Found Item Two +NOTICE: Found Item Three +NOTICE: Found Item Four +NOTICE: Found Item Five +NOTICE: Found Item Six +NOTICE: Found Item Seven +NOTICE: Found Item Eight +NOTICE: Found Item Nine + test_param_where +------------------ + +(1 row) + +PREPARE test_where_pd(int) as SELECT b FROM test_numbers WHERE a =$1; +explain (verbose, costs false) execute test_where_pd(1); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = 1) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(2); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = 2) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(3); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = 3) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(4); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = 4) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(5); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = 5) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(6); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = $1) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(7); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = $1) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(8); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = $1) + Foreign Namespace: testdb.test_numbers +(4 rows) + +explain (verbose, costs false) execute test_where_pd(9); + QUERY PLAN +------------------------------------------ + Foreign Scan on public.test_numbers + Output: b + Filter: (test_numbers.a = $1) + Foreign Namespace: testdb.test_numbers +(4 rows) + +execute test_where_pd(1); + b +----- + One +(1 row) + +execute test_where_pd(2); + b +----- + Two +(1 row) + +execute test_where_pd(3); + b +------- + Three +(1 row) + +execute test_where_pd(4); + b +------ + Four +(1 row) + +execute test_where_pd(5); + b +------ + Five +(1 row) + +execute test_where_pd(6); + b +----- + Six +(1 row) + +execute test_where_pd(7); + b +------- + Seven +(1 row) + +execute test_where_pd(8); + b +------- + Eight +(1 row) + +execute test_where_pd(9); + b +------ + Nine +(1 row) + +DELETE FROM test_numbers; +DROP FOREIGN TABLE test_numbers; DROP FOREIGN TABLE test_json; DROP FOREIGN TABLE test_jsonb; DROP FOREIGN TABLE test_text; diff --git a/mongo_fdw.c b/mongo_fdw.c index 3ff57de..97fd944 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -376,7 +376,7 @@ MongoGetForeignPlan(PlannerInfo *root, * the MongoDB server-side, so we instead filter out columns on our side. */ opExpressionList = ApplicableOpExpressionList(baserel); - queryDocument = QueryDocument(foreigntableid, opExpressionList); + queryDocument = QueryDocument(foreigntableid, opExpressionList, NULL); /* we don't need to serialize column list as lists are copiable */ columnList = ColumnList(baserel); @@ -513,7 +513,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) columnList = list_nth(foreignPrivateList, 0); opExpressionList = list_nth(foreignPrivateList, 1); - queryDocument = QueryDocument(foreignTableId, opExpressionList); + queryDocument = QueryDocument(foreignTableId, opExpressionList, scanState); columnMappingHash = ColumnMappingHash(foreignTableId, columnList); @@ -1995,7 +1995,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, scanState->ss.ss_currentRelation = relation; foreignTableId = RelationGetRelid(relation); - queryDocument = QueryDocument(foreignTableId, NIL); + queryDocument = QueryDocument(foreignTableId, NIL, NULL); foreignPrivateList = list_make2(columnList, NULL); /* only clean up the query struct, but not its data */ diff --git a/mongo_fdw.h b/mongo_fdw.h index e18d8c1..3940b4a 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -303,7 +303,8 @@ extern void mongo_release_connection(MONGO_CONN* conn); /* Function declarations related to creating the mongo query */ extern List * ApplicableOpExpressionList(RelOptInfo *baserel); -extern BSON * QueryDocument(Oid relationId, List *opExpressionList); +extern BSON * QueryDocument(Oid relationId, List *opExpressionList, + ForeignScanState *scanStateNode); extern List * ColumnList(RelOptInfo *baserel); /* Function declarations for foreign data wrapper */ diff --git a/mongo_query.c b/mongo_query.c index 6111e34..dad6941 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -50,7 +50,8 @@ static List * UniqueColumnList(List *operatorList); static List * ColumnOperatorList(Var *column, List *operatorList); static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); - +static void AppendParamValue(BSON *queryDocument, const char *keyName, + Param *paramNode, ForeignScanState *scanStateNode); /* * ApplicableOpExpressionList walks over all filter clauses that relate to this * foreign table, and chooses applicable clauses that we know we can translate @@ -79,6 +80,7 @@ ApplicableOpExpressionList(RelOptInfo *baserel) Const *constant = NULL; bool equalsOperator = false; bool constantIsArray = false; + Param *paramNode = NULL; /* we only support operator expressions */ expressionType = nodeTag(expression); @@ -109,6 +111,7 @@ ApplicableOpExpressionList(RelOptInfo *baserel) argumentList = opExpression->args; column = (Var *) FindArgumentOfType(argumentList, T_Var); constant = (Const *) FindArgumentOfType(argumentList, T_Const); + paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); /* * We don't push down operators where the constant is an array, since @@ -130,6 +133,11 @@ ApplicableOpExpressionList(RelOptInfo *baserel) { opExpressionList = lappend(opExpressionList, opExpression); } + + if (column != NULL && paramNode != NULL) + { + opExpressionList = lappend(opExpressionList, opExpression); + } } return opExpressionList; @@ -169,7 +177,7 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". */ BSON * -QueryDocument(Oid relationId, List *opExpressionList) +QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode) { List *equalityOperatorList = NIL; List *comparisonOperatorList = NIL; @@ -193,15 +201,21 @@ QueryDocument(Oid relationId, List *opExpressionList) OpExpr *equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); Oid columnId = InvalidOid; char *columnName = NULL; + Const *constant = NULL; + Param *paramNode = NULL; List *argumentList = equalityOperator->args; Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); - Const *constant = (Const *) FindArgumentOfType(argumentList, T_Const); + constant = (Const *) FindArgumentOfType(argumentList, T_Const); + paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); columnId = column->varattno; columnName = get_relid_attribute_name(relationId, columnId); - AppendConstantValue(queryDocument, columnName, constant); + if (constant != NULL) + AppendConstantValue(queryDocument, columnName, constant); + else + AppendParamValue(queryDocument, columnName, paramNode, scanStateNode); } /* @@ -373,6 +387,29 @@ ColumnOperatorList(Var *column, List *operatorList) return columnOperatorList; } +static void +AppendParamValue(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, NULL); + + AppenMongoValue(queryDocument, keyName, param_value, isNull, + paramNode->paramtype); +} /* * AppendConstantValue appends to the query document the key name and constant diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index ba6e4b2..df7c158 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -83,6 +83,57 @@ SELECT * FROM test_jsonb; SELECT * FROM test_text; SELECT * FROM test_varchar; +-- where clause push down test +CREATE FOREIGN TABLE test_numbers(_id NAME, a int, b text) SERVER mongo_server OPTIONS (database 'testdb', collection 'test_numbers'); +insert into test_numbers values('1', 1, 'One'); +insert into test_numbers values('2', 2, 'Two'); +insert into test_numbers values('3', 3, 'Three'); +insert into test_numbers values('4', 4, 'Four'); +insert into test_numbers values('5', 5, 'Five'); +insert into test_numbers values('6', 6, 'Six'); +insert into test_numbers values('7', 7, 'Seven'); +insert into test_numbers values('8', 8, 'Eight'); +insert into test_numbers values('9', 9, 'Nine'); +insert into test_numbers values('10', 10, 'Ten'); + +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 test_numbers where a=x; + raise notice 'Found Item %', n; + end loop; + return; +END +$$ LANGUAGE plpgsql; + +SELECT test_param_where(); + +PREPARE test_where_pd(int) as SELECT b FROM test_numbers WHERE a =$1; +explain (verbose, costs false) execute test_where_pd(1); +explain (verbose, costs false) execute test_where_pd(2); +explain (verbose, costs false) execute test_where_pd(3); +explain (verbose, costs false) execute test_where_pd(4); +explain (verbose, costs false) execute test_where_pd(5); +explain (verbose, costs false) execute test_where_pd(6); +explain (verbose, costs false) execute test_where_pd(7); +explain (verbose, costs false) execute test_where_pd(8); +explain (verbose, costs false) execute test_where_pd(9); + +execute test_where_pd(1); +execute test_where_pd(2); +execute test_where_pd(3); +execute test_where_pd(4); +execute test_where_pd(5); +execute test_where_pd(6); +execute test_where_pd(7); +execute test_where_pd(8); +execute test_where_pd(9); + +DELETE FROM test_numbers; +DROP FOREIGN TABLE test_numbers; + DROP FOREIGN TABLE test_json; DROP FOREIGN TABLE test_jsonb; DROP FOREIGN TABLE test_text; From 15ea688a2bc3a4c217ea5452e92faabf9ac8459e Mon Sep 17 00:00:00 2001 From: lreimus Date: Wed, 8 Mar 2017 12:57:43 -0500 Subject: [PATCH 098/239] Added support for authentication database in the CREATE SERVER declaration. --- .gitignore | 2 ++ README.md | 1 + connection.c | 2 +- mongo_fdw.h | 5 ++++- mongo_wrapper.h | 5 +++-- mongo_wrapper_meta.c | 18 +++++++++++++----- option.c | 3 +++ 7 files changed, 27 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 39fc481..65a153a 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,5 @@ # pgregress results directory results/ + +.idea/ diff --git a/README.md b/README.md index 22ad3f6..2424183 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ The following parameters can be set on a MongoDB foreign server object: * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` * **`port`**: the port number of the MongoDB server. Defaults to `27017` + * **`authentication_database`**: database against which user will be authenticated against. Only valid with password based authentication. Defaults to per same database as the MongoDB collection database. * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary` * **`ssl`**: false [default], true to enable ssl (meta driver only). See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to understand the options. * **`pem_file`**: SSL option; diff --git a/connection.c b/connection.c index bbd81aa..bb7a695 100644 --- a/connection.c +++ b/connection.c @@ -99,7 +99,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * if (entry->conn == NULL) { #ifdef META_DRIVER - entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->readPreference, + entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->authenticationDatabase, opt->readPreference, opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); #else entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); diff --git a/mongo_fdw.h b/mongo_fdw.h index 3940b4a..98c6097 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -150,6 +150,7 @@ #define OPTION_NAME_PASSWORD "password" #ifdef META_DRIVER #define OPTION_NAME_READ_PREFERENCE "read_preference" +#define OPTION_NAME_AUTHENTICATION_DATABASE "authentication_database" #define OPTION_NAME_SSL "ssl" #define OPTION_NAME_PEM_FILE "pem_file" #define OPTION_NAME_PEM_PWD "pem_pwd" @@ -188,7 +189,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 14; +static const uint32 ValidOptionCount = 15; #else static const uint32 ValidOptionCount = 6; #endif @@ -200,6 +201,7 @@ static const MongoValidOption ValidOptionArray[] = #ifdef META_DRIVER { OPTION_NAME_READ_PREFERENCE, ForeignServerRelationId }, + { OPTION_NAME_AUTHENTICATION_DATABASE, ForeignServerRelationId }, { OPTION_NAME_SSL, ForeignServerRelationId }, { OPTION_NAME_PEM_FILE, ForeignServerRelationId }, { OPTION_NAME_PEM_PWD, ForeignServerRelationId }, @@ -235,6 +237,7 @@ typedef struct MongoFdwOptions char *svr_password; #ifdef META_DRIVER char *readPreference; + char *authenticationDatabase; bool ssl; char *pem_file; char *pem_pwd; diff --git a/mongo_wrapper.h b/mongo_wrapper.h index d0b3872..c5da18d 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -31,8 +31,9 @@ #include #ifdef META_DRIVER -MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, char *readPreference, - bool ssl, char *pem_file, char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation); +MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, + char *authenticationDatabase, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, + char *ca_dir, char *crl_file, bool weak_cert_validation); #else MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); #endif diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 4abc915..248ffff 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -24,16 +24,24 @@ * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN* -MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, char *readPreference, bool ssl, char *pem_file, +MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, + char *authenticationDatabase, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) { MONGO_CONN *client = NULL; char* uri = NULL; - if (user && password && readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); - else if (user && password) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); + if (user && password) + if (authenticationDatabase) + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase); + else + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); else if (readPreference) uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); else diff --git a/option.c b/option.c index 4972352..4bc731c 100644 --- a/option.c +++ b/option.c @@ -154,6 +154,7 @@ mongo_get_options(Oid foreignTableId) char *svr_password= NULL; #ifdef META_DRIVER char *readPreference = NULL; + char *authenticationDatabase = NULL; bool ssl = false; char *pem_file = NULL; char *pem_pwd = NULL; @@ -163,6 +164,7 @@ mongo_get_options(Oid foreignTableId) bool weak_cert_validation = false; readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); + authenticationDatabase = mongo_get_option_value(foreignTableId, OPTION_NAME_AUTHENTICATION_DATABASE); ssl = mongo_get_option_value(foreignTableId, OPTION_NAME_SSL); pem_file = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_FILE); pem_pwd = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_PWD); @@ -204,6 +206,7 @@ mongo_get_options(Oid foreignTableId) #ifdef META_DRIVER options->readPreference = readPreference; + options->authenticationDatabase = authenticationDatabase; options->ssl = ssl; options->pem_file = pem_file; options->pem_pwd = pem_pwd; From 510388f4ffe084defd2434c72e2a2400e423a8f2 Mon Sep 17 00:00:00 2001 From: lreimus Date: Wed, 8 Mar 2017 15:42:36 -0500 Subject: [PATCH 099/239] Added support for replica set in the CREATE SERVER declaration. --- README.md | 1 + connection.c | 3 ++- mongo_fdw.h | 5 ++++- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 40 ++++++++++++++++++++++++++++++---------- option.c | 3 +++ 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 2424183..48e9876 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ The following parameters can be set on a MongoDB foreign server object: * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` * **`port`**: the port number of the MongoDB server. Defaults to `27017` * **`authentication_database`**: database against which user will be authenticated against. Only valid with password based authentication. Defaults to per same database as the MongoDB collection database. + * **`replica_set`**: replica set the server is member of. If set, driver will auto-connect to correct primary in the replica set when writing. * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary` * **`ssl`**: false [default], true to enable ssl (meta driver only). See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to understand the options. * **`pem_file`**: SSL option; diff --git a/connection.c b/connection.c index bb7a695..4a3fcb4 100644 --- a/connection.c +++ b/connection.c @@ -99,7 +99,8 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * if (entry->conn == NULL) { #ifdef META_DRIVER - entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->authenticationDatabase, opt->readPreference, + entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, + opt->authenticationDatabase, opt->replicaSet, opt->readPreference, opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); #else entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); diff --git a/mongo_fdw.h b/mongo_fdw.h index 98c6097..d5dd1a7 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -151,6 +151,7 @@ #ifdef META_DRIVER #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" @@ -189,7 +190,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 15; +static const uint32 ValidOptionCount = 16; #else static const uint32 ValidOptionCount = 6; #endif @@ -202,6 +203,7 @@ static const MongoValidOption ValidOptionArray[] = #ifdef META_DRIVER { 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 }, @@ -238,6 +240,7 @@ typedef struct MongoFdwOptions #ifdef META_DRIVER char *readPreference; char *authenticationDatabase; + char *replicaSet; bool ssl; char *pem_file; char *pem_pwd; diff --git a/mongo_wrapper.h b/mongo_wrapper.h index c5da18d..6d39a6e 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -32,7 +32,7 @@ #ifdef META_DRIVER MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, - char *authenticationDatabase, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, + char *authenticationDatabase,char *replicaSet, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation); #else MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 248ffff..062a8c7 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -25,7 +25,7 @@ */ MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, - char *authenticationDatabase, char *readPreference, bool ssl, char *pem_file, + char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) { MONGO_CONN *client = NULL; @@ -33,19 +33,39 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch if (user && password) if (authenticationDatabase) - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase); + if (replicaSet) + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase,replicaSet); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase,replicaSet); else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase); + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase); else - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); + if (replicaSet) + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",replicaSet); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false",replicaSet); else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); - else if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); + else + uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); else - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s", host, port, databaseName, ssl ? "true" : "false"); + if (replicaSet) + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", host, port, databaseName, readPreference, ssl ? "true" : "false",replicaSet); + else + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s&replicaSet=%s", host, port, databaseName, ssl ? "true" : "false",replicaSet); + else + if (readPreference) + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); + else + uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s", host, port, databaseName, ssl ? "true" : "false"); + client = mongoc_client_new(uri); diff --git a/option.c b/option.c index 4bc731c..0fa15f4 100644 --- a/option.c +++ b/option.c @@ -155,6 +155,7 @@ mongo_get_options(Oid foreignTableId) #ifdef META_DRIVER char *readPreference = NULL; char *authenticationDatabase = NULL; + char *replicaSet = NULL; bool ssl = false; char *pem_file = NULL; char *pem_pwd = NULL; @@ -165,6 +166,7 @@ mongo_get_options(Oid foreignTableId) readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); authenticationDatabase = mongo_get_option_value(foreignTableId, OPTION_NAME_AUTHENTICATION_DATABASE); + replicaSet = mongo_get_option_value(foreignTableId, OPTION_NAME_REPLICA_SET); ssl = mongo_get_option_value(foreignTableId, OPTION_NAME_SSL); pem_file = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_FILE); pem_pwd = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_PWD); @@ -207,6 +209,7 @@ mongo_get_options(Oid foreignTableId) #ifdef META_DRIVER options->readPreference = readPreference; options->authenticationDatabase = authenticationDatabase; + options->replicaSet = replicaSet; options->ssl = ssl; options->pem_file = pem_file; options->pem_pwd = pem_pwd; From 9dfa6ae2512a4a0ef3bd62b62c9a3337459a428d Mon Sep 17 00:00:00 2001 From: vagrant Date: Fri, 10 Mar 2017 10:02:13 +0000 Subject: [PATCH 100/239] Update json-c to 0.12.1 --- autogen.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autogen.sh b/autogen.sh index 55b0cfe..fb8ba27 100755 --- a/autogen.sh +++ b/autogen.sh @@ -49,9 +49,9 @@ function checkout_json_lib { echo $PWD rm -rf json-c - wget https://github.com/json-c/json-c/archive/json-c-0.12-20140410.tar.gz - tar -zxvf json-c-0.12-20140410.tar.gz - mv json-c-json-c-0.12-20140410 json-c + wget https://github.com/json-c/json-c/archive/json-c-0.12.1-20160607.tar.gz + tar -zxvf json-c-0.12.1-20160607.tar.gz + mv json-c-json-c-0.12.1-20160607 json-c cd json-c patch -p1 < ../json_compilation_error.patch cd .. From b97128825d9606c3e90ff8e0c43fa2c60cd4ff1f Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Sun, 15 Oct 2017 14:17:53 -0700 Subject: [PATCH 101/239] Issue - (#88) - PostgreSQL 10 compilation issue. --- mongo_query.c | 5 +++++ mongo_wrapper.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/mongo_query.c b/mongo_query.c index dad6941..d879a7d 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -405,7 +405,12 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *)scanStateNode); /* Evaluate the parameter expression */ +#if PG_VERSION_NUM >= 100000 + param_value = ExecEvalExpr(param_expr, econtext, &isNull); +#else param_value = ExecEvalExpr(param_expr, econtext, &isNull, NULL); +#endif + AppenMongoValue(queryDocument, keyName, param_value, isNull, paramNode->paramtype); diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 6d39a6e..bd3020b 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -17,6 +17,7 @@ #ifndef MONGO_WRAPPER_H #define MONGO_WRAPPER_H + #include "mongo_fdw.h" #include "bson.h" @@ -25,6 +26,7 @@ #else #include "mongo.h" #endif +#define json_object json_object_tmp #include #include From 657c150bf3791db4a16951bcac31e41202496c28 Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Fri, 1 Dec 2017 15:27:14 +0100 Subject: [PATCH 102/239] Add PostgreSQL version 10 to all Makefiles --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 2e173d8..aeb728c 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) - $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 2e173d8..aeb728c 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) - $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index fbd483c..d47868b 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -43,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6)) - $(error PostgreSQL 9.3, 9.4, 9.5 or 9.6 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) endif From a318751ee16b4c2b02020c2dd6c70c18fcef36f2 Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Tue, 12 Dec 2017 02:13:40 +0100 Subject: [PATCH 103/239] Update version number from 10 to 10.0 --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index aeb728c..482b176 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index aeb728c..482b176 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index d47868b..e9a3a09 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -43,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) endif From 84d16387ce55cd70e582a04f87370bb100abba89 Mon Sep 17 00:00:00 2001 From: Abbas Butt Date: Tue, 13 Mar 2018 03:21:11 -0700 Subject: [PATCH 104/239] Fix for issue #99, Prepare to release for 5.1 Change version in control file Add sql file for 1.1 & sql file to upgrade from 1.0 to 1.1 Add new sql files in Makefile Add a function to get .so version select mongo_fdw_version(); mongo_fdw_version ------------------- 50100 (1 row) --- Makefile | 2 +- README.md | 9 ++++++++- mongo_fdw--1.0--1.1.sql | 6 ++++++ mongo_fdw--1.1.sql | 25 +++++++++++++++++++++++++ mongo_fdw.c | 13 +++++++++++++ mongo_fdw.control | 2 +- 6 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 mongo_fdw--1.0--1.1.sql create mode 100644 mongo_fdw--1.1.sql diff --git a/Makefile b/Makefile index 482b176..b614f11 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) EXTENSION = mongo_fdw -DATA = mongo_fdw--1.0.sql +DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql REGRESS = mongo_fdw REGRESS_OPTS = --load-extension=$(EXTENSION) diff --git a/README.md b/README.md index 48e9876..9a08624 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5 and 9.6. +Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5, 9.6 and 10. Installation ------------ @@ -176,6 +176,13 @@ EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1; -- collect data distribution statistics` ANALYZE warehouse; +select mongo_fdw_version(); + mongo_fdw_version +------------------- + 50100 +(1 row) + + ``` Limitations diff --git a/mongo_fdw--1.0--1.1.sql b/mongo_fdw--1.0--1.1.sql new file mode 100644 index 0000000..781155d --- /dev/null +++ b/mongo_fdw--1.0--1.1.sql @@ -0,0 +1,6 @@ +/* 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; + diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql new file mode 100644 index 0000000..38da196 --- /dev/null +++ b/mongo_fdw--1.1.sql @@ -0,0 +1,25 @@ +/* mongo_fdw/mongo_fdw--1.1.sql */ + +-- Portions Copyright © 2004-2014, 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 97fd944..43862eb 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -69,6 +69,12 @@ #include "access/htup_details.h" #endif +/* + * In PG 9.5.1 the number will be 90501, + * our version is 5.1.0 so number will be 50100 + */ +#define CODE_VERSION 50100 + /* Local functions forward declarations */ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, @@ -164,6 +170,7 @@ static JsonSemAction nullSemAction = PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(mongo_fdw_handler); +PG_FUNCTION_INFO_V1(mongo_fdw_version); /* * Library load-time initalization, sets on_proc_exit() callback for @@ -2137,3 +2144,9 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, return sampleRowCount; } + +Datum +mongo_fdw_version(PG_FUNCTION_ARGS) +{ + PG_RETURN_INT32(CODE_VERSION); +} diff --git a/mongo_fdw.control b/mongo_fdw.control index a457792..57dfad9 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -4,6 +4,6 @@ # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/mongo_fdw' relocatable = true From 8307ced3e304e81240d8fa6709b594c8769b1760 Mon Sep 17 00:00:00 2001 From: Ahsan Date: Wed, 14 Mar 2018 14:15:49 +0000 Subject: [PATCH 105/239] Fixed a build issue --- json_compilation_error.patch | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/json_compilation_error.patch b/json_compilation_error.patch index 601f0a1..a909fea 100644 --- a/json_compilation_error.patch +++ b/json_compilation_error.patch @@ -2,16 +2,4 @@ --- json-c/json_tokener.c 2015-02-26 01:16:01.729596399 +0500 *************** *** 352,363 **** - - case json_tokener_state_inf: /* aka starts with 'i' */ - { -- int size; - int size_inf; - int is_negative = 0; - - printbuf_memappend_fast(tok->pb, &c, 1); -- size = json_min(tok->st_pos+1, json_null_str_len); - size_inf = json_min(tok->st_pos+1, json_inf_str_len); - char *infbuf = tok->pb->buf; - if (*infbuf == '-') --- 352,361 ---- From 83ed3eecdcedc4f31c13050591fdc2ff29d858e1 Mon Sep 17 00:00:00 2001 From: Ahsan Hadi Date: Wed, 14 Mar 2018 19:19:57 +0500 Subject: [PATCH 106/239] Update mongo_fdw.c --- mongo_fdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 43862eb..2f9457c 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -73,7 +73,7 @@ * In PG 9.5.1 the number will be 90501, * our version is 5.1.0 so number will be 50100 */ -#define CODE_VERSION 50100 +#define CODE_VERSION 50200 /* Local functions forward declarations */ From 35fe193107951a1f4c6faa9c78febfbd5c889d12 Mon Sep 17 00:00:00 2001 From: Abbas Butt Date: Wed, 21 Mar 2018 01:55:51 -0700 Subject: [PATCH 107/239] Fix issue #100, CREATE EXTENSION fails due to missing sql files --- Makefile.legacy | 2 +- Makefile.meta | 2 +- mongo_fdw.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.legacy b/Makefile.legacy index 482b176..b614f11 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -24,7 +24,7 @@ PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) EXTENSION = mongo_fdw -DATA = mongo_fdw--1.0.sql +DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql REGRESS = mongo_fdw REGRESS_OPTS = --load-extension=$(EXTENSION) diff --git a/Makefile.meta b/Makefile.meta index e9a3a09..398a212 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -25,7 +25,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o $(LI EXTENSION = mongo_fdw -DATA = mongo_fdw--1.0.sql +DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql REGRESS = mongo_fdw REGRESS_OPTS = --load-extension=$(EXTENSION) diff --git a/mongo_fdw.c b/mongo_fdw.c index 2f9457c..9ef13f7 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -73,7 +73,7 @@ * In PG 9.5.1 the number will be 90501, * our version is 5.1.0 so number will be 50100 */ -#define CODE_VERSION 50200 +#define CODE_VERSION 50201 /* Local functions forward declarations */ From e16476eb5ec825e1aa104ad4387f85026f06ae1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20J=2E=20=C5=81akis?= Date: Thu, 10 May 2018 14:39:26 +0200 Subject: [PATCH 108/239] Bump version of mongo-c-driver --- autogen.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/autogen.sh b/autogen.sh index fb8ba27..c8ce143 100755 --- a/autogen.sh +++ b/autogen.sh @@ -14,6 +14,10 @@ # #------------------------------------------------------------------------- + +MONGOC_VERSION=1.9.5 +JSONC_VERSION=0.12.1-20160607 + if [ "$#" -ne 1 ]; then echo "Usage: autogen.sh --[with-legacy | with-master]" exit @@ -25,10 +29,10 @@ fi function checkout_mongo_driver { rm -rf mongo-c-driver - wget https://github.com/mongodb/mongo-c-driver/releases/download/1.3.1/mongo-c-driver-1.3.1.tar.gz - tar -zxvf mongo-c-driver-1.3.1.tar.gz - mv mongo-c-driver-1.3.1 mongo-c-driver - rm -rf mongo-c-driver-1.3.1.tar.gz + wget https://github.com/mongodb/mongo-c-driver/releases/download/$MONGOC_VERSION/mongo-c-driver-$MONGOC_VERSION.tar.gz + tar -zxvf 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 } ### @@ -49,13 +53,13 @@ function checkout_json_lib { echo $PWD rm -rf json-c - wget https://github.com/json-c/json-c/archive/json-c-0.12.1-20160607.tar.gz - tar -zxvf json-c-0.12.1-20160607.tar.gz - mv json-c-json-c-0.12.1-20160607 json-c + wget https://github.com/json-c/json-c/archive/json-c-$JSONC_VERSION.tar.gz + tar -zxvf json-c-$JSONC_VERSION.tar.gz + mv json-c-json-c-$JSONC_VERSION json-c cd json-c patch -p1 < ../json_compilation_error.patch cd .. - rm -rf json-c-0.12-20140410.tar.gz + rm -rf json-c-$JSONC_VERSION.tar.gz echo $PWD } From 4ef4341812812eef2d517211316acf963a32b61b Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Tue, 7 Aug 2018 17:11:14 +0500 Subject: [PATCH 109/239] Issue - #105: Fix crash while selecting data. Don't destroy BSON object returned by mongoc_cursor_next function. --- mongo_wrapper_meta.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 062a8c7..8c51b7d 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -567,7 +567,6 @@ MongoAggregateCount(MONGO_CONN* conn, const char* database, const char* collecti bson_copy_to(doc, reply); if (bson_iter_init_find(&it, reply, "n")) count = BsonIterDouble(&it); - BsonDestroy(doc); } mongoc_cursor_destroy(cursor); } From 32358928345c9a9e6be957b0fbbe36d22a03c851 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Thu, 25 Oct 2018 16:12:39 +0530 Subject: [PATCH 110/239] Support PG/EPAS 11. Commit fixes the issues which caused compilation error when compiled against PG/EPAS 11. --- Makefile | 4 ++-- mongo_fdw.c | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index b614f11..d8f55fc 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) endif diff --git a/mongo_fdw.c b/mongo_fdw.c index 9ef13f7..a357e12 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -655,7 +655,11 @@ MongoPlanForeignModify(PlannerInfo *root, for (attnum = 1; attnum <= tupdesc->natts; attnum++) { +#if PG_VERSION_NUM < 110000 Form_pg_attribute attr = tupdesc->attrs[attnum - 1]; +#else + Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); +#endif if (!attr->attisdropped) targetAttrs = lappend_int(targetAttrs, attnum); @@ -746,7 +750,11 @@ MongoBeginForeignModify(ModifyTableState *mtstate, foreach(lc, fmstate->target_attrs) { int attnum = lfirst_int(lc); +#if PG_VERSION_NUM < 110000 Form_pg_attribute attr = RelationGetDescr(rel)->attrs[attnum - 1]; +#else + Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1); +#endif Assert(!attr->attisdropped); @@ -811,16 +819,24 @@ MongoExecForeignInsert(EState *estate, 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 PG_VERSION_NUM < 110000 if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) +#else + if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "_id") != 0) +#endif 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 PG_VERSION_NUM < 110000 if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "__doc") == 0) +#else + if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "__doc") == 0) +#endif continue; if (attnum == 1) @@ -832,8 +848,13 @@ MongoExecForeignInsert(EState *estate, } else { +#if PG_VERSION_NUM < 110000 AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); +#else + AppenMongoValue(b, TupleDescAttr(slot->tts_tupleDescriptor, attnum-1)->attname.data, value, + isnull, TupleDescAttr(slot->tts_tupleDescriptor, attnum-1)->atttypid); +#endif } } } @@ -865,8 +886,11 @@ MongoAddForeignUpdateTargets(Query *parsetree, /* * What we need is the rowid which is the first column */ - Form_pg_attribute attr = - RelationGetDescr(target_relation)->attrs[0]; +#if PG_VERSION_NUM < 110000 + Form_pg_attribute attr = RelationGetDescr(target_relation)->attrs[0]; +#else + Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(target_relation), 0); +#endif /* Make a Var representing the desired value */ var = makeVar(parsetree->resultRelation, @@ -947,22 +971,27 @@ MongoExecForeignUpdate(EState *estate, foreach(lc, fmstate->target_attrs) { int attnum = lfirst_int(lc); +#if PG_VERSION_NUM < 110000 + Form_pg_attribute attr = slot->tts_tupleDescriptor->attrs[attnum - 1]; +#else + Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1); +#endif Datum value; bool isnull; - if (strcmp("_id", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + if (strcmp("_id", attr->attname.data) == 0) continue; - if (strcmp("__doc", slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data) == 0) + if (strcmp("__doc", attr->attname.data) == 0) elog(ERROR, "system column '__doc' update is not supported"); value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER - AppenMongoValue(&set, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + AppenMongoValue(&set, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #else - AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull ? true : false, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + AppenMongoValue(b, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #endif } } @@ -2023,11 +2052,17 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, * Use per-tuple memory context to prevent leak of memory used to read * rows from the file with copy routines. */ +#if PG_VERSION_NUM < 110000 tupleContext = AllocSetContextCreate(CurrentMemoryContext, "mongo_fdw temporary context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); +#else + tupleContext = AllocSetContextCreate(CurrentMemoryContext, + "mongo_fdw temporary context", + ALLOCSET_DEFAULT_SIZES); +#endif /* prepare for sampling rows */ randomState = anl_init_selection_state(targetRowCount); From 83b7134e038dd35309b5109bcbbbe2069e8f4623 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Fri, 26 Oct 2018 15:54:27 +0530 Subject: [PATCH 111/239] Add PostgreSQL version 11 to all Makefiles. --- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.legacy b/Makefile.legacy index b614f11..d8f55fc 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 398a212..1f9b9f7 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -43,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 or 10.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) endif From 09b424820af99f33945cb3b49f5b641667e92058 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Thu, 1 Nov 2018 18:19:50 +0530 Subject: [PATCH 112/239] Fix a build issue with v11. Upstream commit 8237f27b504ff1d1e2da7ae4c81a7f72ea0e0e3e removed get_relid_attribute_name and added missing_ok get_attname so it can serve the purpose of get_relid_attribute_name. This commit replace get_relid_attribute_name() to get_attname(). --- mongo_fdw.c | 12 ++++++++++++ mongo_query.c | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/mongo_fdw.c b/mongo_fdw.c index a357e12..2d07473 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -956,7 +956,11 @@ MongoExecForeignUpdate(EState *estate, /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); +#if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, 1); +#else + columnName = get_attname(foreignTableId, 1, false); +#endif typoid = get_atttype(foreignTableId, 1); @@ -1061,7 +1065,11 @@ MongoExecForeignDelete(EState *estate, /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); +#if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, 1); +#else + columnName = get_attname(foreignTableId, 1, false); +#endif typoid = get_atttype(foreignTableId, 1); @@ -1177,7 +1185,11 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) bool handleFound = false; void *hashKey = NULL; +#if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, columnId); +#else + columnName = get_attname(foreignTableId, columnId, false); +#endif hashKey = (void *) columnName; columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, diff --git a/mongo_query.c b/mongo_query.c index d879a7d..cff6eeb 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -210,7 +210,11 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); columnId = column->varattno; +#if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(relationId, columnId); +#else + columnName = get_attname(relationId, columnId, false); +#endif if (constant != NULL) AppendConstantValue(queryDocument, columnName, constant); @@ -238,7 +242,11 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat BSON r; columnId = column->varattno; +#if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(relationId, columnId); +#else + columnName = get_attname(relationId, columnId, false); +#endif /* find all expressions that correspond to the column */ columnOperatorList = ColumnOperatorList(column, comparisonOperatorList); From fa7a260985346ac8ce9f6b681604f951085eebf7 Mon Sep 17 00:00:00 2001 From: Ibrar Ahmed Date: Mon, 10 Dec 2018 04:08:49 +0000 Subject: [PATCH 113/239] Issue - (#113): Fix compilation error of the json-c library. Update the json-c library release version and remove the unnecessary file to patch json-c. --- autogen.sh | 5 ++--- json_compilation_error.patch | 5 ----- 2 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 json_compilation_error.patch diff --git a/autogen.sh b/autogen.sh index c8ce143..dce2d28 100755 --- a/autogen.sh +++ b/autogen.sh @@ -16,7 +16,7 @@ MONGOC_VERSION=1.9.5 -JSONC_VERSION=0.12.1-20160607 +JSONC_VERSION=0.13.1-20180305 if [ "$#" -ne 1 ]; then echo "Usage: autogen.sh --[with-legacy | with-master]" @@ -57,7 +57,6 @@ function checkout_json_lib tar -zxvf json-c-$JSONC_VERSION.tar.gz mv json-c-json-c-$JSONC_VERSION json-c cd json-c - patch -p1 < ../json_compilation_error.patch cd .. rm -rf json-c-$JSONC_VERSION.tar.gz echo $PWD @@ -71,7 +70,7 @@ function install_json_lib { cd json-c sh ./autogen.sh - ./configure + ./configure CFLAGS='-fPIC' make install cd .. } diff --git a/json_compilation_error.patch b/json_compilation_error.patch deleted file mode 100644 index a909fea..0000000 --- a/json_compilation_error.patch +++ /dev/null @@ -1,5 +0,0 @@ -*** json-c-json-c-0.12-20140410/json_tokener.c 2014-04-11 07:35:45.000000000 +0500 ---- json-c/json_tokener.c 2015-02-26 01:16:01.729596399 +0500 -*************** -*** 352,363 **** ---- 352,361 ---- From 063957be945b9c93f511817af23bddc821690226 Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Fri, 11 Jan 2019 15:24:32 +0100 Subject: [PATCH 114/239] Remove unnecessary cd into a subdir, followed by a cd back up --- autogen.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index dce2d28..9e068f3 100755 --- a/autogen.sh +++ b/autogen.sh @@ -56,8 +56,6 @@ function checkout_json_lib wget https://github.com/json-c/json-c/archive/json-c-$JSONC_VERSION.tar.gz tar -zxvf json-c-$JSONC_VERSION.tar.gz mv json-c-json-c-$JSONC_VERSION json-c - cd json-c - cd .. rm -rf json-c-$JSONC_VERSION.tar.gz echo $PWD } From 6d7a3415316fd991d3adeec44d4acb28ebb1238d Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Sat, 12 Jan 2019 21:41:57 +0100 Subject: [PATCH 115/239] Add $(LIBJSON)/strerror_override.o to $LIBJSON_OBJS --- Makefile | 2 +- Makefile.legacy | 2 +- Makefile.meta | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d8f55fc..16f2624 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.o 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)/arraylist.o $(LIBJSON)/random_seed.o $(LIBJSON)/debug.o $(LIBJSON)/strerror_override.o PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) diff --git a/Makefile.legacy b/Makefile.legacy index d8f55fc..16f2624 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -19,7 +19,7 @@ MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.o 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)/arraylist.o $(LIBJSON)/random_seed.o $(LIBJSON)/debug.o $(LIBJSON)/strerror_override.o PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) diff --git a/Makefile.meta b/Makefile.meta index 1f9b9f7..b696181 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -15,7 +15,7 @@ MODULE_big = mongo_fdw 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)/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) -DMETA_DRIVER From 37755858790beb4fb07e185c00d2009904b5f8cb Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Sun, 31 Mar 2019 13:51:42 +0200 Subject: [PATCH 116/239] Make "tar" non-verbose (#117) --- autogen.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autogen.sh b/autogen.sh index 9e068f3..5258378 100755 --- a/autogen.sh +++ b/autogen.sh @@ -30,7 +30,7 @@ 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 -zxvf 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 } @@ -42,7 +42,7 @@ function checkout_legacy_branch { rm -rf mongo-c-driver wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz - tar -zxvf v0.8.tar.gz + tar -zxf v0.8.tar.gz mv mongo-c-driver-0.8 mongo-c-driver rm -rf v0.8.tar.gz } @@ -54,7 +54,7 @@ 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 -zxvf 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 From 5fe371a0a8c44ec021a8afc58a4f1ba45bb32528 Mon Sep 17 00:00:00 2001 From: Andreas Scherbaum Date: Sun, 31 Mar 2019 13:52:09 +0200 Subject: [PATCH 117/239] Avoid error message when config.h does not exist (#116) --- autogen.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen.sh b/autogen.sh index 5258378..2f61ce2 100755 --- a/autogen.sh +++ b/autogen.sh @@ -89,7 +89,7 @@ function install_mongoc_driver # function cleanup { - rm config.h + rm -f config.h touch config.h } From 3c5b1f524074768f1c84a2ded7bfad65c01b3228 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Fri, 27 Sep 2019 10:21:41 +0530 Subject: [PATCH 118/239] Support PG/EPAS 12, Commit fixes the issues which caused compilation error when compiled against PG/EPAS 12. --- Makefile | 4 ++-- mongo_fdw.c | 15 +++++++++++++-- mongo_fdw.h | 4 +++- mongo_query.c | 9 +++++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 16f2624..d7813ec 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) endif diff --git a/mongo_fdw.c b/mongo_fdw.c index 2d07473..9c23303 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -22,6 +22,9 @@ #include "mongo_query.h" #include "access/reloptions.h" +#if PG_VERSION_NUM >= 120000 + #include "access/table.h" +#endif #include "catalog/pg_type.h" #include "commands/defrem.h" #include "commands/explain.h" @@ -30,6 +33,9 @@ #include "foreign/foreign.h" #include "nodes/makefuncs.h" #include "optimizer/cost.h" +#if PG_VERSION_NUM >= 120000 + #include "optimizer/optimizer.h" +#endif #include "optimizer/pathnode.h" #include "optimizer/plancat.h" #include "optimizer/planmain.h" @@ -57,7 +63,9 @@ #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" -#include "optimizer/var.h" +#if PG_VERSION_NUM < 120000 + #include "optimizer/var.h" +#endif #include "parser/parsetree.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -646,8 +654,11 @@ MongoPlanForeignModify(PlannerInfo *root, * Core code already has some lock on each rel being planned, so we can * use NoLock here. */ +#if PG_VERSION_NUM < 120000 rel = heap_open(rte->relid, NoLock); - +#else + rel = table_open(rte->relid, NoLock); +#endif if (operation == CMD_INSERT) { TupleDesc tupdesc = RelationGetDescr(rel); diff --git a/mongo_fdw.h b/mongo_fdw.h index d5dd1a7..623b0db 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -33,7 +33,9 @@ #include "catalog/pg_foreign_table.h" #include "utils/datetime.h" #include "nodes/pg_list.h" -#include "nodes/relation.h" +#if PG_VERSION_NUM < 120000 + #include "nodes/relation.h" +#endif #include "utils/timestamp.h" #include "access/reloptions.h" #include "catalog/pg_type.h" diff --git a/mongo_query.c b/mongo_query.c index cff6eeb..4efd61f 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -33,8 +33,13 @@ #include "catalog/pg_type.h" #include "nodes/makefuncs.h" -#include "nodes/relation.h" -#include "optimizer/var.h" +#if PG_VERSION_NUM < 120000 + #include "nodes/relation.h" + #include "optimizer/var.h" +#endif +#if PG_VERSION_NUM >= 120000 + #include "optimizer/optimizer.h" +#endif #include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" From 272b2933df837995aada1e99d059f4cb454021d8 Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Fri, 27 Sep 2019 16:52:20 +0530 Subject: [PATCH 119/239] Support PG/EPAS 12. Earlier commit missed to change the Makefile.meta and .legacy for the v12 support. So this commit fix the same as well as fix few other warning due to v12 API changes. --- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- mongo_fdw.c | 13 +++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Makefile.legacy b/Makefile.legacy index 16f2624..d7813ec 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index b696181..25b9c9b 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -43,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 or 11.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) + $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) endif diff --git a/mongo_fdw.c b/mongo_fdw.c index 9c23303..4306bd8 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -2016,7 +2016,6 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, bool *columnNulls = NULL; Oid foreignTableId = InvalidOid; TupleDesc tupleDescriptor = NULL; - Form_pg_attribute *attributesPtr = NULL; AttrNumber columnCount = 0; AttrNumber columnId = 0; HTAB *columnMappingHash = NULL; @@ -2035,16 +2034,22 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, /* create list of columns in the relation */ tupleDescriptor = RelationGetDescr(relation); columnCount = tupleDescriptor->natts; - attributesPtr = tupleDescriptor->attrs; for (columnId = 1; columnId <= columnCount; columnId++) { Var *column = (Var *) palloc0(sizeof(Var)); +#if PG_VERSION_NUM >= 120000 + Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId-1); + column->varattno = columnId; + column->vartype = attr->atttypid; + column->vartypmod = attr->atttypmod; +#else /* only assign required fields for column mapping hash */ column->varattno = columnId; - column->vartype = attributesPtr[columnId-1]->atttypid; - column->vartypmod = attributesPtr[columnId-1]->atttypmod; + column->vartype = tupleDescriptor->attrs[columnId-1]->atttypid; + column->vartypmod = tupleDescriptor->attrs[columnId-1]->atttypmod; +#endif columnList = lappend(columnList, column); } From 5eaa0bf98b895abeea7b06ea9c86ee6fd92ea25b Mon Sep 17 00:00:00 2001 From: Rushabh Lathia Date: Fri, 27 Sep 2019 17:32:23 +0530 Subject: [PATCH 120/239] Fix build for v11. This was broken as it's not using the correct APIs for fetch the tuple descriptor attributes. --- mongo_fdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 4306bd8..8375800 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -2038,7 +2038,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, for (columnId = 1; columnId <= columnCount; columnId++) { Var *column = (Var *) palloc0(sizeof(Var)); -#if PG_VERSION_NUM >= 120000 +#if PG_VERSION_NUM >= 110000 Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId-1); column->varattno = columnId; From 4416dc91c209173fc00f9834d21f9977adeecbbd Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 6 May 2020 19:30:10 +0530 Subject: [PATCH 121/239] Fix compiler warnings. FDW-83, Vaibhav Dalvi, reviewed by Suraj Kharage. --- mongo_fdw.c | 4 ++-- mongo_wrapper.c | 12 ++++++++---- mongo_wrapper.h | 10 +++++++++- mongo_wrapper_meta.c | 15 +++++++++------ 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 8375800..4724869 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1755,7 +1755,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) #ifndef META_DRIVER { - char *bsonData = bson_iterator_value(&i); + const char *bsonData = bson_iterator_value(&i); bson_iterator_from_buffer(&i, bsonData); } #endif @@ -1795,7 +1795,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) case BSON_TYPE_OID: { char oidhex[25]; - BsonOidToString(BsonIterOid(&i), (char**)&oidhex); + BsonOidToString(BsonIterOid(&i), oidhex); appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); break; } diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 08da8c2..5410f0c 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -398,7 +398,8 @@ JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) { bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - BsonOidFromString(&bsonObjectId, json_object_get_string(joj)); + BsonOidFromString(&bsonObjectId, + (char *)json_object_get_string(joj)); status = BsonAppendOid(bb, k , &bsonObjectId); break; } @@ -410,9 +411,12 @@ JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) } bson_append_start_object(bb , k); - json_object_object_foreach(v, kk, vv) + { - JsonToBsonAppendElement(bb, kk, vv); + json_object_object_foreach(v, kk, vv) + { + JsonToBsonAppendElement(bb, kk, vv); + } } bson_append_finish_object(bb); break; @@ -450,7 +454,7 @@ void BsonIteratorFromBuffer(BSON_ITERATOR * i, const char * buffer) bson_iterator_from_buffer(i, buffer); } -void BsonOidToString(bson_oid_t *o, char* str[25]) +void BsonOidToString(const bson_oid_t *o, char str[25]) { bson_oid_to_string (o, str); } diff --git a/mongo_wrapper.h b/mongo_wrapper.h index bd3020b..6d9d7ab 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -65,17 +65,25 @@ const char* BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); const char* BsonIterBinData(BSON_ITERATOR *it); int BsonIterBinLen(BSON_ITERATOR *it); #endif +#ifdef META_DRIVER +const bson_oid_t *BsonIterOid(BSON_ITERATOR *it); +#else bson_oid_t * BsonIterOid(BSON_ITERATOR *it); +#endif 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(bson_oid_t *o, char* str[25]); +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); +#ifdef META_DRIVER +const bson_value_t *BsonIterValue(BSON_ITERATOR *i); +#else const char* BsonIterValue(BSON_ITERATOR *i); +#endif void BsonIteratorFromBuffer(BSON_ITERATOR* i, const char * buffer); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 8c51b7d..001fca5 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -174,7 +174,7 @@ MongoCursorCreate(MONGO_CONN* conn, char* database, char *collection, BSON* q) bson_error_t error; c = mongoc_client_get_collection (conn, database, collection); - cur = mongoc_collection_find(c, MONGOC_QUERY_SLAVE_OK, 0, 0, 0, q, NULL, NULL); + cur = mongoc_collection_find_with_opts(c, q, NULL, NULL); mongoc_cursor_error(cur, &error); if (!cur) ereport(ERROR, (errmsg("failed to create cursor"), @@ -304,7 +304,7 @@ BsonIterBinData(BSON_ITERATOR *it, uint32_t *len) return (char*)binary; } -bson_oid_t * +const bson_oid_t * BsonIterOid(BSON_ITERATOR *it) { return bson_iter_oid(it); @@ -502,9 +502,12 @@ bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) break; } BsonAppendStartObject(bb , (char*)k, &t); - json_object_object_foreach(v, kk, vv) + { - JsonToBsonAppendElement(&t, kk, vv); + json_object_object_foreach(v, kk, vv) + { + JsonToBsonAppendElement(&t, kk, vv); + } } BsonAppendFinishObject(bb, &t); break; @@ -582,7 +585,7 @@ BsonIteratorFromBuffer(BSON_ITERATOR *i, const char * buffer) } void -BsonOidToString(bson_oid_t *o, char* str[25]) +BsonOidToString(const bson_oid_t *o, char str[25]) { bson_oid_to_string (o, str); } @@ -599,7 +602,7 @@ BsonIterRegex(BSON_ITERATOR *i) return bson_iter_regex(i, NULL); } -const char* +const bson_value_t* BsonIterValue(BSON_ITERATOR *i) { return bson_iter_value(i); From 27eb375bd151caaffc42dcf51dbb0bae09867848 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 6 May 2020 19:30:17 +0530 Subject: [PATCH 122/239] Fix and improve the regression test. FDW-104, Vaibhav Dalvi, further revised by Jeevan Chalke, reviewed by Rajkumar Raghuwanshi. --- expected/mongo_fdw.out | 1026 ++++------------------------------------ sql/mongo_fdw.sql | 30 +- 2 files changed, 106 insertions(+), 950 deletions(-) diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 8ab994c..50c873f 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -1,12 +1,12 @@ \c postgres postgres CREATE EXTENSION mongo_fdw; CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); -\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --maintainInsertionOrder --host='127.0.0.1' --port=27017 --quiet < data/mongo_fixture.json CREATE USER MAPPING FOR postgres SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); -INSERT INTO department VALUES(0, generate_series(1,10), 'dept - ' || generate_series(1,10)); -INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,10)); +INSERT INTO department SELECT 0, i, 'dept - ' || i FROM generate_series(1,10) i; +INSERT INTO employee SELECT 0, i, 'emp - ' || i, (i - 1)%10 + 1 FROM generate_series(1,100) i; SELECT count(*) FROM department; count ------- @@ -24,1147 +24,303 @@ EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , de ---------------------------------------------------------- Sort Sort Key: e.emp_id - -> Hash Join - Hash Cond: (e.emp_dept_id = d.department_id) - -> Foreign Scan on employee e - Foreign Namespace: testdb.employee - -> Hash + -> Merge Join + Merge Cond: (d.department_id = e.emp_dept_id) + -> Sort + Sort Key: d.department_id -> Foreign Scan on department d Foreign Namespace: testdb.department -(9 rows) + -> Sort + Sort Key: e.emp_dept_id + -> Foreign Scan on employee e + Foreign Namespace: testdb.employee +(12 rows) EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; - QUERY PLAN ------------------------------------------------------------------ + QUERY PLAN +---------------------------------------------------------------------- Sort Sort Key: e.emp_id - -> Hash Join - Hash Cond: (department.department_id = d.department_id) - -> Nested Loop - -> HashAggregate - Group Key: department.department_id - -> Foreign Scan on department - Foreign Namespace: testdb.department - -> Foreign Scan on employee e - Foreign Namespace: testdb.employee - -> Hash + -> Merge Join + Merge Cond: (department.department_id = d.department_id) + -> Sort + Sort Key: department.department_id + -> Nested Loop + -> HashAggregate + Group Key: department.department_id + -> Foreign Scan on department + Foreign Namespace: testdb.department + -> Foreign Scan on employee e + Foreign Namespace: testdb.employee + -> Sort + Sort Key: d.department_id -> Foreign Scan on department d Foreign Namespace: testdb.department -(14 rows) +(17 rows) -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id AND e.emp_dept_id > 5 ORDER by emp_id, department_id; emp_id | emp_name | emp_dept_id | department_id | department_name --------+-----------+-------------+---------------+----------------- - 1 | emp - 1 | 1 | 1 | dept - 1 - 2 | emp - 2 | 2 | 2 | dept - 2 - 3 | emp - 3 | 3 | 3 | dept - 3 - 4 | emp - 4 | 4 | 4 | dept - 4 - 5 | emp - 5 | 5 | 5 | dept - 5 6 | emp - 6 | 6 | 6 | dept - 6 7 | emp - 7 | 7 | 7 | dept - 7 8 | emp - 8 | 8 | 8 | dept - 8 9 | emp - 9 | 9 | 9 | dept - 9 10 | emp - 10 | 10 | 10 | dept - 10 - 11 | emp - 11 | 1 | 1 | dept - 1 - 12 | emp - 12 | 2 | 2 | dept - 2 - 13 | emp - 13 | 3 | 3 | dept - 3 - 14 | emp - 14 | 4 | 4 | dept - 4 - 15 | emp - 15 | 5 | 5 | dept - 5 16 | emp - 16 | 6 | 6 | dept - 6 17 | emp - 17 | 7 | 7 | dept - 7 18 | emp - 18 | 8 | 8 | dept - 8 19 | emp - 19 | 9 | 9 | dept - 9 20 | emp - 20 | 10 | 10 | dept - 10 - 21 | emp - 21 | 1 | 1 | dept - 1 - 22 | emp - 22 | 2 | 2 | dept - 2 - 23 | emp - 23 | 3 | 3 | dept - 3 - 24 | emp - 24 | 4 | 4 | dept - 4 - 25 | emp - 25 | 5 | 5 | dept - 5 26 | emp - 26 | 6 | 6 | dept - 6 27 | emp - 27 | 7 | 7 | dept - 7 28 | emp - 28 | 8 | 8 | dept - 8 29 | emp - 29 | 9 | 9 | dept - 9 30 | emp - 30 | 10 | 10 | dept - 10 - 31 | emp - 31 | 1 | 1 | dept - 1 - 32 | emp - 32 | 2 | 2 | dept - 2 - 33 | emp - 33 | 3 | 3 | dept - 3 - 34 | emp - 34 | 4 | 4 | dept - 4 - 35 | emp - 35 | 5 | 5 | dept - 5 36 | emp - 36 | 6 | 6 | dept - 6 37 | emp - 37 | 7 | 7 | dept - 7 38 | emp - 38 | 8 | 8 | dept - 8 39 | emp - 39 | 9 | 9 | dept - 9 40 | emp - 40 | 10 | 10 | dept - 10 - 41 | emp - 41 | 1 | 1 | dept - 1 - 42 | emp - 42 | 2 | 2 | dept - 2 - 43 | emp - 43 | 3 | 3 | dept - 3 - 44 | emp - 44 | 4 | 4 | dept - 4 - 45 | emp - 45 | 5 | 5 | dept - 5 46 | emp - 46 | 6 | 6 | dept - 6 47 | emp - 47 | 7 | 7 | dept - 7 48 | emp - 48 | 8 | 8 | dept - 8 49 | emp - 49 | 9 | 9 | dept - 9 50 | emp - 50 | 10 | 10 | dept - 10 - 51 | emp - 51 | 1 | 1 | dept - 1 - 52 | emp - 52 | 2 | 2 | dept - 2 - 53 | emp - 53 | 3 | 3 | dept - 3 - 54 | emp - 54 | 4 | 4 | dept - 4 - 55 | emp - 55 | 5 | 5 | dept - 5 56 | emp - 56 | 6 | 6 | dept - 6 57 | emp - 57 | 7 | 7 | dept - 7 58 | emp - 58 | 8 | 8 | dept - 8 59 | emp - 59 | 9 | 9 | dept - 9 60 | emp - 60 | 10 | 10 | dept - 10 - 61 | emp - 61 | 1 | 1 | dept - 1 - 62 | emp - 62 | 2 | 2 | dept - 2 - 63 | emp - 63 | 3 | 3 | dept - 3 - 64 | emp - 64 | 4 | 4 | dept - 4 - 65 | emp - 65 | 5 | 5 | dept - 5 66 | emp - 66 | 6 | 6 | dept - 6 67 | emp - 67 | 7 | 7 | dept - 7 68 | emp - 68 | 8 | 8 | dept - 8 69 | emp - 69 | 9 | 9 | dept - 9 70 | emp - 70 | 10 | 10 | dept - 10 - 71 | emp - 71 | 1 | 1 | dept - 1 - 72 | emp - 72 | 2 | 2 | dept - 2 - 73 | emp - 73 | 3 | 3 | dept - 3 - 74 | emp - 74 | 4 | 4 | dept - 4 - 75 | emp - 75 | 5 | 5 | dept - 5 76 | emp - 76 | 6 | 6 | dept - 6 77 | emp - 77 | 7 | 7 | dept - 7 78 | emp - 78 | 8 | 8 | dept - 8 79 | emp - 79 | 9 | 9 | dept - 9 80 | emp - 80 | 10 | 10 | dept - 10 - 81 | emp - 81 | 1 | 1 | dept - 1 - 82 | emp - 82 | 2 | 2 | dept - 2 - 83 | emp - 83 | 3 | 3 | dept - 3 - 84 | emp - 84 | 4 | 4 | dept - 4 - 85 | emp - 85 | 5 | 5 | dept - 5 86 | emp - 86 | 6 | 6 | dept - 6 87 | emp - 87 | 7 | 7 | dept - 7 88 | emp - 88 | 8 | 8 | dept - 8 89 | emp - 89 | 9 | 9 | dept - 9 90 | emp - 90 | 10 | 10 | dept - 10 - 91 | emp - 91 | 1 | 1 | dept - 1 - 92 | emp - 92 | 2 | 2 | dept - 2 - 93 | emp - 93 | 3 | 3 | dept - 3 - 94 | emp - 94 | 4 | 4 | dept - 4 - 95 | emp - 95 | 5 | 5 | dept - 5 96 | emp - 96 | 6 | 6 | dept - 6 97 | emp - 97 | 7 | 7 | dept - 7 98 | emp - 98 | 8 | 8 | dept - 8 99 | emp - 99 | 9 | 9 | dept - 9 100 | emp - 100 | 10 | 10 | dept - 10 -(100 rows) +(50 rows) -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department WHERE department_id < 3) ORDER by emp_id, department_id; emp_id | emp_name | emp_dept_id | department_id | department_name --------+-----------+-------------+---------------+----------------- - 1 | emp - 1 | 1 | 10 | dept - 10 - 1 | emp - 1 | 1 | 6 | dept - 6 1 | emp - 1 | 1 | 1 | dept - 1 - 1 | emp - 1 | 1 | 3 | dept - 3 - 1 | emp - 1 | 1 | 8 | dept - 8 - 1 | emp - 1 | 1 | 5 | dept - 5 - 1 | emp - 1 | 1 | 9 | dept - 9 - 1 | emp - 1 | 1 | 4 | dept - 4 1 | emp - 1 | 1 | 2 | dept - 2 - 1 | emp - 1 | 1 | 7 | dept - 7 - 2 | emp - 2 | 2 | 8 | dept - 8 - 2 | emp - 2 | 2 | 7 | dept - 7 - 2 | emp - 2 | 2 | 5 | dept - 5 2 | emp - 2 | 2 | 1 | dept - 1 - 2 | emp - 2 | 2 | 3 | dept - 3 2 | emp - 2 | 2 | 2 | dept - 2 - 2 | emp - 2 | 2 | 4 | dept - 4 - 2 | emp - 2 | 2 | 9 | dept - 9 - 2 | emp - 2 | 2 | 10 | dept - 10 - 2 | emp - 2 | 2 | 6 | dept - 6 - 3 | emp - 3 | 3 | 10 | dept - 10 3 | emp - 3 | 3 | 1 | dept - 1 - 3 | emp - 3 | 3 | 7 | dept - 7 - 3 | emp - 3 | 3 | 6 | dept - 6 - 3 | emp - 3 | 3 | 4 | dept - 4 - 3 | emp - 3 | 3 | 8 | dept - 8 - 3 | emp - 3 | 3 | 5 | dept - 5 - 3 | emp - 3 | 3 | 3 | dept - 3 - 3 | emp - 3 | 3 | 9 | dept - 9 3 | emp - 3 | 3 | 2 | dept - 2 - 4 | emp - 4 | 4 | 3 | dept - 3 4 | emp - 4 | 4 | 1 | dept - 1 4 | emp - 4 | 4 | 2 | dept - 2 - 4 | emp - 4 | 4 | 8 | dept - 8 - 4 | emp - 4 | 4 | 7 | dept - 7 - 4 | emp - 4 | 4 | 9 | dept - 9 - 4 | emp - 4 | 4 | 4 | dept - 4 - 4 | emp - 4 | 4 | 6 | dept - 6 - 4 | emp - 4 | 4 | 10 | dept - 10 - 4 | emp - 4 | 4 | 5 | dept - 5 - 5 | emp - 5 | 5 | 6 | dept - 6 - 5 | emp - 5 | 5 | 3 | dept - 3 - 5 | emp - 5 | 5 | 7 | dept - 7 - 5 | emp - 5 | 5 | 9 | dept - 9 - 5 | emp - 5 | 5 | 2 | dept - 2 - 5 | emp - 5 | 5 | 5 | dept - 5 5 | emp - 5 | 5 | 1 | dept - 1 - 5 | emp - 5 | 5 | 8 | dept - 8 - 5 | emp - 5 | 5 | 4 | dept - 4 - 5 | emp - 5 | 5 | 10 | dept - 10 + 5 | emp - 5 | 5 | 2 | dept - 2 6 | emp - 6 | 6 | 1 | dept - 1 - 6 | emp - 6 | 6 | 7 | dept - 7 - 6 | emp - 6 | 6 | 5 | dept - 5 6 | emp - 6 | 6 | 2 | dept - 2 - 6 | emp - 6 | 6 | 9 | dept - 9 - 6 | emp - 6 | 6 | 4 | dept - 4 - 6 | emp - 6 | 6 | 10 | dept - 10 - 6 | emp - 6 | 6 | 6 | dept - 6 - 6 | emp - 6 | 6 | 8 | dept - 8 - 6 | emp - 6 | 6 | 3 | dept - 3 - 7 | emp - 7 | 7 | 4 | dept - 4 - 7 | emp - 7 | 7 | 7 | dept - 7 - 7 | emp - 7 | 7 | 2 | dept - 2 - 7 | emp - 7 | 7 | 5 | dept - 5 - 7 | emp - 7 | 7 | 3 | dept - 3 - 7 | emp - 7 | 7 | 10 | dept - 10 - 7 | emp - 7 | 7 | 6 | dept - 6 - 7 | emp - 7 | 7 | 9 | dept - 9 7 | emp - 7 | 7 | 1 | dept - 1 - 7 | emp - 7 | 7 | 8 | dept - 8 - 8 | emp - 8 | 8 | 10 | dept - 10 - 8 | emp - 8 | 8 | 4 | dept - 4 - 8 | emp - 8 | 8 | 2 | dept - 2 - 8 | emp - 8 | 8 | 3 | dept - 3 - 8 | emp - 8 | 8 | 6 | dept - 6 - 8 | emp - 8 | 8 | 8 | dept - 8 - 8 | emp - 8 | 8 | 5 | dept - 5 - 8 | emp - 8 | 8 | 7 | dept - 7 - 8 | emp - 8 | 8 | 9 | dept - 9 + 7 | emp - 7 | 7 | 2 | dept - 2 8 | emp - 8 | 8 | 1 | dept - 1 - 9 | emp - 9 | 9 | 10 | dept - 10 - 9 | emp - 9 | 9 | 4 | dept - 4 - 9 | emp - 9 | 9 | 5 | dept - 5 + 8 | emp - 8 | 8 | 2 | dept - 2 9 | emp - 9 | 9 | 1 | dept - 1 - 9 | emp - 9 | 9 | 6 | dept - 6 - 9 | emp - 9 | 9 | 9 | dept - 9 - 9 | emp - 9 | 9 | 8 | dept - 8 - 9 | emp - 9 | 9 | 3 | dept - 3 9 | emp - 9 | 9 | 2 | dept - 2 - 9 | emp - 9 | 9 | 7 | dept - 7 - 10 | emp - 10 | 10 | 4 | dept - 4 - 10 | emp - 10 | 10 | 2 | dept - 2 - 10 | emp - 10 | 10 | 7 | dept - 7 - 10 | emp - 10 | 10 | 10 | dept - 10 - 10 | emp - 10 | 10 | 9 | dept - 9 - 10 | emp - 10 | 10 | 6 | dept - 6 - 10 | emp - 10 | 10 | 5 | dept - 5 10 | emp - 10 | 10 | 1 | dept - 1 - 10 | emp - 10 | 10 | 3 | dept - 3 - 10 | emp - 10 | 10 | 8 | dept - 8 - 11 | emp - 11 | 1 | 8 | dept - 8 - 11 | emp - 11 | 1 | 2 | dept - 2 - 11 | emp - 11 | 1 | 9 | dept - 9 - 11 | emp - 11 | 1 | 10 | dept - 10 - 11 | emp - 11 | 1 | 6 | dept - 6 + 10 | emp - 10 | 10 | 2 | dept - 2 11 | emp - 11 | 1 | 1 | dept - 1 - 11 | emp - 11 | 1 | 3 | dept - 3 - 11 | emp - 11 | 1 | 4 | dept - 4 - 11 | emp - 11 | 1 | 5 | dept - 5 - 11 | emp - 11 | 1 | 7 | dept - 7 - 12 | emp - 12 | 2 | 4 | dept - 4 - 12 | emp - 12 | 2 | 5 | dept - 5 - 12 | emp - 12 | 2 | 8 | dept - 8 - 12 | emp - 12 | 2 | 7 | dept - 7 - 12 | emp - 12 | 2 | 3 | dept - 3 - 12 | emp - 12 | 2 | 10 | dept - 10 - 12 | emp - 12 | 2 | 6 | dept - 6 - 12 | emp - 12 | 2 | 2 | dept - 2 + 11 | emp - 11 | 1 | 2 | dept - 2 12 | emp - 12 | 2 | 1 | dept - 1 - 12 | emp - 12 | 2 | 9 | dept - 9 - 13 | emp - 13 | 3 | 3 | dept - 3 - 13 | emp - 13 | 3 | 2 | dept - 2 - 13 | emp - 13 | 3 | 5 | dept - 5 + 12 | emp - 12 | 2 | 2 | dept - 2 13 | emp - 13 | 3 | 1 | dept - 1 - 13 | emp - 13 | 3 | 8 | dept - 8 - 13 | emp - 13 | 3 | 7 | dept - 7 - 13 | emp - 13 | 3 | 10 | dept - 10 - 13 | emp - 13 | 3 | 9 | dept - 9 - 13 | emp - 13 | 3 | 6 | dept - 6 - 13 | emp - 13 | 3 | 4 | dept - 4 - 14 | emp - 14 | 4 | 8 | dept - 8 - 14 | emp - 14 | 4 | 9 | dept - 9 - 14 | emp - 14 | 4 | 3 | dept - 3 - 14 | emp - 14 | 4 | 6 | dept - 6 + 13 | emp - 13 | 3 | 2 | dept - 2 14 | emp - 14 | 4 | 1 | dept - 1 - 14 | emp - 14 | 4 | 4 | dept - 4 - 14 | emp - 14 | 4 | 10 | dept - 10 - 14 | emp - 14 | 4 | 5 | dept - 5 - 14 | emp - 14 | 4 | 7 | dept - 7 14 | emp - 14 | 4 | 2 | dept - 2 - 15 | emp - 15 | 5 | 9 | dept - 9 - 15 | emp - 15 | 5 | 8 | dept - 8 - 15 | emp - 15 | 5 | 10 | dept - 10 - 15 | emp - 15 | 5 | 4 | dept - 4 - 15 | emp - 15 | 5 | 2 | dept - 2 - 15 | emp - 15 | 5 | 3 | dept - 3 - 15 | emp - 15 | 5 | 6 | dept - 6 - 15 | emp - 15 | 5 | 7 | dept - 7 - 15 | emp - 15 | 5 | 5 | dept - 5 15 | emp - 15 | 5 | 1 | dept - 1 - 16 | emp - 16 | 6 | 10 | dept - 10 - 16 | emp - 16 | 6 | 3 | dept - 3 - 16 | emp - 16 | 6 | 2 | dept - 2 - 16 | emp - 16 | 6 | 9 | dept - 9 - 16 | emp - 16 | 6 | 7 | dept - 7 + 15 | emp - 15 | 5 | 2 | dept - 2 16 | emp - 16 | 6 | 1 | dept - 1 - 16 | emp - 16 | 6 | 8 | dept - 8 - 16 | emp - 16 | 6 | 5 | dept - 5 - 16 | emp - 16 | 6 | 6 | dept - 6 - 16 | emp - 16 | 6 | 4 | dept - 4 - 17 | emp - 17 | 7 | 5 | dept - 5 - 17 | emp - 17 | 7 | 6 | dept - 6 - 17 | emp - 17 | 7 | 4 | dept - 4 - 17 | emp - 17 | 7 | 8 | dept - 8 - 17 | emp - 17 | 7 | 7 | dept - 7 - 17 | emp - 17 | 7 | 9 | dept - 9 + 16 | emp - 16 | 6 | 2 | dept - 2 17 | emp - 17 | 7 | 1 | dept - 1 17 | emp - 17 | 7 | 2 | dept - 2 - 17 | emp - 17 | 7 | 10 | dept - 10 - 17 | emp - 17 | 7 | 3 | dept - 3 - 18 | emp - 18 | 8 | 4 | dept - 4 18 | emp - 18 | 8 | 1 | dept - 1 - 18 | emp - 18 | 8 | 3 | dept - 3 18 | emp - 18 | 8 | 2 | dept - 2 - 18 | emp - 18 | 8 | 8 | dept - 8 - 18 | emp - 18 | 8 | 7 | dept - 7 - 18 | emp - 18 | 8 | 5 | dept - 5 - 18 | emp - 18 | 8 | 10 | dept - 10 - 18 | emp - 18 | 8 | 9 | dept - 9 - 18 | emp - 18 | 8 | 6 | dept - 6 19 | emp - 19 | 9 | 1 | dept - 1 - 19 | emp - 19 | 9 | 9 | dept - 9 19 | emp - 19 | 9 | 2 | dept - 2 - 19 | emp - 19 | 9 | 8 | dept - 8 - 19 | emp - 19 | 9 | 6 | dept - 6 - 19 | emp - 19 | 9 | 4 | dept - 4 - 19 | emp - 19 | 9 | 10 | dept - 10 - 19 | emp - 19 | 9 | 3 | dept - 3 - 19 | emp - 19 | 9 | 5 | dept - 5 - 19 | emp - 19 | 9 | 7 | dept - 7 - 20 | emp - 20 | 10 | 4 | dept - 4 - 20 | emp - 20 | 10 | 2 | dept - 2 - 20 | emp - 20 | 10 | 9 | dept - 9 - 20 | emp - 20 | 10 | 7 | dept - 7 20 | emp - 20 | 10 | 1 | dept - 1 - 20 | emp - 20 | 10 | 3 | dept - 3 - 20 | emp - 20 | 10 | 6 | dept - 6 - 20 | emp - 20 | 10 | 8 | dept - 8 - 20 | emp - 20 | 10 | 10 | dept - 10 - 20 | emp - 20 | 10 | 5 | dept - 5 - 21 | emp - 21 | 1 | 5 | dept - 5 - 21 | emp - 21 | 1 | 6 | dept - 6 - 21 | emp - 21 | 1 | 4 | dept - 4 + 20 | emp - 20 | 10 | 2 | dept - 2 21 | emp - 21 | 1 | 1 | dept - 1 - 21 | emp - 21 | 1 | 9 | dept - 9 - 21 | emp - 21 | 1 | 3 | dept - 3 - 21 | emp - 21 | 1 | 10 | dept - 10 - 21 | emp - 21 | 1 | 7 | dept - 7 21 | emp - 21 | 1 | 2 | dept - 2 - 21 | emp - 21 | 1 | 8 | dept - 8 22 | emp - 22 | 2 | 1 | dept - 1 - 22 | emp - 22 | 2 | 4 | dept - 4 - 22 | emp - 22 | 2 | 3 | dept - 3 - 22 | emp - 22 | 2 | 5 | dept - 5 - 22 | emp - 22 | 2 | 6 | dept - 6 - 22 | emp - 22 | 2 | 8 | dept - 8 - 22 | emp - 22 | 2 | 10 | dept - 10 - 22 | emp - 22 | 2 | 7 | dept - 7 - 22 | emp - 22 | 2 | 9 | dept - 9 22 | emp - 22 | 2 | 2 | dept - 2 - 23 | emp - 23 | 3 | 5 | dept - 5 - 23 | emp - 23 | 3 | 9 | dept - 9 23 | emp - 23 | 3 | 1 | dept - 1 - 23 | emp - 23 | 3 | 6 | dept - 6 - 23 | emp - 23 | 3 | 8 | dept - 8 - 23 | emp - 23 | 3 | 7 | dept - 7 - 23 | emp - 23 | 3 | 10 | dept - 10 - 23 | emp - 23 | 3 | 3 | dept - 3 23 | emp - 23 | 3 | 2 | dept - 2 - 23 | emp - 23 | 3 | 4 | dept - 4 - 24 | emp - 24 | 4 | 4 | dept - 4 - 24 | emp - 24 | 4 | 9 | dept - 9 - 24 | emp - 24 | 4 | 7 | dept - 7 - 24 | emp - 24 | 4 | 5 | dept - 5 - 24 | emp - 24 | 4 | 2 | dept - 2 - 24 | emp - 24 | 4 | 8 | dept - 8 - 24 | emp - 24 | 4 | 3 | dept - 3 24 | emp - 24 | 4 | 1 | dept - 1 - 24 | emp - 24 | 4 | 6 | dept - 6 - 24 | emp - 24 | 4 | 10 | dept - 10 - 25 | emp - 25 | 5 | 5 | dept - 5 - 25 | emp - 25 | 5 | 10 | dept - 10 - 25 | emp - 25 | 5 | 8 | dept - 8 - 25 | emp - 25 | 5 | 7 | dept - 7 - 25 | emp - 25 | 5 | 3 | dept - 3 - 25 | emp - 25 | 5 | 6 | dept - 6 - 25 | emp - 25 | 5 | 2 | dept - 2 - 25 | emp - 25 | 5 | 9 | dept - 9 + 24 | emp - 24 | 4 | 2 | dept - 2 25 | emp - 25 | 5 | 1 | dept - 1 - 25 | emp - 25 | 5 | 4 | dept - 4 - 26 | emp - 26 | 6 | 8 | dept - 8 - 26 | emp - 26 | 6 | 10 | dept - 10 - 26 | emp - 26 | 6 | 6 | dept - 6 - 26 | emp - 26 | 6 | 4 | dept - 4 - 26 | emp - 26 | 6 | 7 | dept - 7 - 26 | emp - 26 | 6 | 5 | dept - 5 - 26 | emp - 26 | 6 | 3 | dept - 3 + 25 | emp - 25 | 5 | 2 | dept - 2 26 | emp - 26 | 6 | 1 | dept - 1 - 26 | emp - 26 | 6 | 9 | dept - 9 26 | emp - 26 | 6 | 2 | dept - 2 - 27 | emp - 27 | 7 | 4 | dept - 4 - 27 | emp - 27 | 7 | 7 | dept - 7 - 27 | emp - 27 | 7 | 2 | dept - 2 - 27 | emp - 27 | 7 | 6 | dept - 6 - 27 | emp - 27 | 7 | 8 | dept - 8 - 27 | emp - 27 | 7 | 3 | dept - 3 27 | emp - 27 | 7 | 1 | dept - 1 - 27 | emp - 27 | 7 | 10 | dept - 10 - 27 | emp - 27 | 7 | 9 | dept - 9 - 27 | emp - 27 | 7 | 5 | dept - 5 - 28 | emp - 28 | 8 | 5 | dept - 5 - 28 | emp - 28 | 8 | 2 | dept - 2 - 28 | emp - 28 | 8 | 9 | dept - 9 - 28 | emp - 28 | 8 | 4 | dept - 4 - 28 | emp - 28 | 8 | 7 | dept - 7 - 28 | emp - 28 | 8 | 3 | dept - 3 + 27 | emp - 27 | 7 | 2 | dept - 2 28 | emp - 28 | 8 | 1 | dept - 1 - 28 | emp - 28 | 8 | 8 | dept - 8 - 28 | emp - 28 | 8 | 6 | dept - 6 - 28 | emp - 28 | 8 | 10 | dept - 10 - 29 | emp - 29 | 9 | 10 | dept - 10 - 29 | emp - 29 | 9 | 9 | dept - 9 - 29 | emp - 29 | 9 | 6 | dept - 6 - 29 | emp - 29 | 9 | 4 | dept - 4 - 29 | emp - 29 | 9 | 3 | dept - 3 - 29 | emp - 29 | 9 | 8 | dept - 8 - 29 | emp - 29 | 9 | 5 | dept - 5 - 29 | emp - 29 | 9 | 7 | dept - 7 - 29 | emp - 29 | 9 | 2 | dept - 2 + 28 | emp - 28 | 8 | 2 | dept - 2 29 | emp - 29 | 9 | 1 | dept - 1 - 30 | emp - 30 | 10 | 3 | dept - 3 - 30 | emp - 30 | 10 | 9 | dept - 9 + 29 | emp - 29 | 9 | 2 | dept - 2 30 | emp - 30 | 10 | 1 | dept - 1 - 30 | emp - 30 | 10 | 6 | dept - 6 - 30 | emp - 30 | 10 | 5 | dept - 5 - 30 | emp - 30 | 10 | 10 | dept - 10 - 30 | emp - 30 | 10 | 8 | dept - 8 - 30 | emp - 30 | 10 | 7 | dept - 7 - 30 | emp - 30 | 10 | 4 | dept - 4 30 | emp - 30 | 10 | 2 | dept - 2 31 | emp - 31 | 1 | 1 | dept - 1 31 | emp - 31 | 1 | 2 | dept - 2 - 31 | emp - 31 | 1 | 8 | dept - 8 - 31 | emp - 31 | 1 | 5 | dept - 5 - 31 | emp - 31 | 1 | 6 | dept - 6 - 31 | emp - 31 | 1 | 10 | dept - 10 - 31 | emp - 31 | 1 | 3 | dept - 3 - 31 | emp - 31 | 1 | 7 | dept - 7 - 31 | emp - 31 | 1 | 9 | dept - 9 - 31 | emp - 31 | 1 | 4 | dept - 4 - 32 | emp - 32 | 2 | 10 | dept - 10 - 32 | emp - 32 | 2 | 9 | dept - 9 - 32 | emp - 32 | 2 | 8 | dept - 8 - 32 | emp - 32 | 2 | 2 | dept - 2 - 32 | emp - 32 | 2 | 6 | dept - 6 32 | emp - 32 | 2 | 1 | dept - 1 - 32 | emp - 32 | 2 | 7 | dept - 7 - 32 | emp - 32 | 2 | 3 | dept - 3 - 32 | emp - 32 | 2 | 5 | dept - 5 - 32 | emp - 32 | 2 | 4 | dept - 4 - 33 | emp - 33 | 3 | 10 | dept - 10 - 33 | emp - 33 | 3 | 5 | dept - 5 - 33 | emp - 33 | 3 | 8 | dept - 8 - 33 | emp - 33 | 3 | 3 | dept - 3 - 33 | emp - 33 | 3 | 6 | dept - 6 - 33 | emp - 33 | 3 | 7 | dept - 7 - 33 | emp - 33 | 3 | 2 | dept - 2 + 32 | emp - 32 | 2 | 2 | dept - 2 33 | emp - 33 | 3 | 1 | dept - 1 - 33 | emp - 33 | 3 | 4 | dept - 4 - 33 | emp - 33 | 3 | 9 | dept - 9 - 34 | emp - 34 | 4 | 9 | dept - 9 - 34 | emp - 34 | 4 | 6 | dept - 6 - 34 | emp - 34 | 4 | 8 | dept - 8 - 34 | emp - 34 | 4 | 4 | dept - 4 - 34 | emp - 34 | 4 | 3 | dept - 3 - 34 | emp - 34 | 4 | 5 | dept - 5 - 34 | emp - 34 | 4 | 10 | dept - 10 - 34 | emp - 34 | 4 | 7 | dept - 7 + 33 | emp - 33 | 3 | 2 | dept - 2 34 | emp - 34 | 4 | 1 | dept - 1 34 | emp - 34 | 4 | 2 | dept - 2 - 35 | emp - 35 | 5 | 4 | dept - 4 - 35 | emp - 35 | 5 | 9 | dept - 9 - 35 | emp - 35 | 5 | 5 | dept - 5 - 35 | emp - 35 | 5 | 2 | dept - 2 - 35 | emp - 35 | 5 | 3 | dept - 3 - 35 | emp - 35 | 5 | 7 | dept - 7 - 35 | emp - 35 | 5 | 8 | dept - 8 - 35 | emp - 35 | 5 | 10 | dept - 10 - 35 | emp - 35 | 5 | 6 | dept - 6 35 | emp - 35 | 5 | 1 | dept - 1 - 36 | emp - 36 | 6 | 7 | dept - 7 - 36 | emp - 36 | 6 | 3 | dept - 3 - 36 | emp - 36 | 6 | 8 | dept - 8 + 35 | emp - 35 | 5 | 2 | dept - 2 36 | emp - 36 | 6 | 1 | dept - 1 - 36 | emp - 36 | 6 | 6 | dept - 6 - 36 | emp - 36 | 6 | 10 | dept - 10 - 36 | emp - 36 | 6 | 4 | dept - 4 - 36 | emp - 36 | 6 | 9 | dept - 9 36 | emp - 36 | 6 | 2 | dept - 2 - 36 | emp - 36 | 6 | 5 | dept - 5 - 37 | emp - 37 | 7 | 2 | dept - 2 - 37 | emp - 37 | 7 | 4 | dept - 4 - 37 | emp - 37 | 7 | 10 | dept - 10 - 37 | emp - 37 | 7 | 7 | dept - 7 - 37 | emp - 37 | 7 | 6 | dept - 6 - 37 | emp - 37 | 7 | 9 | dept - 9 - 37 | emp - 37 | 7 | 3 | dept - 3 - 37 | emp - 37 | 7 | 8 | dept - 8 - 37 | emp - 37 | 7 | 5 | dept - 5 37 | emp - 37 | 7 | 1 | dept - 1 + 37 | emp - 37 | 7 | 2 | dept - 2 38 | emp - 38 | 8 | 1 | dept - 1 - 38 | emp - 38 | 8 | 8 | dept - 8 - 38 | emp - 38 | 8 | 4 | dept - 4 - 38 | emp - 38 | 8 | 3 | dept - 3 - 38 | emp - 38 | 8 | 6 | dept - 6 - 38 | emp - 38 | 8 | 7 | dept - 7 38 | emp - 38 | 8 | 2 | dept - 2 - 38 | emp - 38 | 8 | 5 | dept - 5 - 38 | emp - 38 | 8 | 9 | dept - 9 - 38 | emp - 38 | 8 | 10 | dept - 10 - 39 | emp - 39 | 9 | 6 | dept - 6 39 | emp - 39 | 9 | 1 | dept - 1 - 39 | emp - 39 | 9 | 5 | dept - 5 - 39 | emp - 39 | 9 | 7 | dept - 7 39 | emp - 39 | 9 | 2 | dept - 2 - 39 | emp - 39 | 9 | 3 | dept - 3 - 39 | emp - 39 | 9 | 9 | dept - 9 - 39 | emp - 39 | 9 | 4 | dept - 4 - 39 | emp - 39 | 9 | 10 | dept - 10 - 39 | emp - 39 | 9 | 8 | dept - 8 - 40 | emp - 40 | 10 | 3 | dept - 3 - 40 | emp - 40 | 10 | 5 | dept - 5 40 | emp - 40 | 10 | 1 | dept - 1 - 40 | emp - 40 | 10 | 10 | dept - 10 - 40 | emp - 40 | 10 | 4 | dept - 4 - 40 | emp - 40 | 10 | 9 | dept - 9 - 40 | emp - 40 | 10 | 7 | dept - 7 40 | emp - 40 | 10 | 2 | dept - 2 - 40 | emp - 40 | 10 | 8 | dept - 8 - 40 | emp - 40 | 10 | 6 | dept - 6 - 41 | emp - 41 | 1 | 7 | dept - 7 - 41 | emp - 41 | 1 | 5 | dept - 5 - 41 | emp - 41 | 1 | 6 | dept - 6 - 41 | emp - 41 | 1 | 10 | dept - 10 41 | emp - 41 | 1 | 1 | dept - 1 - 41 | emp - 41 | 1 | 8 | dept - 8 - 41 | emp - 41 | 1 | 3 | dept - 3 - 41 | emp - 41 | 1 | 4 | dept - 4 - 41 | emp - 41 | 1 | 9 | dept - 9 41 | emp - 41 | 1 | 2 | dept - 2 - 42 | emp - 42 | 2 | 10 | dept - 10 - 42 | emp - 42 | 2 | 4 | dept - 4 - 42 | emp - 42 | 2 | 7 | dept - 7 42 | emp - 42 | 2 | 1 | dept - 1 - 42 | emp - 42 | 2 | 3 | dept - 3 - 42 | emp - 42 | 2 | 8 | dept - 8 - 42 | emp - 42 | 2 | 5 | dept - 5 42 | emp - 42 | 2 | 2 | dept - 2 - 42 | emp - 42 | 2 | 9 | dept - 9 - 42 | emp - 42 | 2 | 6 | dept - 6 - 43 | emp - 43 | 3 | 9 | dept - 9 - 43 | emp - 43 | 3 | 3 | dept - 3 - 43 | emp - 43 | 3 | 4 | dept - 4 - 43 | emp - 43 | 3 | 2 | dept - 2 - 43 | emp - 43 | 3 | 10 | dept - 10 43 | emp - 43 | 3 | 1 | dept - 1 - 43 | emp - 43 | 3 | 7 | dept - 7 - 43 | emp - 43 | 3 | 8 | dept - 8 - 43 | emp - 43 | 3 | 6 | dept - 6 - 43 | emp - 43 | 3 | 5 | dept - 5 - 44 | emp - 44 | 4 | 3 | dept - 3 - 44 | emp - 44 | 4 | 9 | dept - 9 - 44 | emp - 44 | 4 | 5 | dept - 5 - 44 | emp - 44 | 4 | 10 | dept - 10 - 44 | emp - 44 | 4 | 6 | dept - 6 - 44 | emp - 44 | 4 | 8 | dept - 8 - 44 | emp - 44 | 4 | 7 | dept - 7 - 44 | emp - 44 | 4 | 4 | dept - 4 - 44 | emp - 44 | 4 | 2 | dept - 2 + 43 | emp - 43 | 3 | 2 | dept - 2 44 | emp - 44 | 4 | 1 | dept - 1 - 45 | emp - 45 | 5 | 9 | dept - 9 - 45 | emp - 45 | 5 | 3 | dept - 3 - 45 | emp - 45 | 5 | 7 | dept - 7 + 44 | emp - 44 | 4 | 2 | dept - 2 45 | emp - 45 | 5 | 1 | dept - 1 - 45 | emp - 45 | 5 | 5 | dept - 5 45 | emp - 45 | 5 | 2 | dept - 2 - 45 | emp - 45 | 5 | 10 | dept - 10 - 45 | emp - 45 | 5 | 6 | dept - 6 - 45 | emp - 45 | 5 | 8 | dept - 8 - 45 | emp - 45 | 5 | 4 | dept - 4 - 46 | emp - 46 | 6 | 3 | dept - 3 - 46 | emp - 46 | 6 | 4 | dept - 4 - 46 | emp - 46 | 6 | 6 | dept - 6 - 46 | emp - 46 | 6 | 2 | dept - 2 46 | emp - 46 | 6 | 1 | dept - 1 - 46 | emp - 46 | 6 | 7 | dept - 7 - 46 | emp - 46 | 6 | 8 | dept - 8 - 46 | emp - 46 | 6 | 9 | dept - 9 - 46 | emp - 46 | 6 | 5 | dept - 5 - 46 | emp - 46 | 6 | 10 | dept - 10 - 47 | emp - 47 | 7 | 8 | dept - 8 - 47 | emp - 47 | 7 | 10 | dept - 10 - 47 | emp - 47 | 7 | 5 | dept - 5 - 47 | emp - 47 | 7 | 7 | dept - 7 - 47 | emp - 47 | 7 | 9 | dept - 9 - 47 | emp - 47 | 7 | 2 | dept - 2 + 46 | emp - 46 | 6 | 2 | dept - 2 47 | emp - 47 | 7 | 1 | dept - 1 - 47 | emp - 47 | 7 | 6 | dept - 6 - 47 | emp - 47 | 7 | 4 | dept - 4 - 47 | emp - 47 | 7 | 3 | dept - 3 + 47 | emp - 47 | 7 | 2 | dept - 2 48 | emp - 48 | 8 | 1 | dept - 1 - 48 | emp - 48 | 8 | 6 | dept - 6 - 48 | emp - 48 | 8 | 10 | dept - 10 - 48 | emp - 48 | 8 | 8 | dept - 8 - 48 | emp - 48 | 8 | 3 | dept - 3 - 48 | emp - 48 | 8 | 5 | dept - 5 - 48 | emp - 48 | 8 | 9 | dept - 9 - 48 | emp - 48 | 8 | 4 | dept - 4 - 48 | emp - 48 | 8 | 7 | dept - 7 48 | emp - 48 | 8 | 2 | dept - 2 - 49 | emp - 49 | 9 | 7 | dept - 7 - 49 | emp - 49 | 9 | 6 | dept - 6 - 49 | emp - 49 | 9 | 9 | dept - 9 - 49 | emp - 49 | 9 | 10 | dept - 10 - 49 | emp - 49 | 9 | 3 | dept - 3 - 49 | emp - 49 | 9 | 4 | dept - 4 - 49 | emp - 49 | 9 | 2 | dept - 2 - 49 | emp - 49 | 9 | 8 | dept - 8 49 | emp - 49 | 9 | 1 | dept - 1 - 49 | emp - 49 | 9 | 5 | dept - 5 + 49 | emp - 49 | 9 | 2 | dept - 2 50 | emp - 50 | 10 | 1 | dept - 1 - 50 | emp - 50 | 10 | 3 | dept - 3 - 50 | emp - 50 | 10 | 8 | dept - 8 - 50 | emp - 50 | 10 | 7 | dept - 7 - 50 | emp - 50 | 10 | 6 | dept - 6 - 50 | emp - 50 | 10 | 4 | dept - 4 - 50 | emp - 50 | 10 | 10 | dept - 10 - 50 | emp - 50 | 10 | 5 | dept - 5 - 50 | emp - 50 | 10 | 9 | dept - 9 50 | emp - 50 | 10 | 2 | dept - 2 51 | emp - 51 | 1 | 1 | dept - 1 - 51 | emp - 51 | 1 | 5 | dept - 5 - 51 | emp - 51 | 1 | 8 | dept - 8 - 51 | emp - 51 | 1 | 3 | dept - 3 - 51 | emp - 51 | 1 | 7 | dept - 7 - 51 | emp - 51 | 1 | 9 | dept - 9 - 51 | emp - 51 | 1 | 6 | dept - 6 - 51 | emp - 51 | 1 | 10 | dept - 10 51 | emp - 51 | 1 | 2 | dept - 2 - 51 | emp - 51 | 1 | 4 | dept - 4 - 52 | emp - 52 | 2 | 9 | dept - 9 - 52 | emp - 52 | 2 | 3 | dept - 3 - 52 | emp - 52 | 2 | 8 | dept - 8 - 52 | emp - 52 | 2 | 4 | dept - 4 - 52 | emp - 52 | 2 | 2 | dept - 2 - 52 | emp - 52 | 2 | 7 | dept - 7 52 | emp - 52 | 2 | 1 | dept - 1 - 52 | emp - 52 | 2 | 5 | dept - 5 - 52 | emp - 52 | 2 | 10 | dept - 10 - 52 | emp - 52 | 2 | 6 | dept - 6 - 53 | emp - 53 | 3 | 6 | dept - 6 - 53 | emp - 53 | 3 | 5 | dept - 5 - 53 | emp - 53 | 3 | 9 | dept - 9 - 53 | emp - 53 | 3 | 8 | dept - 8 + 52 | emp - 52 | 2 | 2 | dept - 2 53 | emp - 53 | 3 | 1 | dept - 1 - 53 | emp - 53 | 3 | 10 | dept - 10 - 53 | emp - 53 | 3 | 3 | dept - 3 - 53 | emp - 53 | 3 | 4 | dept - 4 53 | emp - 53 | 3 | 2 | dept - 2 - 53 | emp - 53 | 3 | 7 | dept - 7 - 54 | emp - 54 | 4 | 7 | dept - 7 - 54 | emp - 54 | 4 | 3 | dept - 3 - 54 | emp - 54 | 4 | 8 | dept - 8 - 54 | emp - 54 | 4 | 2 | dept - 2 - 54 | emp - 54 | 4 | 4 | dept - 4 - 54 | emp - 54 | 4 | 5 | dept - 5 - 54 | emp - 54 | 4 | 9 | dept - 9 54 | emp - 54 | 4 | 1 | dept - 1 - 54 | emp - 54 | 4 | 10 | dept - 10 - 54 | emp - 54 | 4 | 6 | dept - 6 + 54 | emp - 54 | 4 | 2 | dept - 2 55 | emp - 55 | 5 | 1 | dept - 1 - 55 | emp - 55 | 5 | 4 | dept - 4 - 55 | emp - 55 | 5 | 3 | dept - 3 - 55 | emp - 55 | 5 | 10 | dept - 10 - 55 | emp - 55 | 5 | 6 | dept - 6 - 55 | emp - 55 | 5 | 7 | dept - 7 55 | emp - 55 | 5 | 2 | dept - 2 - 55 | emp - 55 | 5 | 8 | dept - 8 - 55 | emp - 55 | 5 | 5 | dept - 5 - 55 | emp - 55 | 5 | 9 | dept - 9 - 56 | emp - 56 | 6 | 10 | dept - 10 - 56 | emp - 56 | 6 | 2 | dept - 2 - 56 | emp - 56 | 6 | 9 | dept - 9 - 56 | emp - 56 | 6 | 4 | dept - 4 - 56 | emp - 56 | 6 | 3 | dept - 3 - 56 | emp - 56 | 6 | 6 | dept - 6 56 | emp - 56 | 6 | 1 | dept - 1 - 56 | emp - 56 | 6 | 8 | dept - 8 - 56 | emp - 56 | 6 | 5 | dept - 5 - 56 | emp - 56 | 6 | 7 | dept - 7 - 57 | emp - 57 | 7 | 6 | dept - 6 - 57 | emp - 57 | 7 | 9 | dept - 9 - 57 | emp - 57 | 7 | 3 | dept - 3 + 56 | emp - 56 | 6 | 2 | dept - 2 57 | emp - 57 | 7 | 1 | dept - 1 - 57 | emp - 57 | 7 | 10 | dept - 10 - 57 | emp - 57 | 7 | 5 | dept - 5 57 | emp - 57 | 7 | 2 | dept - 2 - 57 | emp - 57 | 7 | 4 | dept - 4 - 57 | emp - 57 | 7 | 7 | dept - 7 - 57 | emp - 57 | 7 | 8 | dept - 8 - 58 | emp - 58 | 8 | 9 | dept - 9 - 58 | emp - 58 | 8 | 4 | dept - 4 - 58 | emp - 58 | 8 | 8 | dept - 8 - 58 | emp - 58 | 8 | 3 | dept - 3 - 58 | emp - 58 | 8 | 6 | dept - 6 - 58 | emp - 58 | 8 | 7 | dept - 7 - 58 | emp - 58 | 8 | 5 | dept - 5 - 58 | emp - 58 | 8 | 2 | dept - 2 - 58 | emp - 58 | 8 | 10 | dept - 10 58 | emp - 58 | 8 | 1 | dept - 1 + 58 | emp - 58 | 8 | 2 | dept - 2 59 | emp - 59 | 9 | 1 | dept - 1 - 59 | emp - 59 | 9 | 4 | dept - 4 - 59 | emp - 59 | 9 | 5 | dept - 5 - 59 | emp - 59 | 9 | 6 | dept - 6 - 59 | emp - 59 | 9 | 10 | dept - 10 - 59 | emp - 59 | 9 | 9 | dept - 9 - 59 | emp - 59 | 9 | 7 | dept - 7 - 59 | emp - 59 | 9 | 8 | dept - 8 59 | emp - 59 | 9 | 2 | dept - 2 - 59 | emp - 59 | 9 | 3 | dept - 3 - 60 | emp - 60 | 10 | 10 | dept - 10 - 60 | emp - 60 | 10 | 6 | dept - 6 60 | emp - 60 | 10 | 1 | dept - 1 - 60 | emp - 60 | 10 | 9 | dept - 9 - 60 | emp - 60 | 10 | 4 | dept - 4 - 60 | emp - 60 | 10 | 8 | dept - 8 60 | emp - 60 | 10 | 2 | dept - 2 - 60 | emp - 60 | 10 | 5 | dept - 5 - 60 | emp - 60 | 10 | 7 | dept - 7 - 60 | emp - 60 | 10 | 3 | dept - 3 61 | emp - 61 | 1 | 1 | dept - 1 - 61 | emp - 61 | 1 | 4 | dept - 4 - 61 | emp - 61 | 1 | 7 | dept - 7 - 61 | emp - 61 | 1 | 3 | dept - 3 - 61 | emp - 61 | 1 | 6 | dept - 6 - 61 | emp - 61 | 1 | 10 | dept - 10 - 61 | emp - 61 | 1 | 8 | dept - 8 61 | emp - 61 | 1 | 2 | dept - 2 - 61 | emp - 61 | 1 | 5 | dept - 5 - 61 | emp - 61 | 1 | 9 | dept - 9 - 62 | emp - 62 | 2 | 5 | dept - 5 62 | emp - 62 | 2 | 1 | dept - 1 - 62 | emp - 62 | 2 | 4 | dept - 4 - 62 | emp - 62 | 2 | 10 | dept - 10 - 62 | emp - 62 | 2 | 3 | dept - 3 - 62 | emp - 62 | 2 | 9 | dept - 9 - 62 | emp - 62 | 2 | 7 | dept - 7 - 62 | emp - 62 | 2 | 6 | dept - 6 - 62 | emp - 62 | 2 | 8 | dept - 8 62 | emp - 62 | 2 | 2 | dept - 2 - 63 | emp - 63 | 3 | 4 | dept - 4 - 63 | emp - 63 | 3 | 6 | dept - 6 - 63 | emp - 63 | 3 | 5 | dept - 5 - 63 | emp - 63 | 3 | 8 | dept - 8 63 | emp - 63 | 3 | 1 | dept - 1 63 | emp - 63 | 3 | 2 | dept - 2 - 63 | emp - 63 | 3 | 9 | dept - 9 - 63 | emp - 63 | 3 | 3 | dept - 3 - 63 | emp - 63 | 3 | 7 | dept - 7 - 63 | emp - 63 | 3 | 10 | dept - 10 - 64 | emp - 64 | 4 | 4 | dept - 4 - 64 | emp - 64 | 4 | 9 | dept - 9 64 | emp - 64 | 4 | 1 | dept - 1 64 | emp - 64 | 4 | 2 | dept - 2 - 64 | emp - 64 | 4 | 8 | dept - 8 - 64 | emp - 64 | 4 | 6 | dept - 6 - 64 | emp - 64 | 4 | 7 | dept - 7 - 64 | emp - 64 | 4 | 3 | dept - 3 - 64 | emp - 64 | 4 | 5 | dept - 5 - 64 | emp - 64 | 4 | 10 | dept - 10 - 65 | emp - 65 | 5 | 9 | dept - 9 - 65 | emp - 65 | 5 | 10 | dept - 10 - 65 | emp - 65 | 5 | 7 | dept - 7 65 | emp - 65 | 5 | 1 | dept - 1 65 | emp - 65 | 5 | 2 | dept - 2 - 65 | emp - 65 | 5 | 6 | dept - 6 - 65 | emp - 65 | 5 | 5 | dept - 5 - 65 | emp - 65 | 5 | 8 | dept - 8 - 65 | emp - 65 | 5 | 3 | dept - 3 - 65 | emp - 65 | 5 | 4 | dept - 4 - 66 | emp - 66 | 6 | 4 | dept - 4 - 66 | emp - 66 | 6 | 8 | dept - 8 - 66 | emp - 66 | 6 | 7 | dept - 7 - 66 | emp - 66 | 6 | 9 | dept - 9 - 66 | emp - 66 | 6 | 6 | dept - 6 66 | emp - 66 | 6 | 1 | dept - 1 - 66 | emp - 66 | 6 | 3 | dept - 3 - 66 | emp - 66 | 6 | 10 | dept - 10 - 66 | emp - 66 | 6 | 5 | dept - 5 66 | emp - 66 | 6 | 2 | dept - 2 - 67 | emp - 67 | 7 | 7 | dept - 7 - 67 | emp - 67 | 7 | 3 | dept - 3 - 67 | emp - 67 | 7 | 2 | dept - 2 - 67 | emp - 67 | 7 | 10 | dept - 10 - 67 | emp - 67 | 7 | 8 | dept - 8 - 67 | emp - 67 | 7 | 5 | dept - 5 - 67 | emp - 67 | 7 | 9 | dept - 9 67 | emp - 67 | 7 | 1 | dept - 1 - 67 | emp - 67 | 7 | 6 | dept - 6 - 67 | emp - 67 | 7 | 4 | dept - 4 - 68 | emp - 68 | 8 | 2 | dept - 2 + 67 | emp - 67 | 7 | 2 | dept - 2 68 | emp - 68 | 8 | 1 | dept - 1 - 68 | emp - 68 | 8 | 5 | dept - 5 - 68 | emp - 68 | 8 | 3 | dept - 3 - 68 | emp - 68 | 8 | 10 | dept - 10 - 68 | emp - 68 | 8 | 6 | dept - 6 - 68 | emp - 68 | 8 | 4 | dept - 4 - 68 | emp - 68 | 8 | 9 | dept - 9 - 68 | emp - 68 | 8 | 8 | dept - 8 - 68 | emp - 68 | 8 | 7 | dept - 7 - 69 | emp - 69 | 9 | 6 | dept - 6 - 69 | emp - 69 | 9 | 5 | dept - 5 - 69 | emp - 69 | 9 | 9 | dept - 9 - 69 | emp - 69 | 9 | 4 | dept - 4 - 69 | emp - 69 | 9 | 8 | dept - 8 + 68 | emp - 68 | 8 | 2 | dept - 2 69 | emp - 69 | 9 | 1 | dept - 1 - 69 | emp - 69 | 9 | 10 | dept - 10 69 | emp - 69 | 9 | 2 | dept - 2 - 69 | emp - 69 | 9 | 3 | dept - 3 - 69 | emp - 69 | 9 | 7 | dept - 7 - 70 | emp - 70 | 10 | 10 | dept - 10 - 70 | emp - 70 | 10 | 3 | dept - 3 - 70 | emp - 70 | 10 | 9 | dept - 9 - 70 | emp - 70 | 10 | 4 | dept - 4 - 70 | emp - 70 | 10 | 6 | dept - 6 - 70 | emp - 70 | 10 | 8 | dept - 8 - 70 | emp - 70 | 10 | 5 | dept - 5 - 70 | emp - 70 | 10 | 7 | dept - 7 70 | emp - 70 | 10 | 1 | dept - 1 70 | emp - 70 | 10 | 2 | dept - 2 - 71 | emp - 71 | 1 | 10 | dept - 10 - 71 | emp - 71 | 1 | 8 | dept - 8 - 71 | emp - 71 | 1 | 6 | dept - 6 - 71 | emp - 71 | 1 | 2 | dept - 2 - 71 | emp - 71 | 1 | 4 | dept - 4 - 71 | emp - 71 | 1 | 9 | dept - 9 - 71 | emp - 71 | 1 | 3 | dept - 3 - 71 | emp - 71 | 1 | 5 | dept - 5 71 | emp - 71 | 1 | 1 | dept - 1 - 71 | emp - 71 | 1 | 7 | dept - 7 - 72 | emp - 72 | 2 | 2 | dept - 2 - 72 | emp - 72 | 2 | 10 | dept - 10 + 71 | emp - 71 | 1 | 2 | dept - 2 72 | emp - 72 | 2 | 1 | dept - 1 - 72 | emp - 72 | 2 | 8 | dept - 8 - 72 | emp - 72 | 2 | 6 | dept - 6 - 72 | emp - 72 | 2 | 4 | dept - 4 - 72 | emp - 72 | 2 | 9 | dept - 9 - 72 | emp - 72 | 2 | 7 | dept - 7 - 72 | emp - 72 | 2 | 3 | dept - 3 - 72 | emp - 72 | 2 | 5 | dept - 5 - 73 | emp - 73 | 3 | 5 | dept - 5 - 73 | emp - 73 | 3 | 2 | dept - 2 + 72 | emp - 72 | 2 | 2 | dept - 2 73 | emp - 73 | 3 | 1 | dept - 1 - 73 | emp - 73 | 3 | 9 | dept - 9 - 73 | emp - 73 | 3 | 8 | dept - 8 - 73 | emp - 73 | 3 | 6 | dept - 6 - 73 | emp - 73 | 3 | 10 | dept - 10 - 73 | emp - 73 | 3 | 3 | dept - 3 - 73 | emp - 73 | 3 | 4 | dept - 4 - 73 | emp - 73 | 3 | 7 | dept - 7 - 74 | emp - 74 | 4 | 3 | dept - 3 - 74 | emp - 74 | 4 | 6 | dept - 6 - 74 | emp - 74 | 4 | 5 | dept - 5 - 74 | emp - 74 | 4 | 2 | dept - 2 - 74 | emp - 74 | 4 | 10 | dept - 10 + 73 | emp - 73 | 3 | 2 | dept - 2 74 | emp - 74 | 4 | 1 | dept - 1 - 74 | emp - 74 | 4 | 9 | dept - 9 - 74 | emp - 74 | 4 | 7 | dept - 7 - 74 | emp - 74 | 4 | 4 | dept - 4 - 74 | emp - 74 | 4 | 8 | dept - 8 - 75 | emp - 75 | 5 | 8 | dept - 8 - 75 | emp - 75 | 5 | 10 | dept - 10 - 75 | emp - 75 | 5 | 4 | dept - 4 - 75 | emp - 75 | 5 | 3 | dept - 3 - 75 | emp - 75 | 5 | 5 | dept - 5 - 75 | emp - 75 | 5 | 6 | dept - 6 - 75 | emp - 75 | 5 | 7 | dept - 7 - 75 | emp - 75 | 5 | 2 | dept - 2 + 74 | emp - 74 | 4 | 2 | dept - 2 75 | emp - 75 | 5 | 1 | dept - 1 - 75 | emp - 75 | 5 | 9 | dept - 9 - 76 | emp - 76 | 6 | 10 | dept - 10 - 76 | emp - 76 | 6 | 2 | dept - 2 - 76 | emp - 76 | 6 | 8 | dept - 8 - 76 | emp - 76 | 6 | 3 | dept - 3 - 76 | emp - 76 | 6 | 4 | dept - 4 - 76 | emp - 76 | 6 | 5 | dept - 5 - 76 | emp - 76 | 6 | 7 | dept - 7 + 75 | emp - 75 | 5 | 2 | dept - 2 76 | emp - 76 | 6 | 1 | dept - 1 - 76 | emp - 76 | 6 | 6 | dept - 6 - 76 | emp - 76 | 6 | 9 | dept - 9 - 77 | emp - 77 | 7 | 7 | dept - 7 - 77 | emp - 77 | 7 | 5 | dept - 5 - 77 | emp - 77 | 7 | 8 | dept - 8 - 77 | emp - 77 | 7 | 6 | dept - 6 - 77 | emp - 77 | 7 | 10 | dept - 10 - 77 | emp - 77 | 7 | 3 | dept - 3 - 77 | emp - 77 | 7 | 9 | dept - 9 - 77 | emp - 77 | 7 | 4 | dept - 4 + 76 | emp - 76 | 6 | 2 | dept - 2 77 | emp - 77 | 7 | 1 | dept - 1 77 | emp - 77 | 7 | 2 | dept - 2 - 78 | emp - 78 | 8 | 2 | dept - 2 - 78 | emp - 78 | 8 | 5 | dept - 5 78 | emp - 78 | 8 | 1 | dept - 1 - 78 | emp - 78 | 8 | 8 | dept - 8 - 78 | emp - 78 | 8 | 3 | dept - 3 - 78 | emp - 78 | 8 | 7 | dept - 7 - 78 | emp - 78 | 8 | 4 | dept - 4 - 78 | emp - 78 | 8 | 10 | dept - 10 - 78 | emp - 78 | 8 | 9 | dept - 9 - 78 | emp - 78 | 8 | 6 | dept - 6 - 79 | emp - 79 | 9 | 9 | dept - 9 - 79 | emp - 79 | 9 | 3 | dept - 3 - 79 | emp - 79 | 9 | 4 | dept - 4 - 79 | emp - 79 | 9 | 5 | dept - 5 - 79 | emp - 79 | 9 | 8 | dept - 8 - 79 | emp - 79 | 9 | 6 | dept - 6 - 79 | emp - 79 | 9 | 2 | dept - 2 + 78 | emp - 78 | 8 | 2 | dept - 2 79 | emp - 79 | 9 | 1 | dept - 1 - 79 | emp - 79 | 9 | 10 | dept - 10 - 79 | emp - 79 | 9 | 7 | dept - 7 - 80 | emp - 80 | 10 | 10 | dept - 10 + 79 | emp - 79 | 9 | 2 | dept - 2 80 | emp - 80 | 10 | 1 | dept - 1 - 80 | emp - 80 | 10 | 8 | dept - 8 - 80 | emp - 80 | 10 | 3 | dept - 3 80 | emp - 80 | 10 | 2 | dept - 2 - 80 | emp - 80 | 10 | 9 | dept - 9 - 80 | emp - 80 | 10 | 5 | dept - 5 - 80 | emp - 80 | 10 | 7 | dept - 7 - 80 | emp - 80 | 10 | 4 | dept - 4 - 80 | emp - 80 | 10 | 6 | dept - 6 - 81 | emp - 81 | 1 | 4 | dept - 4 - 81 | emp - 81 | 1 | 9 | dept - 9 - 81 | emp - 81 | 1 | 2 | dept - 2 81 | emp - 81 | 1 | 1 | dept - 1 - 81 | emp - 81 | 1 | 3 | dept - 3 - 81 | emp - 81 | 1 | 8 | dept - 8 - 81 | emp - 81 | 1 | 7 | dept - 7 - 81 | emp - 81 | 1 | 5 | dept - 5 - 81 | emp - 81 | 1 | 10 | dept - 10 - 81 | emp - 81 | 1 | 6 | dept - 6 - 82 | emp - 82 | 2 | 5 | dept - 5 - 82 | emp - 82 | 2 | 8 | dept - 8 - 82 | emp - 82 | 2 | 9 | dept - 9 - 82 | emp - 82 | 2 | 6 | dept - 6 - 82 | emp - 82 | 2 | 10 | dept - 10 - 82 | emp - 82 | 2 | 2 | dept - 2 - 82 | emp - 82 | 2 | 4 | dept - 4 + 81 | emp - 81 | 1 | 2 | dept - 2 82 | emp - 82 | 2 | 1 | dept - 1 - 82 | emp - 82 | 2 | 3 | dept - 3 - 82 | emp - 82 | 2 | 7 | dept - 7 - 83 | emp - 83 | 3 | 2 | dept - 2 - 83 | emp - 83 | 3 | 4 | dept - 4 - 83 | emp - 83 | 3 | 6 | dept - 6 - 83 | emp - 83 | 3 | 10 | dept - 10 - 83 | emp - 83 | 3 | 5 | dept - 5 - 83 | emp - 83 | 3 | 8 | dept - 8 + 82 | emp - 82 | 2 | 2 | dept - 2 83 | emp - 83 | 3 | 1 | dept - 1 - 83 | emp - 83 | 3 | 9 | dept - 9 - 83 | emp - 83 | 3 | 7 | dept - 7 - 83 | emp - 83 | 3 | 3 | dept - 3 - 84 | emp - 84 | 4 | 7 | dept - 7 - 84 | emp - 84 | 4 | 5 | dept - 5 - 84 | emp - 84 | 4 | 10 | dept - 10 - 84 | emp - 84 | 4 | 2 | dept - 2 - 84 | emp - 84 | 4 | 9 | dept - 9 - 84 | emp - 84 | 4 | 6 | dept - 6 - 84 | emp - 84 | 4 | 3 | dept - 3 - 84 | emp - 84 | 4 | 4 | dept - 4 + 83 | emp - 83 | 3 | 2 | dept - 2 84 | emp - 84 | 4 | 1 | dept - 1 - 84 | emp - 84 | 4 | 8 | dept - 8 - 85 | emp - 85 | 5 | 6 | dept - 6 - 85 | emp - 85 | 5 | 10 | dept - 10 - 85 | emp - 85 | 5 | 2 | dept - 2 - 85 | emp - 85 | 5 | 4 | dept - 4 + 84 | emp - 84 | 4 | 2 | dept - 2 85 | emp - 85 | 5 | 1 | dept - 1 - 85 | emp - 85 | 5 | 7 | dept - 7 - 85 | emp - 85 | 5 | 5 | dept - 5 - 85 | emp - 85 | 5 | 3 | dept - 3 - 85 | emp - 85 | 5 | 9 | dept - 9 - 85 | emp - 85 | 5 | 8 | dept - 8 - 86 | emp - 86 | 6 | 6 | dept - 6 - 86 | emp - 86 | 6 | 8 | dept - 8 - 86 | emp - 86 | 6 | 7 | dept - 7 - 86 | emp - 86 | 6 | 5 | dept - 5 - 86 | emp - 86 | 6 | 3 | dept - 3 - 86 | emp - 86 | 6 | 4 | dept - 4 + 85 | emp - 85 | 5 | 2 | dept - 2 86 | emp - 86 | 6 | 1 | dept - 1 86 | emp - 86 | 6 | 2 | dept - 2 - 86 | emp - 86 | 6 | 10 | dept - 10 - 86 | emp - 86 | 6 | 9 | dept - 9 - 87 | emp - 87 | 7 | 2 | dept - 2 - 87 | emp - 87 | 7 | 6 | dept - 6 - 87 | emp - 87 | 7 | 7 | dept - 7 - 87 | emp - 87 | 7 | 8 | dept - 8 - 87 | emp - 87 | 7 | 4 | dept - 4 - 87 | emp - 87 | 7 | 5 | dept - 5 87 | emp - 87 | 7 | 1 | dept - 1 - 87 | emp - 87 | 7 | 3 | dept - 3 - 87 | emp - 87 | 7 | 9 | dept - 9 - 87 | emp - 87 | 7 | 10 | dept - 10 - 88 | emp - 88 | 8 | 4 | dept - 4 - 88 | emp - 88 | 8 | 10 | dept - 10 - 88 | emp - 88 | 8 | 6 | dept - 6 - 88 | emp - 88 | 8 | 9 | dept - 9 + 87 | emp - 87 | 7 | 2 | dept - 2 88 | emp - 88 | 8 | 1 | dept - 1 - 88 | emp - 88 | 8 | 7 | dept - 7 88 | emp - 88 | 8 | 2 | dept - 2 - 88 | emp - 88 | 8 | 5 | dept - 5 - 88 | emp - 88 | 8 | 8 | dept - 8 - 88 | emp - 88 | 8 | 3 | dept - 3 - 89 | emp - 89 | 9 | 8 | dept - 8 - 89 | emp - 89 | 9 | 7 | dept - 7 - 89 | emp - 89 | 9 | 9 | dept - 9 - 89 | emp - 89 | 9 | 3 | dept - 3 - 89 | emp - 89 | 9 | 6 | dept - 6 - 89 | emp - 89 | 9 | 10 | dept - 10 - 89 | emp - 89 | 9 | 4 | dept - 4 - 89 | emp - 89 | 9 | 5 | dept - 5 89 | emp - 89 | 9 | 1 | dept - 1 89 | emp - 89 | 9 | 2 | dept - 2 - 90 | emp - 90 | 10 | 9 | dept - 9 - 90 | emp - 90 | 10 | 3 | dept - 3 90 | emp - 90 | 10 | 1 | dept - 1 - 90 | emp - 90 | 10 | 5 | dept - 5 - 90 | emp - 90 | 10 | 8 | dept - 8 - 90 | emp - 90 | 10 | 7 | dept - 7 - 90 | emp - 90 | 10 | 10 | dept - 10 - 90 | emp - 90 | 10 | 4 | dept - 4 90 | emp - 90 | 10 | 2 | dept - 2 - 90 | emp - 90 | 10 | 6 | dept - 6 - 91 | emp - 91 | 1 | 10 | dept - 10 - 91 | emp - 91 | 1 | 2 | dept - 2 - 91 | emp - 91 | 1 | 9 | dept - 9 - 91 | emp - 91 | 1 | 4 | dept - 4 - 91 | emp - 91 | 1 | 5 | dept - 5 - 91 | emp - 91 | 1 | 3 | dept - 3 - 91 | emp - 91 | 1 | 7 | dept - 7 - 91 | emp - 91 | 1 | 8 | dept - 8 91 | emp - 91 | 1 | 1 | dept - 1 - 91 | emp - 91 | 1 | 6 | dept - 6 - 92 | emp - 92 | 2 | 6 | dept - 6 - 92 | emp - 92 | 2 | 4 | dept - 4 - 92 | emp - 92 | 2 | 10 | dept - 10 + 91 | emp - 91 | 1 | 2 | dept - 2 92 | emp - 92 | 2 | 1 | dept - 1 - 92 | emp - 92 | 2 | 8 | dept - 8 - 92 | emp - 92 | 2 | 5 | dept - 5 - 92 | emp - 92 | 2 | 7 | dept - 7 92 | emp - 92 | 2 | 2 | dept - 2 - 92 | emp - 92 | 2 | 9 | dept - 9 - 92 | emp - 92 | 2 | 3 | dept - 3 - 93 | emp - 93 | 3 | 4 | dept - 4 - 93 | emp - 93 | 3 | 3 | dept - 3 - 93 | emp - 93 | 3 | 8 | dept - 8 93 | emp - 93 | 3 | 1 | dept - 1 93 | emp - 93 | 3 | 2 | dept - 2 - 93 | emp - 93 | 3 | 10 | dept - 10 - 93 | emp - 93 | 3 | 6 | dept - 6 - 93 | emp - 93 | 3 | 5 | dept - 5 - 93 | emp - 93 | 3 | 9 | dept - 9 - 93 | emp - 93 | 3 | 7 | dept - 7 - 94 | emp - 94 | 4 | 4 | dept - 4 - 94 | emp - 94 | 4 | 8 | dept - 8 - 94 | emp - 94 | 4 | 9 | dept - 9 - 94 | emp - 94 | 4 | 7 | dept - 7 - 94 | emp - 94 | 4 | 10 | dept - 10 - 94 | emp - 94 | 4 | 3 | dept - 3 - 94 | emp - 94 | 4 | 2 | dept - 2 - 94 | emp - 94 | 4 | 6 | dept - 6 94 | emp - 94 | 4 | 1 | dept - 1 - 94 | emp - 94 | 4 | 5 | dept - 5 - 95 | emp - 95 | 5 | 7 | dept - 7 - 95 | emp - 95 | 5 | 6 | dept - 6 - 95 | emp - 95 | 5 | 2 | dept - 2 - 95 | emp - 95 | 5 | 8 | dept - 8 - 95 | emp - 95 | 5 | 9 | dept - 9 - 95 | emp - 95 | 5 | 4 | dept - 4 + 94 | emp - 94 | 4 | 2 | dept - 2 95 | emp - 95 | 5 | 1 | dept - 1 - 95 | emp - 95 | 5 | 5 | dept - 5 - 95 | emp - 95 | 5 | 10 | dept - 10 - 95 | emp - 95 | 5 | 3 | dept - 3 - 96 | emp - 96 | 6 | 2 | dept - 2 - 96 | emp - 96 | 6 | 4 | dept - 4 - 96 | emp - 96 | 6 | 6 | dept - 6 - 96 | emp - 96 | 6 | 7 | dept - 7 - 96 | emp - 96 | 6 | 10 | dept - 10 - 96 | emp - 96 | 6 | 8 | dept - 8 + 95 | emp - 95 | 5 | 2 | dept - 2 96 | emp - 96 | 6 | 1 | dept - 1 - 96 | emp - 96 | 6 | 9 | dept - 9 - 96 | emp - 96 | 6 | 5 | dept - 5 - 96 | emp - 96 | 6 | 3 | dept - 3 + 96 | emp - 96 | 6 | 2 | dept - 2 97 | emp - 97 | 7 | 1 | dept - 1 97 | emp - 97 | 7 | 2 | dept - 2 - 97 | emp - 97 | 7 | 4 | dept - 4 - 97 | emp - 97 | 7 | 7 | dept - 7 - 97 | emp - 97 | 7 | 10 | dept - 10 - 97 | emp - 97 | 7 | 6 | dept - 6 - 97 | emp - 97 | 7 | 8 | dept - 8 - 97 | emp - 97 | 7 | 5 | dept - 5 - 97 | emp - 97 | 7 | 9 | dept - 9 - 97 | emp - 97 | 7 | 3 | dept - 3 - 98 | emp - 98 | 8 | 2 | dept - 2 - 98 | emp - 98 | 8 | 5 | dept - 5 - 98 | emp - 98 | 8 | 8 | dept - 8 - 98 | emp - 98 | 8 | 9 | dept - 9 - 98 | emp - 98 | 8 | 10 | dept - 10 - 98 | emp - 98 | 8 | 7 | dept - 7 - 98 | emp - 98 | 8 | 3 | dept - 3 - 98 | emp - 98 | 8 | 6 | dept - 6 - 98 | emp - 98 | 8 | 4 | dept - 4 98 | emp - 98 | 8 | 1 | dept - 1 - 99 | emp - 99 | 9 | 10 | dept - 10 - 99 | emp - 99 | 9 | 8 | dept - 8 - 99 | emp - 99 | 9 | 3 | dept - 3 - 99 | emp - 99 | 9 | 4 | dept - 4 + 98 | emp - 98 | 8 | 2 | dept - 2 99 | emp - 99 | 9 | 1 | dept - 1 - 99 | emp - 99 | 9 | 9 | dept - 9 - 99 | emp - 99 | 9 | 5 | dept - 5 99 | emp - 99 | 9 | 2 | dept - 2 - 99 | emp - 99 | 9 | 7 | dept - 7 - 99 | emp - 99 | 9 | 6 | dept - 6 - 100 | emp - 100 | 10 | 5 | dept - 5 - 100 | emp - 100 | 10 | 8 | dept - 8 - 100 | emp - 100 | 10 | 6 | dept - 6 - 100 | emp - 100 | 10 | 3 | dept - 3 - 100 | emp - 100 | 10 | 9 | dept - 9 - 100 | emp - 100 | 10 | 10 | dept - 10 100 | emp - 100 | 10 | 1 | dept - 1 100 | emp - 100 | 10 | 2 | dept - 2 - 100 | emp - 100 | 10 | 4 | dept - 4 - 100 | emp - 100 | 10 | 7 | dept - 7 -(1000 rows) +(200 rows) DELETE FROM employee WHERE emp_id = 10; UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; -SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; +SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp' ORDER BY emp_id; emp_id | emp_name --------+------------- 20 | Updated emp @@ -1367,7 +523,7 @@ population INTEGER, capital VARCHAR, hdi FLOAT ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM countries; +SELECT * FROM countries ORDER BY _id; _id | name | population | capital | hdi --------------------------+---------+------------+----------+------- 5381ccf9d6d81c8e8bf0434f | Ukraine | 45590000 | Kyiv | 0.74 @@ -1382,7 +538,7 @@ _id NAME, "lastElections.type" VARCHAR, "lastElections.date" TIMESTAMP ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM country_elections; +SELECT * FROM country_elections ORDER BY _id; _id | lastElections.type | lastElections.date --------------------------+--------------------+-------------------------- 5381ccf9d6d81c8e8bf0434f | presedential | Sun May 25 00:00:00 2014 @@ -1396,7 +552,7 @@ CREATE FOREIGN TABLE main_exports ( _id NAME, "mainExports" TEXT[] ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM main_exports; +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"} @@ -1407,34 +563,34 @@ SELECT * FROM main_exports; -- __doc tests -- the collection warehouse must contain the following data -- use testdb; --- db.warehouse.insert ({"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); --- db.warehouse.insert({"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); +-- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b90545859"),"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); +-- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b9054585a"),"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -SELECT * FROM test_json; +SELECT * FROM test_json ORDER BY __doc::text COLLATE "C"; __doc --------------------------------------------------------------------------------------------------------------------------------------------------------- { "_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 } } (2 rows) -SELECT * FROM test_jsonb; +SELECT * FROM test_jsonb ORDER BY __doc::text COLLATE "C"; __doc --------------------------------------------------------------------------------------------------------------------------------------------- {"_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}} (2 rows) -SELECT * FROM test_text; +SELECT * FROM test_text ORDER BY __doc::text COLLATE "C"; __doc --------------------------------------------------------------------------------------------------------------------------------------------------------- { "_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 } } (2 rows) -SELECT * FROM test_varchar; +SELECT * FROM test_varchar ORDER BY __doc::text COLLATE "C"; __doc --------------------------------------------------------------------------------------------------------------------------------------------------------- { "_id" : { "$oid" : "58a1ebbaf543ec0b90545859" }, "warehouse_id" : 1, "warehouse_name" : "UPS", "warehouse_created" : { "$date" : 1418368330000 } } diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index df7c158..1d86346 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -1,14 +1,14 @@ \c postgres postgres CREATE EXTENSION mongo_fdw; CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); -\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --quiet < data/mongo_fixture.json +\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --maintainInsertionOrder --host='127.0.0.1' --port=27017 --quiet < data/mongo_fixture.json CREATE USER MAPPING FOR postgres SERVER mongo_server; CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); -INSERT INTO department VALUES(0, generate_series(1,10), 'dept - ' || generate_series(1,10)); -INSERT INTO employee VALUES(0, generate_series(1,100), 'emp - ' || generate_series(1,100), generate_series(1,10)); +INSERT INTO department SELECT 0, i, 'dept - ' || i FROM generate_series(1,10) i; +INSERT INTO employee SELECT 0, i, 'emp - ' || i, (i - 1)%10 + 1 FROM generate_series(1,100) i; SELECT count(*) FROM department; SELECT count(*) FROM employee; @@ -17,13 +17,13 @@ EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , de EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id AND e.emp_dept_id > 5 ORDER by emp_id, department_id; +SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department WHERE department_id < 3) ORDER by emp_id, department_id; DELETE FROM employee WHERE emp_id = 10; UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; -SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp'; +SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp' ORDER BY emp_id; SELECT emp_id , emp_name , emp_dept_id FROM employee ORDER by emp_id LIMIT 10; SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1) ORDER by emp_id; @@ -48,7 +48,7 @@ population INTEGER, capital VARCHAR, hdi FLOAT ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM countries; +SELECT * FROM countries ORDER BY _id; -- -- Subfields and dates CREATE FOREIGN TABLE country_elections ( @@ -56,21 +56,21 @@ _id NAME, "lastElections.type" VARCHAR, "lastElections.date" TIMESTAMP ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM country_elections; +SELECT * FROM country_elections ORDER BY _id; -- -- Arrays CREATE FOREIGN TABLE main_exports ( _id NAME, "mainExports" TEXT[] ) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM main_exports; +SELECT * FROM main_exports ORDER BY _id; -- __doc tests -- the collection warehouse must contain the following data -- use testdb; --- db.warehouse.insert ({"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); --- db.warehouse.insert({"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); +-- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b90545859"),"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); +-- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b9054585a"),"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); @@ -78,10 +78,10 @@ CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (databa CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -SELECT * FROM test_json; -SELECT * FROM test_jsonb; -SELECT * FROM test_text; -SELECT * FROM test_varchar; +SELECT * FROM test_json ORDER BY __doc::text COLLATE "C"; +SELECT * FROM test_jsonb ORDER BY __doc::text COLLATE "C"; +SELECT * FROM test_text ORDER BY __doc::text COLLATE "C"; +SELECT * FROM test_varchar ORDER BY __doc::text COLLATE "C"; -- where clause push down test CREATE FOREIGN TABLE test_numbers(_id NAME, a int, b text) SERVER mongo_server OPTIONS (database 'testdb', collection 'test_numbers'); From c32aa067ef7ee6c86b515a6eeba9cc0a7839be84 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 6 May 2020 19:30:23 +0530 Subject: [PATCH 123/239] Add support for EPAS 13. This fixes the warnings and errors appearing when compiled with EPAS 13. FDW-102, Vaibhav Dalvi, reviewed by Suraj Kharage. --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- README.md | 2 +- connection.c | 3 +++ mongo_fdw.c | 21 +++++++++++++++++++++ 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index d7813ec..3e67c0d 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) + $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index d7813ec..3e67c0d 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -47,6 +47,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) + $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 25b9c9b..6c7448b 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -43,6 +43,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.3 9.4 9.5 9.6 10.0 11.0 12.0)) - $(error PostgreSQL 9.3, 9.4, 9.5, 9.6 10.0 11.0 or 12.0 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) + $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/README.md b/README.md index 9a08624..2e583e5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. -Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.3, 9.4, 9.5, 9.6 and 10. +Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 9.5, 9.6, 10, 11, 12, and 13. Installation ------------ diff --git a/connection.c b/connection.c index 4a3fcb4..66d806f 100644 --- a/connection.c +++ b/connection.c @@ -22,6 +22,9 @@ #include "mongo_fdw.h" #include "access/xact.h" +#if PG_VERSION_NUM >= 130000 +#include "common/hashfn.h" +#endif #include "mb/pg_wchar.h" #include "miscadmin.h" #include "utils/hsearch.h" diff --git a/mongo_fdw.c b/mongo_fdw.c index 4724869..35af687 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -29,6 +29,11 @@ #include "commands/defrem.h" #include "commands/explain.h" #include "commands/vacuum.h" +#if PG_VERSION_NUM >= 130000 +#include "common/hashfn.h" +#include "common/jsonapi.h" +#endif + #include "foreign/fdwapi.h" #include "foreign/foreign.h" #include "nodes/makefuncs.h" @@ -71,7 +76,11 @@ #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" +#if PG_VERSION_NUM < 130000 #include "utils/jsonapi.h" +#else +#include "utils/jsonfuncs.h" +#endif #include "utils/jsonb.h" #if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" @@ -168,11 +177,19 @@ const char * EscapeJsonString(const char *string); void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); /* the null action object used for pure validation */ +#if PG_VERSION_NUM < 130000 static 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 /* declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -712,7 +729,11 @@ MongoPlanForeignModify(PlannerInfo *root, if (plan->returningLists) elog(ERROR, "RETURNING is not supported by this FDW"); +#if PG_VERSION_NUM < 130000 heap_close(rel, NoLock); +#else + table_close(rel, NoLock); +#endif return list_make1(targetAttrs); } From 6c0a8f84d878d69277c71ec31a811ccc9b952ea4 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 29 May 2020 10:49:44 +0530 Subject: [PATCH 124/239] Add gitattributes. Like we have it in the database server, set per file type attributes in .gitattributes to fine-tune whitespace checks. However, this file has only the required attributes needed for mongo_fdw. Jeevan Chalke --- .gitattributes | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d17b72e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* 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 From 7bd1f34a5d1c1129849635a8dabd532282314759 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 29 May 2020 16:06:35 +0530 Subject: [PATCH 125/239] Re-establish mongo_fdw connections after server or user mapping changes. Earlier, mongo_fdw was using an ongoing connection even after the connection parameters were changed by ALTER SERVER or ALTER USER MAPPING commands. Fix it by watching for a catcache invals on these catalogs and re-establish the connection when these entries change, similar to that of postgres_fdw. FDW-108, Vaibhav Dalvi, reviewed by Suraj Kharage. --- connection.c | 92 ++++++++++++++++++++++++++++++++++++++++++ expected/mongo_fdw.out | 28 +++++++++++++ sql/mongo_fdw.sql | 27 +++++++++++++ 3 files changed, 147 insertions(+) diff --git a/connection.c b/connection.c index 66d806f..cdfd21c 100644 --- a/connection.c +++ b/connection.c @@ -28,8 +28,10 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "utils/hsearch.h" +#include "utils/inval.h" #include "utils/memutils.h" #include "utils/resowner.h" +#include "utils/syscache.h" /* Length of host */ #define HOST_LEN 256 @@ -51,6 +53,9 @@ 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; /* @@ -58,6 +63,8 @@ typedef struct ConnCacheEntry */ static HTAB *ConnectionHash = NULL; +static void mongo_inval_callback(Datum arg, int cacheid, uint32 hashvalue); + /* * mongo_get_connection: * Get a mong connection which can be used to execute queries on @@ -84,6 +91,15 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * 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 */ @@ -99,8 +115,22 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * /* 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) { +#if PG_VERSION_NUM < 90600 + Oid umoid; +#endif + #ifdef META_DRIVER entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, opt->authenticationDatabase, opt->replicaSet, opt->readPreference, @@ -110,6 +140,35 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * #endif 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)); +#if PG_VERSION_NUM >= 90600 + entry->mapping_hashvalue = + GetSysCacheHashValue1(USERMAPPINGOID, + ObjectIdGetDatum(user->umid)); +#else + /* Pre-9.6, UserMapping doesn't store its OID, so look it up again */ + umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(user->userid), + ObjectIdGetDatum(user->serverid)); + if (!OidIsValid(umoid)) + { + /* Not found for the specific user -- try PUBLIC */ + umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER, + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(user->serverid)); + } + entry->mapping_hashvalue = + GetSysCacheHashValue1(USERMAPPINGOID, ObjectIdGetDatum(umoid)); +#endif } return entry->conn; @@ -151,3 +210,36 @@ mongo_release_connection(MONGO_CONN *conn) * cleanup on the backend exit. */ } + +/* + * 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; + } +} diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 50c873f..ad839e6 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -771,6 +771,34 @@ execute test_where_pd(9); Nine (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.5'); +-- Should fail with an error +INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); +ERROR: failed to insert row +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +-- Set correct address for mongo_server +ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); +-- Should able to insert the data +INSERT INTO test_numbers VALUES ('12', 12, 'Twelve'); +-- Change the user mapping options +-- Set wrong username, password for postgres user +DROP USER MAPPING FOR postgres SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); +-- Should fail with an error +INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); +ERROR: failed to insert row +HINT: Mongo error: "Authentication failed." +-- Set default username, password for postgres user +DROP USER MAPPING FOR postgres SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server; +-- Should able to insert the data +INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); DELETE FROM test_numbers; DROP FOREIGN TABLE test_numbers; DROP FOREIGN TABLE test_json; diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index 1d86346..56eb166 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -131,6 +131,33 @@ execute test_where_pd(7); execute test_where_pd(8); execute test_where_pd(9); +-- +-- 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.5'); +-- Should fail with an error +INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); +-- Set correct address for mongo_server +ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); +-- Should able to insert the data +INSERT INTO test_numbers VALUES ('12', 12, 'Twelve'); + +-- Change the user mapping options +-- Set wrong username, password for postgres user +DROP USER MAPPING FOR postgres SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); +-- Should fail with an error +INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); +-- Set default username, password for postgres user +DROP USER MAPPING FOR postgres SERVER mongo_server; +CREATE USER MAPPING FOR postgres SERVER mongo_server; +-- Should able to insert the data +INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); + DELETE FROM test_numbers; DROP FOREIGN TABLE test_numbers; From d509e4a228842285661c45f372ddd3c50ae146de Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 17 Jun 2020 17:06:07 +0530 Subject: [PATCH 126/239] Update EDB copyrights for 2020. FDW-83, Vaibhav Dalvi, reviewed by Suraj Kharage. --- Makefile | 3 +-- Makefile.legacy | 3 +-- Makefile.meta | 3 +-- README.md | 3 +-- autogen.sh | 3 +-- connection.c | 4 +--- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 4 +--- mongo_fdw.control | 2 +- mongo_fdw.h | 4 +--- mongo_query.c | 4 +--- mongo_query.h | 4 +--- mongo_wrapper.c | 4 +--- mongo_wrapper.h | 4 +--- mongo_wrapper_meta.c | 4 +--- option.c | 4 +--- 17 files changed, 17 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 3e67c0d..0776b11 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright © 2004-2014, EnterpriseDB Corporation. -# +# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.legacy b/Makefile.legacy index 3e67c0d..0776b11 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -1,7 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright © 2004-2014, EnterpriseDB Corporation. -# +# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.meta b/Makefile.meta index 6c7448b..08513b4 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -1,7 +1,6 @@ # mongo_fdw/Makefile.meta # -# Portions Copyright © 2004-2014, EnterpriseDB Corporation. -# +# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index 2e583e5..82badd1 100644 --- a/README.md +++ b/README.md @@ -210,8 +210,7 @@ If you need commercial support, please contact the EnterpriseDB sales team, or c License ------- -Portions Copyright © 2004-2016, EnterpriseDB Corporation. - +Portions Copyright (c) 2004-2020, 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 diff --git a/autogen.sh b/autogen.sh index 2f61ce2..9f5ef50 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,8 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# -# Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index cdfd21c..1a9db70 100644 --- a/connection.c +++ b/connection.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index b495ced..1fb4df2 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright © 2004-2014, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index 38da196..59a1543 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright © 2004-2014, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw.c b/mongo_fdw.c index 35af687..9af3761 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index 57dfad9..5c7b90b 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright © 2004-2014, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index 623b0db..a18d5dc 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index 4efd61f..3158561 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index 9ba8508..245c451 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 5410f0c..2f27ac8 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 6d9d7ab..90206dc 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 001fca5..dd95aed 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index 0fa15f4..643754d 100644 --- a/option.c +++ b/option.c @@ -4,9 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * - * Portions Copyright (c) 2004-2014, EnterpriseDB Corporation. - * + * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From a2967a9aa877a4ec50a8a908c995082afd4b5486 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 17 Jun 2020 17:07:49 +0530 Subject: [PATCH 127/239] Run pgindent. FDW-83, Vaibhav Dalvi, reviewed by Suraj Kharage. --- connection.c | 25 +- mongo_fdw.c | 1273 ++++++++++++++++++++++-------------------- mongo_fdw.h | 278 ++++----- mongo_query.c | 616 ++++++++++---------- mongo_query.h | 4 +- mongo_wrapper.c | 238 ++++---- mongo_wrapper.h | 100 ++-- mongo_wrapper_meta.c | 328 +++++------ option.c | 89 +-- 9 files changed, 1525 insertions(+), 1426 deletions(-) diff --git a/connection.c b/connection.c index 1a9db70..d1fe74a 100644 --- a/connection.c +++ b/connection.c @@ -49,8 +49,8 @@ typedef struct ConnCacheKey typedef struct ConnCacheEntry { - ConnCacheKey key; /* hash key (must be first) */ - MONGO_CONN *conn; /* connection to foreign server, or NULL */ + 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 */ @@ -69,17 +69,18 @@ static void mongo_inval_callback(Datum arg, int cacheid, uint32 hashvalue); * 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_CONN * mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions *opt) { - bool found; - ConnCacheEntry *entry; - ConnCacheKey key; + bool found; + ConnCacheEntry *entry; + ConnCacheKey key; /* First time through, initialize connection cache hashtable */ if (ConnectionHash == NULL) { - HASHCTL ctl; + HASHCTL ctl; + MemSet(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(ConnCacheKey); ctl.entrysize = sizeof(ConnCacheEntry); @@ -87,8 +88,8 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * /* allocate ConnectionHash in the cache context */ ctl.hcxt = CacheMemoryContext; ConnectionHash = hash_create("mongo_fdw connections", 8, - &ctl, - HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); + &ctl, + HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT); /* * Register some callback functions that manage connection cleanup. @@ -131,8 +132,8 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * #ifdef META_DRIVER entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, - opt->authenticationDatabase, opt->replicaSet, opt->readPreference, - opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); + opt->authenticationDatabase, opt->replicaSet, opt->readPreference, + opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); #else entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); #endif @@ -179,7 +180,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * void mongo_cleanup_connection() { - HASH_SEQ_STATUS scan; + HASH_SEQ_STATUS scan; ConnCacheEntry *entry; if (ConnectionHash == NULL) diff --git a/mongo_fdw.c b/mongo_fdw.c index 9af3761..763d11b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -21,7 +21,7 @@ #include "access/reloptions.h" #if PG_VERSION_NUM >= 120000 - #include "access/table.h" +#include "access/table.h" #endif #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -37,7 +37,7 @@ #include "nodes/makefuncs.h" #include "optimizer/cost.h" #if PG_VERSION_NUM >= 120000 - #include "optimizer/optimizer.h" +#include "optimizer/optimizer.h" #endif #include "optimizer/pathnode.h" #include "optimizer/plancat.h" @@ -67,7 +67,7 @@ #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" #if PG_VERSION_NUM < 120000 - #include "optimizer/var.h" +#include "optimizer/var.h" #endif #include "parser/parsetree.h" #include "utils/builtins.h" @@ -81,7 +81,7 @@ #endif #include "utils/jsonb.h" #if PG_VERSION_NUM >= 90300 - #include "access/htup_details.h" +#include "access/htup_details.h" #endif /* @@ -93,84 +93,83 @@ /* Local functions forward declarations */ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); + Oid foreignTableId); -static ForeignScan * -MongoGetForeignPlan(PlannerInfo *root, - RelOptInfo *baserel, - Oid foreigntableid, - ForeignPath *best_path, - List *targetlist, - List *restrictionClauses, - Plan *outer_plan); +static ForeignScan *MongoGetForeignPlan(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *best_path, + List *targetlist, + List *restrictionClauses, + Plan *outer_plan); static void MongoExplainForeignScan(ForeignScanState *scanState, - ExplainState *explainState); + ExplainState *explainState); static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); -static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState); +static TupleTableSlot *MongoIterateForeignScan(ForeignScanState *scanState); static void MongoEndForeignScan(ForeignScanState *scanState); static void MongoReScanForeignScan(ForeignScanState *scanState); static TupleTableSlot *MongoExecForeignUpdate(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static TupleTableSlot *MongoExecForeignDelete(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static void MongoEndForeignModify(EState *estate, - ResultRelInfo *resultRelInfo); + ResultRelInfo *resultRelInfo); static void MongoAddForeignUpdateTargets(Query *parsetree, - RangeTblEntry *target_rte, - Relation target_relation); + RangeTblEntry *target_rte, + Relation target_relation); static void MongoBeginForeignModify(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - List *fdw_private, - int subplan_index, - int eflags); + ResultRelInfo *resultRelInfo, + List *fdw_private, + int subplan_index, + int eflags); static TupleTableSlot *MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot); + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot); static List *MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index); + ModifyTable *plan, + Index resultRelation, + int subplan_index); static void -MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, List *fdw_private, - int subplan_index, ExplainState *es); + MongoExplainForeignModify(ModifyTableState *mtstate, + ResultRelInfo *rinfo, List *fdw_private, + int subplan_index, ExplainState *es); /* local functions */ static double ForeignTableDocumentCount(Oid foreignTableId); -static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList); +static HTAB *ColumnMappingHash(Oid foreignTableId, List *columnList); static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls); static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod); static void MongoFreeScanState(MongoFdwModifyState *fmstate); static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount); -static int MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount); + AcquireSampleRowsFunc *acquireSampleRowsFunc, + BlockNumber *totalPageCount); +static int MongoAcquireSampleRows(Relation relation, int errorLevel, + HeapTuple *sampleRows, int targetRowCount, + double *totalRowCount, double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); extern PGDLLEXPORT void _PG_init(void); -const char * EscapeJsonString(const char *string); +const char *EscapeJsonString(const char *string); void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); @@ -213,6 +212,7 @@ Datum mongo_fdw_handler(PG_FUNCTION_ARGS) { FdwRoutine *fdwRoutine = makeNode(FdwRoutine); + fdwRoutine->GetForeignRelSize = MongoGetForeignRelSize; fdwRoutine->GetForeignPaths = MongoGetForeignPaths; fdwRoutine->GetForeignPlan = MongoGetForeignPlan; @@ -257,18 +257,20 @@ static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) { double documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount > 0.0) + + if (documentCount >0.0) { /* - * We estimate the number of rows returned after restriction qualifiers - * are applied. This will be more accurate if analyze is run on this - * relation. + * We estimate the number of rows returned after restriction + * qualifiers are applied. This will be more accurate if analyze is + * run on this relation. */ - List *rowClauseList = baserel->baserestrictinfo; - double rowSelectivity = clauselist_selectivity(root, rowClauseList, - 0, JOIN_INNER, NULL); + List *rowClauseList = baserel->baserestrictinfo; + double rowSelectivity = clauselist_selectivity(root, rowClauseList, + 0, JOIN_INNER, NULL); + + double outputRowCount = clamp_row_est(documentCount *rowSelectivity); - double outputRowCount = clamp_row_est(documentCount * rowSelectivity); baserel->rows = outputRowCount; } else @@ -288,54 +290,55 @@ MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableI static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) { - double tupleFilterCost = baserel->baserestrictcost.per_tuple; - double inputRowCount = 0.0; - double documentSelectivity = 0.0; - double foreignTableSize = 0; - int32 documentWidth = 0; - BlockNumber pageCount = 0; - double totalDiskAccessCost = 0.0; - double cpuCostPerDoc = 0.0; - double cpuCostPerRow = 0.0; - double totalCpuCost = 0.0; - double connectionCost = 0.0; - double documentCount = 0.0; - List *opExpressionList = NIL; - Cost startupCost = 0.0; - Cost totalCost = 0.0; - Path *foreignPath = NULL; + double tupleFilterCost = baserel->baserestrictcost.per_tuple; + double inputRowCount = 0.0; + double documentSelectivity = 0.0; + double foreignTableSize = 0; + int32 documentWidth = 0; + BlockNumber pageCount = 0; + double totalDiskAccessCost = 0.0; + double cpuCostPerDoc = 0.0; + double cpuCostPerRow = 0.0; + double totalCpuCost = 0.0; + double connectionCost = 0.0; + double documentCount = 0.0; + List *opExpressionList = NIL; + Cost startupCost = 0.0; + Cost totalCost = 0.0; + Path *foreignPath = NULL; documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount > 0.0) + + if (documentCount >0.0) { /* - * We estimate the number of rows returned after restriction qualifiers - * are applied by MongoDB. + * We estimate the number of rows returned after restriction + * qualifiers are applied by MongoDB. */ opExpressionList = ApplicableOpExpressionList(baserel); documentSelectivity = clauselist_selectivity(root, opExpressionList, 0, JOIN_INNER, NULL); - inputRowCount = clamp_row_est(documentCount * documentSelectivity); + 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. + * 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; + 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. + * 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); + totalCpuCost = (cpuCostPerDoc * documentCount) +(cpuCostPerRow * inputRowCount); connectionCost = MONGO_CONNECTION_COST_MULTIPLIER * seq_page_cost; startupCost = baserel->baserestrictcost.startup + connectionCost; @@ -351,17 +354,17 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) foreignPath = (Path *) create_foreignscan_path(root, baserel, #if PG_VERSION_NUM >= 90600 - NULL, /* default pathtarget */ + NULL, /* default pathtarget */ #endif - baserel->rows, - startupCost, - totalCost, - NIL, /* no pathkeys */ - NULL, /* no outer rel either */ + baserel->rows, + startupCost, + totalCost, + NIL, /* no pathkeys */ + NULL, /* no outer rel either */ #if PG_VERSION_NUM >= 90500 - NULL, /* no extra plan */ + NULL, /* no extra plan */ #endif - NULL); /* no fdw_private data */ + NULL); /* no fdw_private data */ /* add foreign path as the only possible path */ add_path(baserel, foreignPath); @@ -375,35 +378,36 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) */ static ForeignScan * MongoGetForeignPlan(PlannerInfo *root, - RelOptInfo *baserel, - Oid foreigntableid, - ForeignPath *best_path, - List *targetList, - List *restrictionClauses, - Plan *outer_plan) + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *best_path, + List *targetList, + List *restrictionClauses, + Plan *outer_plan) { - Index scanRangeTableIndex = baserel->relid; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - List *opExpressionList = NIL; - BSON *queryDocument = NULL; - List *columnList = NIL; + Index scanRangeTableIndex = baserel->relid; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + List *opExpressionList = NIL; + BSON *queryDocument = NULL; + List *columnList = NIL; /* - * We push down applicable restriction clauses to MongoDB, but for simplicity - * we currently put all the restrictionClauses into the plan node's qual - * list for the executor to re-check. So all we have to do here is strip - * RestrictInfo nodes from the clauses and ignore pseudoconstants (which - * will be handled elsewhere). + * We push down applicable restriction clauses to MongoDB, but for + * simplicity we currently put all the restrictionClauses into the plan + * node's qual list for the executor to re-check. So all we have to do + * here is strip RestrictInfo nodes from the clauses and ignore + * pseudoconstants (which will be handled elsewhere). */ restrictionClauses = extract_actual_clauses(restrictionClauses, false); /* - * 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. + * 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. */ opExpressionList = ApplicableOpExpressionList(baserel); queryDocument = QueryDocument(foreigntableid, opExpressionList, NULL); @@ -418,16 +422,16 @@ MongoGetForeignPlan(PlannerInfo *root, BsonDestroy(queryDocument); /* create the foreign scan node */ - foreignScan = make_foreignscan(targetList, restrictionClauses, - scanRangeTableIndex, - NIL, /* no expressions to evaluate */ - foreignPrivateList + foreignScan = make_foreignscan(targetList, restrictionClauses, + scanRangeTableIndex, + NIL, /* no expressions to evaluate */ + foreignPrivateList #if PG_VERSION_NUM >= 90500 - ,NIL - ,NIL - ,NULL + ,NIL + ,NIL + ,NULL #endif - ); + ); return foreignScan; } @@ -439,9 +443,9 @@ MongoGetForeignPlan(PlannerInfo *root, static void MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) { - MongoFdwOptions *options = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *options = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); options = mongo_get_options(foreignTableId); @@ -458,14 +462,14 @@ MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) static void MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, - List *fdw_private, - int subplan_index, - ExplainState *es) + ResultRelInfo *rinfo, + List *fdw_private, + int subplan_index, + ExplainState *es) { - MongoFdwOptions *options = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *options = NULL; + StringInfo namespaceName = NULL; + Oid foreignTableId = InvalidOid; foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); options = mongo_get_options(foreignTableId); @@ -489,24 +493,24 @@ MongoExplainForeignModify(ModifyTableState *mtstate, static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) { - MONGO_CONN *mongoConnection = NULL; - MONGO_CURSOR *mongoCursor = NULL; - Oid foreignTableId = InvalidOid; - List *columnList = NIL; - HTAB *columnMappingHash = NULL; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - BSON *queryDocument = NULL; - MongoFdwOptions *options = NULL; - MongoFdwModifyState *fmstate = NULL; - List *opExpressionList = NIL; - RangeTblEntry *rte; - EState *estate = scanState->ss.ps.state; - ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; - Oid userid; - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MONGO_CONN *mongoConnection = NULL; + MONGO_CURSOR *mongoCursor = NULL; + Oid foreignTableId = InvalidOid; + List *columnList = NIL; + HTAB *columnMappingHash = NULL; + ForeignScan *foreignScan = NULL; + List *foreignPrivateList = NIL; + BSON *queryDocument = NULL; + MongoFdwOptions *options = NULL; + MongoFdwModifyState *fmstate = NULL; + List *opExpressionList = NIL; + RangeTblEntry *rte; + EState *estate = scanState->ss.ps.state; + ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* if Explain with no Analyze, do nothing */ @@ -517,6 +521,7 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) options = mongo_get_options(foreignTableId); fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); + /* * Identify which user to do the remote access as. This should match what * ExecCheckRTEPerms() does. @@ -531,8 +536,8 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -570,13 +575,13 @@ static TupleTableSlot * MongoIterateForeignScan(ForeignScanState *scanState) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - TupleTableSlot *tupleSlot = scanState->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; + TupleTableSlot *tupleSlot = scanState->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; /* * We execute the protocol to load a virtual tuple into a slot. We first @@ -634,10 +639,10 @@ MongoEndForeignScan(ForeignScanState *scanState) static void MongoReScanForeignScan(ForeignScanState *scanState) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *options = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + MONGO_CONN *mongoConnection = fmstate->mongoConnection; + MongoFdwOptions *options = NULL; + Oid foreignTableId = InvalidOid; /* close down the old cursor */ MongoCursorDestroy(fmstate->mongoCursor); @@ -648,22 +653,22 @@ MongoReScanForeignScan(ForeignScanState *scanState) /* reconstruct cursor for collection name and set query */ fmstate->mongoCursor = MongoCursorCreate(mongoConnection, - fmstate->options->svr_database, - fmstate->options->collectionName, - fmstate->queryDocument); + fmstate->options->svr_database, + fmstate->options->collectionName, + fmstate->queryDocument); mongo_free_options(options); } static List * MongoPlanForeignModify(PlannerInfo *root, - ModifyTable *plan, - Index resultRelation, - int subplan_index) + ModifyTable *plan, + Index resultRelation, + int subplan_index) { - CmdType operation = plan->operation; - RangeTblEntry *rte = planner_rt_fetch(resultRelation, root); - Relation rel; - List *targetAttrs = NIL; + 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 @@ -676,8 +681,8 @@ MongoPlanForeignModify(PlannerInfo *root, #endif if (operation == CMD_INSERT) { - TupleDesc tupdesc = RelationGetDescr(rel); - int attnum; + TupleDesc tupdesc = RelationGetDescr(rel); + int attnum; for (attnum = 1; attnum <= tupdesc->natts; attnum++) { @@ -694,22 +699,23 @@ MongoPlanForeignModify(PlannerInfo *root, else if (operation == CMD_UPDATE) { #if PG_VERSION_NUM >= 90500 - Bitmapset *tmpset = bms_copy(rte->updatedCols); + Bitmapset *tmpset = bms_copy(rte->updatedCols); #else - Bitmapset *tmpset = bms_copy(rte->modifiedCols); + Bitmapset *tmpset = bms_copy(rte->modifiedCols); #endif AttrNumber col; while ((col = bms_first_member(tmpset)) >= 0) { col += FirstLowInvalidHeapAttributeNumber; - if (col <= InvalidAttrNumber) /* shouldn't happen */ + 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) + * We also disallow updates to the first column which happens to + * be the row identifier in MongoDb (_id) */ - if (col == 1) /* shouldn't happen */ + if (col == 1) /* shouldn't happen */ elog(ERROR, "row identifier column update is not supported"); targetAttrs = lappend_int(targetAttrs, col); @@ -721,6 +727,7 @@ MongoPlanForeignModify(PlannerInfo *root, { targetAttrs = lcons_int(1, targetAttrs); } + /* * RETURNING list not supported */ @@ -742,18 +749,18 @@ MongoPlanForeignModify(PlannerInfo *root, */ static void MongoBeginForeignModify(ModifyTableState *mtstate, - ResultRelInfo *resultRelInfo, - List *fdw_private, - int subplan_index, - int eflags) + ResultRelInfo *resultRelInfo, + List *fdw_private, + int subplan_index, + int eflags) { - MongoFdwModifyState *fmstate = NULL; - Relation rel = resultRelInfo->ri_RelationDesc; - AttrNumber n_params = 0; - Oid typefnoid = InvalidOid; - bool isvarlena = false; - ListCell *lc = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwModifyState *fmstate = NULL; + Relation rel = resultRelInfo->ri_RelationDesc; + AttrNumber n_params = 0; + Oid typefnoid = InvalidOid; + bool isvarlena = false; + ListCell *lc = NULL; + Oid foreignTableId = InvalidOid; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -779,7 +786,7 @@ MongoBeginForeignModify(ModifyTableState *mtstate, /* Set up for remaining transmittable parameters */ foreach(lc, fmstate->target_attrs) { - int attnum = lfirst_int(lc); + int attnum = lfirst_int(lc); #if PG_VERSION_NUM < 110000 Form_pg_attribute attr = RelationGetDescr(rel)->attrs[attnum - 1]; #else @@ -803,21 +810,21 @@ MongoBeginForeignModify(ModifyTableState *mtstate, */ static TupleTableSlot * MongoExecForeignInsert(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Oid foreignTableId = InvalidOid; - BSON *b = NULL; - Oid typoid; - Datum value; - bool isnull = false; - Oid userid; - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Oid foreignTableId = InvalidOid; + BSON *b = NULL; + Oid typoid; + Datum value; + bool isnull = false; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -831,8 +838,8 @@ MongoExecForeignInsert(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. */ options = fmstate->options; mongoConnection = mongo_get_connection(server, user, options); @@ -844,11 +851,11 @@ MongoExecForeignInsert(EState *estate, /* get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) { - ListCell *lc; + ListCell *lc; foreach(lc, fmstate->target_attrs) { - int attnum = lfirst_int(lc); + int attnum = lfirst_int(lc); value = slot_getattr(slot, attnum, &isnull); @@ -872,18 +879,19 @@ MongoExecForeignInsert(EState *estate, if (attnum == 1) { /* - * Ignore the value of first column which is row identifier in MongoDb (_id) - * and let MongoDB to insert the unique value for that column. + * Ignore the value of first column which is row identifier in + * MongoDb (_id) and let MongoDB to insert the unique value + * for that column. */ } else { #if PG_VERSION_NUM < 110000 AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull, slot->tts_tupleDescriptor->attrs[attnum -1]->atttypid); + isnull, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); #else - AppenMongoValue(b, TupleDescAttr(slot->tts_tupleDescriptor, attnum-1)->attname.data, value, - isnull, TupleDescAttr(slot->tts_tupleDescriptor, attnum-1)->atttypid); + AppenMongoValue(b, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, value, + isnull, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); #endif } } @@ -906,12 +914,12 @@ MongoExecForeignInsert(EState *estate, */ static void MongoAddForeignUpdateTargets(Query *parsetree, - RangeTblEntry *target_rte, - Relation target_relation) + RangeTblEntry *target_rte, + Relation target_relation) { - Var *var = NULL; - const char *attrname = NULL; - TargetEntry *tle = NULL; + Var *var = NULL; + const char *attrname = NULL; + TargetEntry *tle = NULL; /* * What we need is the rowid which is the first column @@ -924,19 +932,19 @@ MongoAddForeignUpdateTargets(Query *parsetree, /* Make a Var representing the desired value */ var = makeVar(parsetree->resultRelation, - 1, - attr->atttypid, - attr->atttypmod, - InvalidOid, - 0); + 1, + attr->atttypid, + attr->atttypmod, + InvalidOid, + 0); /* Wrap it in a TLE with the right name ... */ attrname = NameStr(attr->attname); tle = makeTargetEntry((Expr *) var, - list_length(parsetree->targetList) + 1, - pstrdup(attrname), - true); + list_length(parsetree->targetList) + 1, + pstrdup(attrname), + true); /* ... and add it to the query's targetlist */ parsetree->targetList = lappend(parsetree->targetList, tle); @@ -945,24 +953,24 @@ MongoAddForeignUpdateTargets(Query *parsetree, static TupleTableSlot * MongoExecForeignUpdate(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; - BSON *op = NULL; - BSON set; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; + BSON *op = NULL; + BSON set; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -978,8 +986,8 @@ MongoExecForeignUpdate(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1000,18 +1008,18 @@ MongoExecForeignUpdate(EState *estate, /* get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) { - ListCell *lc; + ListCell *lc; foreach(lc, fmstate->target_attrs) { - int attnum = lfirst_int(lc); + int attnum = lfirst_int(lc); #if PG_VERSION_NUM < 110000 Form_pg_attribute attr = slot->tts_tupleDescriptor->attrs[attnum - 1]; #else Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1); #endif - Datum value; - bool isnull; + Datum value; + bool isnull; if (strcmp("_id", attr->attname.data) == 0) continue; @@ -1056,22 +1064,22 @@ MongoExecForeignUpdate(EState *estate, */ static TupleTableSlot * MongoExecForeignDelete(EState *estate, - ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, - TupleTableSlot *planSlot) + ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, + TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; - bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + Datum datum = 0; + bool isNull = false; + Oid foreignTableId = InvalidOid; + char *columnName = NULL; + Oid typoid = InvalidOid; + BSON *b = NULL; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; @@ -1087,8 +1095,8 @@ MongoExecForeignDelete(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1104,7 +1112,7 @@ MongoExecForeignDelete(EState *estate, typoid = get_atttype(foreignTableId, 1); b = BsonCreate(); - if (!AppenMongoValue(b,columnName, datum, false, typoid)) + if (!AppenMongoValue(b, columnName, datum, false, typoid)) { BsonDestroy(b); return NULL; @@ -1128,6 +1136,7 @@ static void MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + if (fmstate) { if (fmstate->options) @@ -1148,14 +1157,14 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) static double ForeignTableDocumentCount(Oid foreignTableId) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - const BSON *emptyQuery = NULL; - double documentCount = 0.0; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; + MongoFdwOptions *options = NULL; + MONGO_CONN *mongoConnection = NULL; + const BSON *emptyQuery = NULL; + double documentCount = 0.0; + Oid userid = GetUserId(); + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* Get info about foreign table. */ @@ -1167,8 +1176,8 @@ ForeignTableDocumentCount(Oid foreignTableId) options = mongo_get_options(foreignTableId); /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1189,12 +1198,13 @@ ForeignTableDocumentCount(Oid foreignTableId) static HTAB * ColumnMappingHash(Oid foreignTableId, List *columnList) { - ListCell *columnCell = NULL; - const long hashTableSize = 2048; - HTAB *columnMappingHash = NULL; + ListCell *columnCell = NULL; + const long hashTableSize = 2048; + HTAB *columnMappingHash = NULL; /* create hash table */ - HASHCTL hashInfo; + HASHCTL hashInfo; + memset(&hashInfo, 0, sizeof(hashInfo)); hashInfo.keysize = NAMEDATALEN; hashInfo.entrysize = sizeof(ColumnMapping); @@ -1207,13 +1217,13 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) foreach(columnCell, columnList) { - Var *column = (Var *) lfirst(columnCell); - AttrNumber columnId = column->varattno; + Var *column = (Var *) lfirst(columnCell); + AttrNumber columnId = column->varattno; ColumnMapping *columnMapping = NULL; - char *columnName = NULL; - bool handleFound = false; - void *hashKey = NULL; + char *columnName = NULL; + bool handleFound = false; + void *hashKey = NULL; #if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, columnId); @@ -1248,24 +1258,24 @@ static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) { - ColumnMapping* columnMapping = NULL; - bool handleFound = false; - void* hashKey = NULL; - BSON_ITERATOR bsonIterator = { NULL, 0 }; + ColumnMapping *columnMapping = NULL; + bool handleFound = false; + void *hashKey = NULL; + BSON_ITERATOR bsonIterator = {NULL, 0}; - if (BsonIterInit(&bsonIterator, (BSON*)bsonDocument) == false) + if (BsonIterInit(&bsonIterator, (BSON *) bsonDocument) == false) elog(ERROR, "failed to initialize BSON iterator"); hashKey = "__doc"; columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, - HASH_FIND, &handleFound); + HASH_FIND, &handleFound); if (columnMapping != NULL && handleFound == true && columnValues[columnMapping->columnIndex] == 0) { - JsonLexContext* lex = NULL; - text* result = NULL; - Datum columnValue = 0; - char *str; + JsonLexContext *lex = NULL; + text *result = NULL; + Datum columnValue = 0; + char *str; str = BsonAsJson(bsonDocument); result = cstring_to_text_with_len(str, strlen(str)); @@ -1298,11 +1308,11 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, case TIMESTAMPOID: case TIMESTAMPTZOID: case BPCHAROID: - columnValue = PointerGetDatum(result); + columnValue = PointerGetDatum(result); break; case JSONBOID: - columnValue = DirectFunctionCall1(jsonb_in, PointerGetDatum(str)); + columnValue = DirectFunctionCall1(jsonb_in, PointerGetDatum(str)); break; default: @@ -1322,13 +1332,13 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, while (BsonIterNext(&bsonIterator)) { const char *bsonKey = BsonIterKey(&bsonIterator); - BSON_TYPE bsonType = BsonIterType(&bsonIterator); - Oid columnTypeId = InvalidOid; - Oid columnArrayTypeId = InvalidOid; - bool compatibleTypes = false; - bool handleFound = false; + BSON_TYPE bsonType = BsonIterType(&bsonIterator); + Oid columnTypeId = InvalidOid; + Oid columnArrayTypeId = InvalidOid; + bool compatibleTypes = false; + bool handleFound = false; const char *bsonFullKey = NULL; - void *hashKey = NULL; + void *hashKey = NULL; columnMapping = NULL; if (bsonDocumentKey != NULL) @@ -1337,7 +1347,8 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, * For fields in nested BSON objects, we use fully qualified field * name to check the column mapping. */ - StringInfo bsonFullKeyString = makeStringInfo(); + StringInfo bsonFullKeyString = makeStringInfo(); + appendStringInfo(bsonFullKeyString, "%s.%s", bsonDocumentKey, bsonKey); bsonFullKey = bsonFullKeyString->data; } @@ -1361,10 +1372,11 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, { if (columnTypeId != JSONOID) { - BSON subObject; + BSON subObject; + BsonIterSubObject(&bsonIterator, &subObject); FillTupleSlot(&subObject, bsonFullKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls); continue; } } @@ -1396,7 +1408,7 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, /* fill in corresponding column value and null flag */ if (OidIsValid(columnArrayTypeId)) { - int32 columnIndex = columnMapping->columnIndex; + int32 columnIndex = columnMapping->columnIndex; columnValues[columnIndex] = ColumnValueArray(&bsonIterator, columnArrayTypeId); @@ -1404,8 +1416,8 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, } else { - int32 columnIndex = columnMapping->columnIndex; - Oid columnTypeMod = columnMapping->columnTypeMod; + int32 columnIndex = columnMapping->columnIndex; + Oid columnTypeMod = columnMapping->columnTypeMod; columnValues[columnIndex] = ColumnValue(&bsonIterator, columnTypeId, columnTypeMod); @@ -1423,104 +1435,109 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) { - bool compatibleTypes = false; + bool compatibleTypes = false; /* we consider the PostgreSQL column type as authoritative */ - switch(columnTypeId) + 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) + case INT2OID: + case INT4OID: + case INT8OID: + case FLOAT4OID: + case FLOAT8OID: + case NUMERICOID: { - compatibleTypes = true; + if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || + bsonType == BSON_TYPE_DOUBLE) + { + compatibleTypes = true; + } + break; } - break; - } case BOOLOID: - { - if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || - bsonType == BSON_TYPE_DOUBLE || bsonType == BSON_TYPE_BOOL) { - compatibleTypes = true; + if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || + bsonType == BSON_TYPE_DOUBLE || bsonType == BSON_TYPE_BOOL) + { + compatibleTypes = true; + } + break; } - break; - } case BPCHAROID: case VARCHAROID: case TEXTOID: - { - if (bsonType == BSON_TYPE_UTF8) { - compatibleTypes = true; + if (bsonType == BSON_TYPE_UTF8) + { + compatibleTypes = true; + } + break; } - break; - } case BYTEAOID: - { - if (bsonType == BSON_TYPE_BINDATA) { - compatibleTypes = true; - } + if (bsonType == BSON_TYPE_BINDATA) + { + compatibleTypes = true; + } #ifdef META_DRIVER - if (bsonType == BSON_TYPE_OID) - { - compatibleTypes = true; - } + if (bsonType == BSON_TYPE_OID) + { + compatibleTypes = true; + } #endif - break; - } + break; + } case NAMEOID: - { - /* - * 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. - */ - if (bsonType == BSON_TYPE_OID) { - compatibleTypes = true; + /* + * 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. + */ + if (bsonType == BSON_TYPE_OID) + { + compatibleTypes = true; + } + break; } - break; - } case DATEOID: case TIMESTAMPOID: case TIMESTAMPTZOID: - { - if (bsonType == BSON_TYPE_DATE_TIME) { - compatibleTypes = true; + if (bsonType == BSON_TYPE_DATE_TIME) + { + compatibleTypes = true; + } + break; } - break; - } case NUMERICARRAY_OID: - { - if (bsonType == BSON_TYPE_ARRAY) - compatibleTypes = true; - break; - } + { + if (bsonType == BSON_TYPE_ARRAY) + compatibleTypes = true; + break; + } case JSONOID: - { - if (bsonType == BSON_TYPE_DOCUMENT || bsonType == BSON_TYPE_ARRAY) { - compatibleTypes = true; + if (bsonType == BSON_TYPE_DOCUMENT || bsonType == BSON_TYPE_ARRAY) + { + compatibleTypes = true; + } + break; } - 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; - } + { + /* + * 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; @@ -1536,23 +1553,24 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { - Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); - uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; - uint32 arrayGrowthFactor = 2; - uint32 arrayIndex = 0; - - ArrayType *columnValueObject = NULL; - Datum columnValueDatum = 0; - bool typeByValue = false; - char typeAlignment = 0; - int16 typeLength = 0; - - BSON_ITERATOR bsonSubIterator = { NULL, 0 }; + Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); + uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; + uint32 arrayGrowthFactor = 2; + uint32 arrayIndex = 0; + + ArrayType *columnValueObject = NULL; + Datum columnValueDatum = 0; + bool typeByValue = false; + char typeAlignment = 0; + int16 typeLength = 0; + + BSON_ITERATOR bsonSubIterator = {NULL, 0}; + BsonIterSubIter(bsonIterator, &bsonSubIterator); while (BsonIterNext(&bsonSubIterator)) { - BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); - bool compatibleTypes = false; + BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); + bool compatibleTypes = false; compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); if (bsonType == BSON_TYPE_NULL || !compatibleTypes) @@ -1588,170 +1606,179 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { - Datum columnValue = 0; + Datum columnValue = 0; - switch(columnTypeId) + switch (columnTypeId) { case INT2OID: - { - int16 value = (int16) BsonIterInt32(bsonIterator); - columnValue = Int16GetDatum(value); - break; - } + { + int16 value = (int16) BsonIterInt32(bsonIterator); + + columnValue = Int16GetDatum(value); + break; + } case INT4OID: - { - int32 value = BsonIterInt32(bsonIterator); - columnValue = Int32GetDatum(value); - break; - } + { + int32 value = BsonIterInt32(bsonIterator); + + columnValue = Int32GetDatum(value); + break; + } case INT8OID: - { - int64 value = BsonIterInt64(bsonIterator); - columnValue = Int64GetDatum(value); - break; - } + { + int64 value = BsonIterInt64(bsonIterator); + + columnValue = Int64GetDatum(value); + break; + } case FLOAT4OID: - { - float4 value = (float4) BsonIterDouble(bsonIterator); - columnValue = Float4GetDatum(value); - break; - } + { + float4 value = (float4) BsonIterDouble(bsonIterator); + + columnValue = Float4GetDatum(value); + break; + } case FLOAT8OID: - { - float8 value = BsonIterDouble(bsonIterator); - columnValue = Float8GetDatum(value); - break; - } + { + float8 value = BsonIterDouble(bsonIterator); + + columnValue = Float8GetDatum(value); + break; + } case NUMERICOID: - { - float8 value = BsonIterDouble(bsonIterator); - Datum valueDatum = Float8GetDatum(value); + { + float8 value = BsonIterDouble(bsonIterator); + Datum valueDatum = Float8GetDatum(value); - /* overlook type modifiers for numeric */ - columnValue = DirectFunctionCall1(float8_numeric, valueDatum); - break; - } + /* overlook type modifiers for numeric */ + columnValue = DirectFunctionCall1(float8_numeric, valueDatum); + break; + } case BOOLOID: - { - bool value = BsonIterBool(bsonIterator); - columnValue = BoolGetDatum(value); - break; - } + { + bool value = BsonIterBool(bsonIterator); + + columnValue = BoolGetDatum(value); + break; + } case BPCHAROID: - { - const char *value = BsonIterString(bsonIterator); - Datum valueDatum = CStringGetDatum(value); + { + const char *value = BsonIterString(bsonIterator); + Datum valueDatum = CStringGetDatum(value); - columnValue = DirectFunctionCall3(bpcharin, valueDatum, - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(columnTypeMod)); - break; - } + columnValue = DirectFunctionCall3(bpcharin, valueDatum, + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(columnTypeMod)); + break; + } case VARCHAROID: - { - const char *value = BsonIterString(bsonIterator); - Datum valueDatum = CStringGetDatum(value); + { + const char *value = BsonIterString(bsonIterator); + Datum valueDatum = CStringGetDatum(value); - columnValue = DirectFunctionCall3(varcharin, valueDatum, - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(columnTypeMod)); - break; - } + columnValue = DirectFunctionCall3(varcharin, valueDatum, + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(columnTypeMod)); + break; + } case TEXTOID: - { - const char *value = BsonIterString(bsonIterator); - columnValue = CStringGetTextDatum(value); - break; - } + { + const char *value = BsonIterString(bsonIterator); + + columnValue = CStringGetTextDatum(value); + break; + } case NAMEOID: - { - char value[NAMEDATALEN]; - Datum valueDatum = 0; + { + char value[NAMEDATALEN]; + Datum valueDatum = 0; - bson_oid_t *bsonObjectId = (bson_oid_t*) BsonIterOid(bsonIterator); - bson_oid_to_string(bsonObjectId, value); + bson_oid_t *bsonObjectId = (bson_oid_t *) BsonIterOid(bsonIterator); - valueDatum = CStringGetDatum(value); - columnValue = DirectFunctionCall3(namein, valueDatum, - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(columnTypeMod)); - break; - } + 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; -#ifdef META_DRIVER - 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; - } + int value_len; + char *value; + bytea *result; +#ifdef META_DRIVER + 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; + } #else - value_len = BsonIterBinLen(bsonIterator); - value = (char*)BsonIterBinData(bsonIterator); + value_len = BsonIterBinLen(bsonIterator); + value = (char *) BsonIterBinData(bsonIterator); #endif - result = (bytea *)palloc(value_len + VARHDRSZ); - memcpy(VARDATA(result), value, value_len); - SET_VARSIZE(result, value_len + VARHDRSZ); - columnValue = PointerGetDatum(result); - 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); + { + int64 valueMillis = BsonIterDate(bsonIterator); + int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; + Datum timestampDatum = TimestampGetDatum(timestamp); - columnValue = DirectFunctionCall1(timestamp_date, timestampDatum); - break; - } + columnValue = DirectFunctionCall1(timestamp_date, timestampDatum); + break; + } case TIMESTAMPOID: case TIMESTAMPTZOID: - { - int64 valueMillis = BsonIterDate(bsonIterator); - int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; + { + int64 valueMillis = BsonIterDate(bsonIterator); + int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; - /* overlook type modifiers for timestamp */ - columnValue = TimestampGetDatum(timestamp); - break; - } + /* overlook type modifiers for timestamp */ + columnValue = TimestampGetDatum(timestamp); + break; + } case JSONOID: - { - JsonLexContext *lex; - text *result; - StringInfo buffer = makeStringInfo(); + { + JsonLexContext *lex; + text *result; + StringInfo buffer = makeStringInfo(); + + BSON_TYPE type = BSON_ITER_TYPE(bsonIterator); - BSON_TYPE type = BSON_ITER_TYPE(bsonIterator); - if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT) - ereport(ERROR, (errmsg("cannot convert scolar to json"))); + if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT) + ereport(ERROR, (errmsg("cannot convert scolar to json"))); #ifdef META_DRIVER - /* Convert BSON to JSON value */ - BsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); + /* Convert BSON to JSON value */ + BsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); #else - /* Convert BSON to JSON value */ - BsonToJsonString(buffer, *bsonIterator, BSON_TYPE_ARRAY == type); + /* Convert BSON to JSON value */ + BsonToJsonString(buffer, *bsonIterator, BSON_TYPE_ARRAY == type); #endif - result = cstring_to_text_with_len(buffer->data, buffer->len); - lex = makeJsonLexContext(result, false); - pg_parse_json(lex, &nullSemAction); - columnValue = PointerGetDatum(result); - break; - } + result = cstring_to_text_with_len(buffer->data, buffer->len); + lex = makeJsonLexContext(result, false); + 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; - } + { + 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; @@ -1760,11 +1787,11 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) void BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) { - const char *key = NULL; - bool isFirstElement; - char beginSymbol = '{'; - char endSymbol = '}'; - BSON_TYPE t; + const char *key = NULL; + bool isFirstElement; + char beginSymbol = '{'; + char endSymbol = '}'; + BSON_TYPE t; if (isArray) { @@ -1775,6 +1802,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) #ifndef META_DRIVER { const char *bsonData = bson_iterator_value(&i); + bson_iterator_from_buffer(&i, bsonData); } #endif @@ -1803,7 +1831,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) break; case BSON_TYPE_UTF8: appendStringInfo(output, "\"%s\"", - EscapeJsonString(BsonIterString(&i))); + EscapeJsonString(BsonIterString(&i))); break; case BSON_TYPE_SYMBOL: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), @@ -1812,19 +1840,20 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) errhint("Symbol: %s", BsonIterString(&i)))); break; case BSON_TYPE_OID: - { - char oidhex[25]; - BsonOidToString(BsonIterOid(&i), oidhex); - appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); - break; - } + { + char oidhex[25]; + + BsonOidToString(BsonIterOid(&i), oidhex); + appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); + break; + } case BSON_TYPE_BOOL: appendStringInfoString( - output, BsonIterBool(&i) ? "true" : "false"); + output, BsonIterBool(&i) ? "true" : "false"); break; case BSON_TYPE_DATE_TIME: appendStringInfo(output, "{\"$date\":%ld}", - (long int)BsonIterDate(&i)); + (long int) BsonIterDate(&i)); break; case BSON_TYPE_BINDATA: /* It's possible to encode the data with base64 here. */ @@ -1861,7 +1890,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) appendStringInfo(output, "%d", BsonIterInt32(&i)); break; case BSON_TYPE_INT64: - appendStringInfo(output, "%lu", (uint64_t)BsonIterInt64(&i)); + appendStringInfo(output, "%lu", (uint64_t) BsonIterInt64(&i)); break; case BSON_TYPE_TIMESTAMP: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), @@ -1889,44 +1918,59 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) const char * EscapeJsonString(const char *string) { - StringInfo buffer; - const char *ptr; - int i, segmentStartIdx, len; - - bool needsEscaping = false; - for (ptr = string; *ptr; ++ptr) { - if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t'\ - || *ptr == '\\') { + StringInfo buffer; + const char *ptr; + int i, + segmentStartIdx, + len; + + bool needsEscaping = false; + + for (ptr = string; *ptr; ++ptr) + { + if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t' \ + ||*ptr == '\\') + { needsEscaping = true; } } - if (!needsEscaping) return string; + if (!needsEscaping) + return string; buffer = makeStringInfo(); len = strlen(string); segmentStartIdx = 0; - for (i = 0; i < len; ++i) { + for (i = 0; i < len; ++i) + { if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' - || string[i] == '\t' || string[i] == '\\') { - if (segmentStartIdx < i) { + || string[i] == '\t' || string[i] == '\\') + { + if (segmentStartIdx < i) + { appendBinaryStringInfo(buffer, string + segmentStartIdx, - i - segmentStartIdx); + i - segmentStartIdx); } appendStringInfoChar(buffer, '\\'); - if (string[i] == '"') appendStringInfoChar(buffer, '"'); - else if (string[i] == '\r') appendStringInfoChar(buffer, 'r'); - else if (string[i] == '\n') appendStringInfoChar(buffer, 'n'); - else if (string[i] == '\t') appendStringInfoChar(buffer, 't'); - else if (string[i] == '\\') appendStringInfoChar(buffer, '\\'); + if (string[i] == '"') + appendStringInfoChar(buffer, '"'); + else if (string[i] == '\r') + appendStringInfoChar(buffer, 'r'); + else if (string[i] == '\n') + appendStringInfoChar(buffer, 'n'); + else if (string[i] == '\t') + appendStringInfoChar(buffer, 't'); + else if (string[i] == '\\') + appendStringInfoChar(buffer, '\\'); segmentStartIdx = i + 1; } } - if (segmentStartIdx < len) { + if (segmentStartIdx < len) + { appendBinaryStringInfo(buffer, string + segmentStartIdx, - len - segmentStartIdx); + len - segmentStartIdx); } return buffer->data; } @@ -1967,38 +2011,38 @@ MongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *acquireSampleRowsFunc, BlockNumber *totalPageCount) { - BlockNumber pageCount = 0; - int attributeCount = 0; - int32 *attributeWidths = NULL; - Oid foreignTableId = InvalidOid; - int32 documentWidth = 0; - double documentCount = 0.0; - double foreignTableSize = 0; + BlockNumber pageCount = 0; + int attributeCount = 0; + int32 *attributeWidths = NULL; + Oid foreignTableId = InvalidOid; + int32 documentWidth = 0; + double documentCount = 0.0; + double foreignTableSize = 0; foreignTableId = RelationGetRelid(relation); documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount > 0.0) + if (documentCount >0.0) { attributeCount = RelationGetNumberOfAttributes(relation); - attributeWidths = (int32 *) palloc0((attributeCount + 1) * sizeof(int32)); + 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. + * 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; + foreignTableSize = documentCount *documentWidth; pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ); } else { ereport(ERROR, (errmsg("could not retrieve document count for collection"), - errhint("could not collect statistics about foreign table"))); + errhint("could not collect statistics about foreign table"))); } (*totalPageCount) = pageCount; @@ -2027,28 +2071,28 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, HeapTuple *sampleRows, int targetRowCount, double *totalRowCount, double *totalDeadRowCount) { - int sampleRowCount = 0; - double rowCount = 0; - double rowCountToSkip = -1; /* -1 means not set yet */ - double randomState = 0; - Datum *columnValues = NULL; - bool *columnNulls = NULL; - Oid foreignTableId = InvalidOid; - TupleDesc tupleDescriptor = NULL; - AttrNumber columnCount = 0; - AttrNumber columnId = 0; - HTAB *columnMappingHash = NULL; - MONGO_CURSOR *mongoCursor = NULL; - BSON *queryDocument = NULL; - List *columnList = NIL; - ForeignScanState *scanState = NULL; - List *foreignPrivateList = NIL; - ForeignScan *foreignScan = NULL; - MongoFdwModifyState *fmstate = NULL; - char *relationName = NULL; - int executorFlags = 0; - MemoryContext oldContext = CurrentMemoryContext; - MemoryContext tupleContext = NULL; + int sampleRowCount = 0; + double rowCount = 0; + double rowCountToSkip = -1; /* -1 means not set yet */ + double randomState = 0; + Datum *columnValues = NULL; + bool *columnNulls = NULL; + Oid foreignTableId = InvalidOid; + TupleDesc tupleDescriptor = NULL; + AttrNumber columnCount = 0; + AttrNumber columnId = 0; + HTAB *columnMappingHash = NULL; + MONGO_CURSOR *mongoCursor = NULL; + BSON *queryDocument = NULL; + List *columnList = NIL; + ForeignScanState *scanState = NULL; + List *foreignPrivateList = NIL; + ForeignScan *foreignScan = NULL; + MongoFdwModifyState *fmstate = NULL; + char *relationName = NULL; + int executorFlags = 0; + MemoryContext oldContext = CurrentMemoryContext; + MemoryContext tupleContext = NULL; /* create list of columns in the relation */ tupleDescriptor = RelationGetDescr(relation); @@ -2056,9 +2100,9 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, for (columnId = 1; columnId <= columnCount; columnId++) { - Var *column = (Var *) palloc0(sizeof(Var)); + Var *column = (Var *) palloc0(sizeof(Var)); #if PG_VERSION_NUM >= 110000 - Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId-1); + Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId - 1); column->varattno = columnId; column->vartype = attr->atttypid; @@ -2066,8 +2110,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, #else /* only assign required fields for column mapping hash */ column->varattno = columnId; - column->vartype = tupleDescriptor->attrs[columnId-1]->atttypid; - column->vartypmod = tupleDescriptor->attrs[columnId-1]->atttypmod; + column->vartype = tupleDescriptor->attrs[columnId - 1]->atttypid; + column->vartypmod = tupleDescriptor->attrs[columnId - 1]->atttypmod; #endif columnList = lappend(columnList, column); @@ -2126,7 +2170,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, memset(columnValues, 0, columnCount * sizeof(Datum)); memset(columnNulls, true, columnCount * sizeof(bool)); - if(MongoCursorNext(mongoCursor, NULL)) + if (MongoCursorNext(mongoCursor, NULL)) { const BSON *bsonDocument = MongoCursorBson(mongoCursor); const char *bsonDocumentKey = NULL; /* top level document */ @@ -2142,23 +2186,25 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, } else { - #ifdef META_DRIVER +#ifdef META_DRIVER bson_error_t error; - if (mongoc_cursor_error (mongoCursor, &error)) + + if (mongoc_cursor_error(mongoCursor, &error)) { MongoFreeScanState(fmstate); ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver error: %s", error.message))); + errhint("Mongo driver error: %s", error.message))); } - #else - mongo_cursor_error_t errorCode = mongoCursor->err; - if (errorCode != MONGO_CURSOR_EXHAUSTED) - { - MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); - } - #endif +#else + mongo_cursor_error_t errorCode = mongoCursor->err; + + if (errorCode != MONGO_CURSOR_EXHAUSTED) + { + MongoFreeScanState(fmstate); + ereport(ERROR, (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver cursor error code: %d", errorCode))); + } +#endif break; } @@ -2193,7 +2239,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, * Found a suitable tuple, so save it, replacing one old tuple * at random. */ - int rowIndex = (int) (targetRowCount * anl_random_fract()); + int rowIndex = (int) (targetRowCount * anl_random_fract()); + Assert(rowIndex >= 0); Assert(rowIndex < targetRowCount); diff --git a/mongo_fdw.h b/mongo_fdw.h index a18d5dc..6978fe1 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -21,9 +21,9 @@ #include "bson.h" #ifdef META_DRIVER - #include "mongoc.h" +#include "mongoc.h" #else - #include "mongo.h" +#include "mongo.h" #endif #include "fmgr.h" @@ -32,7 +32,7 @@ #include "utils/datetime.h" #include "nodes/pg_list.h" #if PG_VERSION_NUM < 120000 - #include "nodes/relation.h" +#include "nodes/relation.h" #endif #include "utils/timestamp.h" #include "access/reloptions.h" @@ -58,87 +58,87 @@ #include "catalog/pg_user_mapping.h" #ifdef META_DRIVER - #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 +#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 #else - #define BSON bson - #define BSON_TYPE bson_type - #define BSON_ITERATOR bson_iterator - #define MONGO_CONN mongo - #define MONGO_CURSOR mongo_cursor - #define BSON_TYPE_DOCUMENT BSON_OBJECT - #define BSON_TYPE_NULL BSON_NULL - #define BSON_TYPE_ARRAY BSON_ARRAY - #define BSON_TYPE_INT32 BSON_INT - #define BSON_TYPE_INT64 BSON_LONG - #define BSON_TYPE_DOUBLE BSON_DOUBLE - #define BSON_TYPE_BINDATA BSON_BINDATA - #define BSON_TYPE_BOOL BSON_BOOL - #define BSON_TYPE_UTF8 BSON_STRING - #define BSON_TYPE_OID BSON_OID - #define BSON_TYPE_DATE_TIME BSON_DATE - #define BSON_TYPE_SYMBOL BSON_SYMBOL - #define BSON_TYPE_UNDEFINED BSON_UNDEFINED - #define BSON_TYPE_REGEX BSON_REGEX - #define BSON_TYPE_CODE BSON_CODE - #define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE - #define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP - - #define BSON_ITER_BOOL bson_iterator_bool - #define BSON_ITER_DOUBLE bson_iterator_double - #define BSON_ITER_INT32 bson_iterator_int - #define BSON_ITER_INT64 bson_iterator_long - #define BSON_ITER_OID bson_iterator_oid - #define BSON_ITER_UTF8 bson_iterator_string - #define BSON_ITER_REGEX bson_iterator_regex - #define BSON_ITER_DATE_TIME bson_iterator_date - #define BSON_ITER_CODE bson_iterator_code - #define BSON_ITER_VALUE bson_iterator_value - #define BSON_ITER_KEY bson_iterator_key - #define BSON_ITER_NEXT bson_iterator_next - #define BSON_ITER_TYPE bson_iterator_type - #define BSON_ITER_BINARY bson_iterator_bin_data +#define BSON bson +#define BSON_TYPE bson_type +#define BSON_ITERATOR bson_iterator +#define MONGO_CONN mongo +#define MONGO_CURSOR mongo_cursor +#define BSON_TYPE_DOCUMENT BSON_OBJECT +#define BSON_TYPE_NULL BSON_NULL +#define BSON_TYPE_ARRAY BSON_ARRAY +#define BSON_TYPE_INT32 BSON_INT +#define BSON_TYPE_INT64 BSON_LONG +#define BSON_TYPE_DOUBLE BSON_DOUBLE +#define BSON_TYPE_BINDATA BSON_BINDATA +#define BSON_TYPE_BOOL BSON_BOOL +#define BSON_TYPE_UTF8 BSON_STRING +#define BSON_TYPE_OID BSON_OID +#define BSON_TYPE_DATE_TIME BSON_DATE +#define BSON_TYPE_SYMBOL BSON_SYMBOL +#define BSON_TYPE_UNDEFINED BSON_UNDEFINED +#define BSON_TYPE_REGEX BSON_REGEX +#define BSON_TYPE_CODE BSON_CODE +#define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE +#define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP + +#define BSON_ITER_BOOL bson_iterator_bool +#define BSON_ITER_DOUBLE bson_iterator_double +#define BSON_ITER_INT32 bson_iterator_int +#define BSON_ITER_INT64 bson_iterator_long +#define BSON_ITER_OID bson_iterator_oid +#define BSON_ITER_UTF8 bson_iterator_string +#define BSON_ITER_REGEX bson_iterator_regex +#define BSON_ITER_DATE_TIME bson_iterator_date +#define BSON_ITER_CODE bson_iterator_code +#define BSON_ITER_VALUE bson_iterator_value +#define BSON_ITER_KEY bson_iterator_key +#define BSON_ITER_NEXT bson_iterator_next +#define BSON_ITER_TYPE bson_iterator_type +#define BSON_ITER_BINARY bson_iterator_bin_data #endif /* Defines for valid option names */ @@ -183,7 +183,7 @@ typedef struct MongoValidOption { const char *optionName; - Oid optionContextId; + Oid optionContextId; } MongoValidOption; @@ -197,29 +197,29 @@ static const uint32 ValidOptionCount = 6; static const MongoValidOption ValidOptionArray[] = { /* foreign server options */ - { OPTION_NAME_ADDRESS, ForeignServerRelationId }, - { OPTION_NAME_PORT, ForeignServerRelationId }, + {OPTION_NAME_ADDRESS, ForeignServerRelationId}, + {OPTION_NAME_PORT, ForeignServerRelationId}, #ifdef META_DRIVER - { 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_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}, #endif /* foreign table options */ - { OPTION_NAME_DATABASE, ForeignTableRelationId }, - { OPTION_NAME_COLLECTION, ForeignTableRelationId }, + {OPTION_NAME_DATABASE, ForeignTableRelationId}, + {OPTION_NAME_COLLECTION, ForeignTableRelationId}, /* User mapping options */ - { OPTION_NAME_USERNAME, UserMappingRelationId }, - { OPTION_NAME_PASSWORD, UserMappingRelationId } + {OPTION_NAME_USERNAME, UserMappingRelationId}, + {OPTION_NAME_PASSWORD, UserMappingRelationId} }; @@ -231,23 +231,23 @@ static const MongoValidOption ValidOptionArray[] = */ typedef struct MongoFdwOptions { - char *svr_address; - int32 svr_port; - char *svr_database; - char *collectionName; - char *svr_username; - char *svr_password; + char *svr_address; + int32 svr_port; + char *svr_database; + char *collectionName; + char *svr_username; + char *svr_password; #ifdef META_DRIVER - 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; + 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; #endif } MongoFdwOptions; @@ -261,23 +261,23 @@ typedef struct MongoFdwOptions */ typedef struct MongoFdwModifyState { - Relation rel; /* relcache entry for the foreign table */ - List *target_attrs; /* list of target attribute numbers */ + 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 */ + int p_nums; /* number of parameters to transmit */ + FmgrInfo *p_flinfo; /* output conversion functions for them */ - struct HTAB *columnMappingHash; + struct HTAB *columnMappingHash; - MONGO_CONN *mongoConnection; /* MongoDB connection */ - MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ - BSON *queryDocument; /* Bson Document */ + MONGO_CONN *mongoConnection; /* MongoDB connection */ + MONGO_CURSOR *mongoCursor; /* MongoDB cursor */ + BSON *queryDocument; /* Bson Document */ - MongoFdwOptions *options; + MongoFdwOptions *options; /* working memory context */ - MemoryContext temp_cxt; /* context for per-tuple temporary data */ + MemoryContext temp_cxt; /* context for per-tuple temporary data */ } MongoFdwModifyState; @@ -289,33 +289,33 @@ typedef struct MongoFdwModifyState */ typedef struct ColumnMapping { - char columnName[NAMEDATALEN]; - uint32 columnIndex; - Oid columnTypeId; - int32 columnTypeMod; - Oid columnArrayTypeId; + char columnName[NAMEDATALEN]; + uint32 columnIndex; + Oid columnTypeId; + int32 columnTypeMod; + Oid columnArrayTypeId; } ColumnMapping; /* options.c */ -extern MongoFdwOptions * mongo_get_options(Oid foreignTableId); +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); +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); +extern void mongo_release_connection(MONGO_CONN *conn); /* Function declarations related to creating the mongo query */ -extern List * ApplicableOpExpressionList(RelOptInfo *baserel); -extern BSON * QueryDocument(Oid relationId, List *opExpressionList, - ForeignScanState *scanStateNode); -extern List * ColumnList(RelOptInfo *baserel); +extern List *ApplicableOpExpressionList(RelOptInfo *baserel); +extern BSON *QueryDocument(Oid relationId, List *opExpressionList, + ForeignScanState *scanStateNode); +extern List *ColumnList(RelOptInfo *baserel); /* Function declarations for foreign data wrapper */ extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); -#endif /* MONGO_FDW_H */ +#endif /* MONGO_FDW_H */ diff --git a/mongo_query.c b/mongo_query.c index 3158561..d6920be 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -17,9 +17,9 @@ #include "mongo_wrapper.h" #ifdef META_DRIVER - #include "mongoc.h" +#include "mongoc.h" #else - #include "mongo.h" +#include "mongo.h" #endif #include @@ -32,11 +32,11 @@ #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #if PG_VERSION_NUM < 120000 - #include "nodes/relation.h" - #include "optimizer/var.h" +#include "nodes/relation.h" +#include "optimizer/var.h" #endif #if PG_VERSION_NUM >= 120000 - #include "optimizer/optimizer.h" +#include "optimizer/optimizer.h" #endif #include "utils/array.h" #include "utils/builtins.h" @@ -46,15 +46,16 @@ #include "utils/timestamp.h" /* Local functions forward declarations */ -static Expr * FindArgumentOfType(List *argumentList, NodeTag argumentType); -static char * MongoOperatorName(const char *operatorName); -static List * EqualityOperatorList(List *operatorList); -static List * UniqueColumnList(List *operatorList); -static List * ColumnOperatorList(Var *column, List *operatorList); +static Expr *FindArgumentOfType(List *argumentList, NodeTag argumentType); +static char *MongoOperatorName(const char *operatorName); +static List *EqualityOperatorList(List *operatorList); +static List *UniqueColumnList(List *operatorList); +static List *ColumnOperatorList(Var *column, List *operatorList); static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); static void AppendParamValue(BSON *queryDocument, const char *keyName, - Param *paramNode, ForeignScanState *scanStateNode); + Param *paramNode, ForeignScanState *scanStateNode); + /* * ApplicableOpExpressionList walks over all filter clauses that relate to this * foreign table, and chooses applicable clauses that we know we can translate @@ -65,25 +66,25 @@ static void AppendParamValue(BSON *queryDocument, const char *keyName, List * ApplicableOpExpressionList(RelOptInfo *baserel) { - List *opExpressionList = NIL; - List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell = NULL; + List *opExpressionList = NIL; + List *restrictInfoList = baserel->baserestrictinfo; + ListCell *restrictInfoCell = NULL; foreach(restrictInfoCell, restrictInfoList) { RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); - Expr *expression = restrictInfo->clause; - NodeTag expressionType = 0; - - OpExpr *opExpression = NULL; - char *operatorName = NULL; - char *mongoOperatorName = NULL; - List *argumentList = NIL; - Var *column = NULL; - Const *constant = NULL; - bool equalsOperator = false; - bool constantIsArray = false; - Param *paramNode = NULL; + Expr *expression = restrictInfo->clause; + NodeTag expressionType = 0; + + OpExpr *opExpression = NULL; + char *operatorName = NULL; + char *mongoOperatorName = NULL; + List *argumentList = NIL; + Var *column = NULL; + Const *constant = NULL; + bool equalsOperator = false; + bool constantIsArray = false; + Param *paramNode = NULL; /* we only support operator expressions */ expressionType = nodeTag(expression); @@ -108,8 +109,9 @@ ApplicableOpExpressionList(RelOptInfo *baserel) } /* - * We only support simple binary operators that compare a column against - * a constant. If the expression is a tree, we don't recurse into it. + * We only support simple binary operators that compare a column + * against a constant. If the expression is a tree, we don't recurse + * into it. */ argumentList = opExpression->args; column = (Var *) FindArgumentOfType(argumentList, T_Var); @@ -118,14 +120,16 @@ ApplicableOpExpressionList(RelOptInfo *baserel) /* * We don't push down operators where the constant is an array, since - * conditional operators for arrays in MongoDB aren't properly defined. - * For example, {similar_products : [ "B0009S4IJW", "6301964144" ]} - * finds results that are equal to the array, but {similar_products: - * {$gte: [ "B0009S4IJW", "6301964144" ]}} returns an empty set. + * conditional operators for arrays in MongoDB aren't properly + * defined. For example, {similar_products : [ "B0009S4IJW", + * "6301964144" ]} finds results that are equal to the array, but + * {similar_products: {$gte: [ "B0009S4IJW", "6301964144" ]}} returns + * an empty set. */ if (constant != NULL) { - Oid constantArrayTypeId = get_element_type(constant->consttype); + Oid constantArrayTypeId = get_element_type(constant->consttype); + if (constantArrayTypeId != InvalidOid) { constantIsArray = true; @@ -154,12 +158,13 @@ ApplicableOpExpressionList(RelOptInfo *baserel) static Expr * FindArgumentOfType(List *argumentList, NodeTag argumentType) { - Expr *foundArgument = NULL; - ListCell *argumentCell = NULL; + Expr *foundArgument = NULL; + ListCell *argumentCell = NULL; foreach(argumentCell, argumentList) { - Expr *argument = (Expr *) lfirst(argumentCell); + Expr *argument = (Expr *) lfirst(argumentCell); + if (nodeTag(argument) == argumentType) { foundArgument = argument; @@ -182,14 +187,15 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) BSON * QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode) { - List *equalityOperatorList = NIL; - List *comparisonOperatorList = NIL; - List *columnList = NIL; - ListCell *equalityOperatorCell = NULL; - ListCell *columnCell = NULL; - BSON *queryDocument = NULL; + List *equalityOperatorList = NIL; + List *comparisonOperatorList = NIL; + List *columnList = NIL; + ListCell *equalityOperatorCell = NULL; + ListCell *columnCell = NULL; + BSON *queryDocument = NULL; queryDocument = BsonCreate(); + /* * We distinguish between equality expressions and others since we need to * insert the latter (<, >, <=, >=, <>) as separate sub-documents into the @@ -201,14 +207,15 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat /* append equality expressions to the query */ foreach(equalityOperatorCell, equalityOperatorList) { - OpExpr *equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); - Oid columnId = InvalidOid; - char *columnName = NULL; - Const *constant = NULL; - Param *paramNode = NULL; - - List *argumentList = equalityOperator->args; - Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); + OpExpr *equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); + Oid columnId = InvalidOid; + char *columnName = NULL; + Const *constant = NULL; + Param *paramNode = NULL; + + List *argumentList = equalityOperator->args; + Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); + constant = (Const *) FindArgumentOfType(argumentList, T_Const); paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); @@ -237,12 +244,12 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat /* append comparison expressions, grouped by columns, to the query */ foreach(columnCell, columnList) { - Var *column = (Var *) lfirst(columnCell); - Oid columnId = InvalidOid; - char *columnName = NULL; - List *columnOperatorList = NIL; - ListCell *columnOperatorCell = NULL; - BSON r; + Var *column = (Var *) lfirst(columnCell); + Oid columnId = InvalidOid; + char *columnName = NULL; + List *columnOperatorList = NIL; + ListCell *columnOperatorCell = NULL; + BSON r; columnId = column->varattno; #if PG_VERSION_NUM < 110000 @@ -259,12 +266,12 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat foreach(columnOperatorCell, columnOperatorList) { - OpExpr *columnOperator = (OpExpr *) lfirst(columnOperatorCell); - char *operatorName = NULL; - char *mongoOperatorName = NULL; + OpExpr *columnOperator = (OpExpr *) lfirst(columnOperatorCell); + char *operatorName = NULL; + char *mongoOperatorName = NULL; - List *argumentList = columnOperator->args; - Const *constant = (Const *) FindArgumentOfType(argumentList, T_Const); + List *argumentList = columnOperator->args; + Const *constant = (Const *) FindArgumentOfType(argumentList, T_Const); operatorName = get_opname(columnOperator->opno); mongoOperatorName = MongoOperatorName(operatorName); @@ -301,16 +308,18 @@ MongoOperatorName(const char *operatorName) { const char *mongoOperatorName = NULL; const int32 nameCount = 5; - static const char *nameMappings[][2] = { { "<", "$lt" }, - { ">", "$gt" }, - { "<=", "$lte" }, - { ">=", "$gte" }, - { "<>", "$ne" } }; + static const char *nameMappings[][2] = {{"<", "$lt"}, + {">", "$gt"}, + {"<=", "$lte"}, + {">=", "$gte"}, + {"<>", "$ne"}}; + + int32 nameIndex = 0; - int32 nameIndex = 0; for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { const char *pgOperatorName = nameMappings[nameIndex][0]; + if (strncmp(pgOperatorName, operatorName, NAMEDATALEN) == 0) { mongoOperatorName = nameMappings[nameIndex][1]; @@ -329,13 +338,13 @@ MongoOperatorName(const char *operatorName) static List * EqualityOperatorList(List *operatorList) { - List *equalityOperatorList = NIL; - ListCell *operatorCell = NULL; + List *equalityOperatorList = NIL; + ListCell *operatorCell = NULL; foreach(operatorCell, operatorList) { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - char *operatorName = NULL; + OpExpr *operator = (OpExpr *) lfirst(operatorCell); + char *operatorName = NULL; operatorName = get_opname(operator->opno); if (strncmp(operatorName, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) @@ -356,14 +365,14 @@ EqualityOperatorList(List *operatorList) static List * UniqueColumnList(List *operatorList) { - List *uniqueColumnList = NIL; - ListCell *operatorCell = NULL; + List *uniqueColumnList = NIL; + ListCell *operatorCell = NULL; foreach(operatorCell, operatorList) { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - List *argumentList = operator->args; - Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); + OpExpr *operator = (OpExpr *) lfirst(operatorCell); + List *argumentList = operator->args; + Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); /* list membership is determined via column's equal() function */ uniqueColumnList = list_append_unique(uniqueColumnList, column); @@ -380,15 +389,16 @@ UniqueColumnList(List *operatorList) static List * ColumnOperatorList(Var *column, List *operatorList) { - List *columnOperatorList = NIL; - ListCell *operatorCell = NULL; + List *columnOperatorList = NIL; + ListCell *operatorCell = NULL; foreach(operatorCell, operatorList) { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - List *argumentList = operator->args; + OpExpr *operator = (OpExpr *) lfirst(operatorCell); + List *argumentList = operator->args; + + Var *foundColumn = (Var *) FindArgumentOfType(argumentList, T_Var); - Var *foundColumn = (Var *) FindArgumentOfType(argumentList, T_Var); if (equal(column, foundColumn)) { columnOperatorList = lappend(columnOperatorList, operator); @@ -400,12 +410,12 @@ ColumnOperatorList(Var *column, List *operatorList) static void AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, - ForeignScanState *scanStateNode) + ForeignScanState *scanStateNode) { - ExprState *param_expr; + ExprState *param_expr; Datum param_value; bool isNull; - ExprContext *econtext; + ExprContext *econtext; if (scanStateNode == NULL) return; @@ -413,7 +423,7 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, econtext = scanStateNode->ss.ps.ps_ExprContext; /* Prepare for parameter expression evaluation */ - param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *)scanStateNode); + param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *) scanStateNode); /* Evaluate the parameter expression */ #if PG_VERSION_NUM >= 100000 @@ -424,7 +434,7 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, AppenMongoValue(queryDocument, keyName, param_value, isNull, - paramNode->paramtype); + paramNode->paramtype); } /* @@ -446,239 +456,258 @@ AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) { - bool status = false; + bool status = false; + if (isnull) { status = BsonAppendNull(queryDocument, keyName); return status; } - switch(id) + switch (id) { case INT2OID: - { - int16 valueInt = DatumGetInt16(value); - status = BsonAppendInt32(queryDocument, keyName, (int) valueInt); - break; - } + { + int16 valueInt = DatumGetInt16(value); + + status = BsonAppendInt32(queryDocument, keyName, (int) valueInt); + break; + } case INT4OID: - { - int32 valueInt = DatumGetInt32(value); - status = BsonAppendInt32(queryDocument, keyName, valueInt); - break; - } + { + int32 valueInt = DatumGetInt32(value); + + status = BsonAppendInt32(queryDocument, keyName, valueInt); + break; + } case INT8OID: - { - int64 valueLong = DatumGetInt64(value); - status = BsonAppendInt64(queryDocument, keyName, valueLong); - break; - } + { + int64 valueLong = DatumGetInt64(value); + + status = BsonAppendInt64(queryDocument, keyName, valueLong); + break; + } case FLOAT4OID: - { - float4 valueFloat = DatumGetFloat4(value); - status = BsonAppendDouble(queryDocument, keyName, (double) valueFloat); - break; - } + { + float4 valueFloat = DatumGetFloat4(value); + + status = BsonAppendDouble(queryDocument, keyName, (double) valueFloat); + break; + } case FLOAT8OID: - { - float8 valueFloat = DatumGetFloat8(value); - status = BsonAppendDouble(queryDocument, keyName, valueFloat); - break; - } + { + 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; - } + { + 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; - } + { + bool valueBool = DatumGetBool(value); + + status = BsonAppendBool(queryDocument, keyName, (int) valueBool); + break; + } case BPCHAROID: case VARCHAROID: case TEXTOID: - { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; - 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); + { + char *outputString = NULL; + Oid outputFunctionId = InvalidOid; + bool typeVarLength = false; + + 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); + } #ifdef META_DRIVER - 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); - } + 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); + } #else - status = BsonAppendBinary(queryDocument, keyName, data, len); + status = BsonAppendBinary(queryDocument, keyName, data, len); #endif - break; - } + break; + } case NAMEOID: - { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; - 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; - } + { + char *outputString = NULL; + Oid outputFunctionId = InvalidOid; + bool typeVarLength = false; + 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; + { + 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; - } + 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; + { + Timestamp valueTimestamp = DatumGetTimestamp(value); + int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; + int64 valueMilliSecs = valueMicroSecs / 1000; - status = BsonAppendDate(queryDocument, keyName, valueMilliSecs); - break; - } + 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 t; - - 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, &t); - 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); + ArrayType *array; + Oid elmtype; + int16 elmlen; + bool elmbyval; + char elmalign; + int num_elems; + Datum *elem_values; + bool *elem_nulls; + int i; + BSON t; + + 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, &t); + 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); #ifdef META_DRIVER - status = BsonAppendDouble(&t, keyName, valueFloat); + status = BsonAppendDouble(&t, keyName, valueFloat); #else - status = BsonAppendDouble(queryDocument, keyName, valueFloat); + status = BsonAppendDouble(queryDocument, keyName, valueFloat); #endif + } + BsonAppendFinishArray(queryDocument, &t); + pfree(elem_values); + pfree(elem_nulls); + break; } - BsonAppendFinishArray(queryDocument, &t); - 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 t; - - 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, &t); - for (i = 0; i < num_elems; i++) { - char *valueString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; - if (elem_nulls[i]) - continue; - getTypeOutputInfo(TEXTOID, &outputFunctionId, &typeVarLength); - valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); - status = BsonAppendUTF8(queryDocument, keyName, valueString); + ArrayType *array; + Oid elmtype; + int16 elmlen; + bool elmbyval; + char elmalign; + int num_elems; + Datum *elem_values; + bool *elem_nulls; + int i; + BSON t; + + 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, &t); + for (i = 0; i < num_elems; i++) + { + char *valueString = NULL; + Oid outputFunctionId = InvalidOid; + bool typeVarLength = false; + + if (elem_nulls[i]) + continue; + getTypeOutputInfo(TEXTOID, &outputFunctionId, &typeVarLength); + valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); + status = BsonAppendUTF8(queryDocument, keyName, valueString); + } + BsonAppendFinishArray(queryDocument, &t); + pfree(elem_values); + pfree(elem_nulls); + break; } - BsonAppendFinishArray(queryDocument, &t); - pfree(elem_values); - pfree(elem_nulls); - break; - } case JSONOID: - { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; - struct json_object *o; - bool typeVarLength = false; - getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); - outputString = OidOutputFunctionCall(outputFunctionId, value); - o = JsonTokenerPrase(outputString); - - if (is_error(o)) { - elog(WARNING, "cannot parse the document"); - status = 0; + char *outputString = NULL; + Oid outputFunctionId = InvalidOid; + struct json_object *o; + bool typeVarLength = false; + + getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); + outputString = OidOutputFunctionCall(outputFunctionId, value); + o = JsonTokenerPrase(outputString); + + if (is_error(o)) + { + elog(WARNING, "cannot parse the document"); + status = 0; + break; + } + + status = JsonToBsonAppendElement(queryDocument, keyName, o); 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; - } + { + /* + * 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; } @@ -693,18 +722,18 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu List * ColumnList(RelOptInfo *baserel) { - List *columnList = NIL; - List *neededColumnList = NIL; - AttrNumber columnIndex = 1; - AttrNumber columnCount = baserel->max_attr; + List *columnList = NIL; + List *neededColumnList = NIL; + AttrNumber columnIndex = 1; + AttrNumber columnCount = baserel->max_attr; #if PG_VERSION_NUM >= 90600 - List *targetColumnList = baserel->reltarget->exprs; + List *targetColumnList = baserel->reltarget->exprs; #else - List *targetColumnList = baserel->reltargetlist; + List *targetColumnList = baserel->reltargetlist; #endif - List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell = NULL; + List *restrictInfoList = baserel->baserestrictinfo; + ListCell *restrictInfoCell = NULL; /* first add the columns used in joins and projections */ neededColumnList = list_copy(targetColumnList); @@ -713,15 +742,15 @@ ColumnList(RelOptInfo *baserel) foreach(restrictInfoCell, restrictInfoList) { RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); - Node *restrictClause = (Node *) restrictInfo->clause; - List *clauseColumnList = NIL; + Node *restrictClause = (Node *) restrictInfo->clause; + List *clauseColumnList = NIL; /* recursively pull up any columns used in the restriction clause */ clauseColumnList = pull_var_clause(restrictClause, #if PG_VERSION_NUM < 90600 - PVC_RECURSE_AGGREGATES, + PVC_RECURSE_AGGREGATES, #endif - PVC_RECURSE_PLACEHOLDERS); + PVC_RECURSE_PLACEHOLDERS); neededColumnList = list_union(neededColumnList, clauseColumnList); } @@ -729,13 +758,14 @@ ColumnList(RelOptInfo *baserel) /* walk over all column definitions, and de-duplicate column list */ for (columnIndex = 1; columnIndex <= columnCount; columnIndex++) { - ListCell *neededColumnCell = NULL; - Var *column = NULL; + ListCell *neededColumnCell = NULL; + Var *column = NULL; /* look for this column in the needed column list */ foreach(neededColumnCell, neededColumnList) { - Var *neededColumn = (Var *) lfirst(neededColumnCell); + Var *neededColumn = (Var *) lfirst(neededColumnCell); + if (neededColumn->varattno == columnIndex) { column = neededColumn; diff --git a/mongo_query.h b/mongo_query.h index 245c451..aa25db8 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -19,6 +19,6 @@ #define NUMERICARRAY_OID 1231 -bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); +bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); -#endif /* MONGO_QUERY_H */ +#endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 2f27ac8..0b98f4d 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -17,9 +17,9 @@ #include "mongo_wrapper.h" #ifdef META_DRIVER - #include "mongoc.h" +#include "mongoc.h" #else - #include "mongo.h" +#include "mongo.h" #endif #include @@ -30,16 +30,18 @@ #define QUAL_STRING_LEN 512 -MONGO_CONN* -MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password) +MONGO_CONN * +MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password) { MONGO_CONN *conn; + conn = mongo_alloc(); mongo_init(conn); if (mongo_connect(conn, host, port) != MONGO_OK) { - int err = conn->err; + int err = conn->err; + mongo_destroy(conn); mongo_dealloc(conn); ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), @@ -49,7 +51,8 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch { if (mongo_cmd_authenticate(conn, databaseName, user, password) != MONGO_OK) { - char *str = pstrdup(conn->errstr); + char *str = pstrdup(conn->errstr); + mongo_destroy(conn); mongo_dealloc(conn); ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), @@ -60,18 +63,18 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch } void -MongoDisconnect(MONGO_CONN* conn) +MongoDisconnect(MONGO_CONN *conn) { mongo_destroy(conn); mongo_dealloc(conn); } bool -MongoInsert(MONGO_CONN* conn, char* database, char *collection, bson* b) +MongoInsert(MONGO_CONN *conn, char *database, char *collection, bson *b) { - char qual[QUAL_STRING_LEN]; + char qual[QUAL_STRING_LEN]; - snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); if (mongo_insert(conn, qual, b, NULL) != MONGO_OK) ereport(ERROR, (errmsg("failed to insert row"), errhint("Mongo driver insert error: %d", conn->err))); @@ -80,11 +83,11 @@ MongoInsert(MONGO_CONN* conn, char* database, char *collection, bson* b) bool -MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* op) +MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) { - char qual[QUAL_STRING_LEN]; + char qual[QUAL_STRING_LEN]; - snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); if (mongo_update(conn, qual, b, op, MONGO_UPDATE_BASIC, 0) != MONGO_OK) ereport(ERROR, (errmsg("failed to update row"), errhint("Mongo driver update error: %d", conn->err))); @@ -93,12 +96,12 @@ MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* o bool -MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) +MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { - char qual[QUAL_STRING_LEN]; + char qual[QUAL_STRING_LEN]; - snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); - if (mongo_remove(conn, qual, b , NULL) != MONGO_OK) + snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); + if (mongo_remove(conn, qual, b, NULL) != MONGO_OK) ereport(ERROR, (errmsg("failed to delete row"), errhint("Mongo driver delete error: %d", conn->err))); @@ -106,44 +109,45 @@ MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) } -MONGO_CURSOR* -MongoCursorCreate(MONGO_CONN* conn, char* database, char *collection, BSON* q) +MONGO_CURSOR * +MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) { - MONGO_CURSOR* c; - char qual[QUAL_STRING_LEN]; + MONGO_CURSOR *c; + char qual[QUAL_STRING_LEN]; - snprintf (qual, QUAL_STRING_LEN, "%s.%s", database, collection); + snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); c = mongo_cursor_alloc(); - mongo_cursor_init(c, conn , qual); + mongo_cursor_init(c, conn, qual); mongo_cursor_set_query(c, q); return c; } -const bson* -MongoCursorBson(MONGO_CURSOR* c) +const bson * +MongoCursorBson(MONGO_CURSOR *c) { return mongo_cursor_bson(c); } bool -MongoCursorNext(MONGO_CURSOR* c, BSON* b) +MongoCursorNext(MONGO_CURSOR *c, BSON *b) { return (mongo_cursor_next(c) == MONGO_OK); } void -MongoCursorDestroy(MONGO_CURSOR* c) +MongoCursorDestroy(MONGO_CURSOR *c) { mongo_cursor_destroy(c); mongo_cursor_dealloc(c); } -BSON* +BSON * BsonCreate() { - BSON *b = NULL; + BSON *b = NULL; + b = bson_alloc(); bson_init(b); return b; @@ -198,18 +202,20 @@ BsonIterBool(BSON_ITERATOR *it) } -const char* +const char * BsonIterString(BSON_ITERATOR *it) { return bson_iterator_string(it); } -const char* BsonIterBinData(BSON_ITERATOR *it) +const char * +BsonIterBinData(BSON_ITERATOR *it) { return bson_iterator_bin_data(it); } -int BsonIterBinLen(BSON_ITERATOR *it) +int +BsonIterBinLen(BSON_ITERATOR *it) { return bson_iterator_bin_len(it); } @@ -243,7 +249,7 @@ BsonIterNext(BSON_ITERATOR *it) bool -BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR* sub) +BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { bson_iterator_subiterator(it, sub); return true; @@ -251,93 +257,96 @@ BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR* sub) void -BsonOidFromString(bson_oid_t *o, char* str) +BsonOidFromString(bson_oid_t *o, char *str) { bson_oid_from_string(o, str); } bool -BsonAppendOid(BSON *b, const char* key, bson_oid_t *v) +BsonAppendOid(BSON *b, const char *key, bson_oid_t *v) { return (bson_append_oid(b, key, v) == MONGO_OK); } bool -BsonAppendBool(BSON *b, const char* key, bool v) +BsonAppendBool(BSON *b, const char *key, bool v) { return (bson_append_int(b, key, v) == MONGO_OK); } bool -BsonAppendNull(BSON *b, const char* key) +BsonAppendNull(BSON *b, const char *key) { return (bson_append_null(b, key) == MONGO_OK); } bool -BsonAppendInt32(BSON *b, const char* key, int v) +BsonAppendInt32(BSON *b, const char *key, int v) { return (bson_append_int(b, key, v) == MONGO_OK); } bool -BsonAppendInt64(BSON *b, const char* key, int64_t v) +BsonAppendInt64(BSON *b, const char *key, int64_t v) { return (bson_append_long(b, key, v) == MONGO_OK); } bool -BsonAppendDouble(BSON *b, const char* key, double v) +BsonAppendDouble(BSON *b, const char *key, double v) { return (bson_append_double(b, key, v) == MONGO_OK); } bool -BsonAppendUTF8(BSON *b, const char* key, char *v) +BsonAppendUTF8(BSON *b, const char *key, char *v) { return (bson_append_string(b, key, v) == MONGO_OK); } -bool BsonAppendBinary(BSON *b, const char* key, char *v, size_t len) +bool +BsonAppendBinary(BSON *b, const char *key, char *v, size_t len) { return (bson_append_binary(b, key, BSON_BIN_BINARY, v, len) == MONGO_OK); } bool -BsonAppendDate(BSON *b, const char* key, time_t v) +BsonAppendDate(BSON *b, const char *key, time_t v) { return (bson_append_date(b, key, v) == MONGO_OK); } -bool BsonAppendStartArray(BSON *b, const char* key, BSON* c) +bool +BsonAppendStartArray(BSON *b, const char *key, BSON *c) { - return (bson_append_start_array(b, key) == MONGO_OK); + return (bson_append_start_array(b, key) == MONGO_OK); } -bool BsonAppendFinishArray(BSON *b, BSON *c) +bool +BsonAppendFinishArray(BSON *b, BSON *c) { - return (bson_append_finish_array(b) == MONGO_OK); + return (bson_append_finish_array(b) == MONGO_OK); } bool -BsonAppendStartObject(BSON* b, char *key, BSON* r) +BsonAppendStartObject(BSON *b, char *key, BSON *r) { return (bson_append_start_object(b, key) == MONGO_OK); } bool -BsonAppendFinishObject(BSON* b, BSON* r) +BsonAppendFinishObject(BSON *b, BSON *r) { return (bson_append_finish_object(b) == MONGO_OK); } bool -BsonAppendBson(BSON* b, char *key, BSON* c) +BsonAppendBson(BSON *b, char *key, BSON *c) { return (bson_append_bson(b, key, c) == MONGO_OK); } @@ -346,21 +355,21 @@ BsonAppendBson(BSON* b, char *key, BSON* c) bool -BsonFinish(BSON* b) +BsonFinish(BSON *b) { return (bson_finish(b) == MONGO_OK); } -json_object* -JsonTokenerPrase(char * s) +json_object * +JsonTokenerPrase(char *s) { return json_tokener_parse(s); } bool -JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) +JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { - bool status; + bool status; status = true; if (!v) @@ -372,110 +381,115 @@ JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) switch (json_object_get_type(v)) { case json_type_int: - bson_append_int(bb, k, json_object_get_int(v)); - break; + bson_append_int(bb, k, json_object_get_int(v)); + break; case json_type_boolean: - bson_append_bool(bb , k, json_object_get_boolean(v)); - break; + bson_append_bool(bb, k, json_object_get_boolean(v)); + break; case json_type_double: - bson_append_double(bb, k, json_object_get_double(v)); - break; + bson_append_double(bb, k, json_object_get_double(v)); + break; case json_type_string: - bson_append_string(bb, k, json_object_get_string(v)); - break; + bson_append_string(bb, k, json_object_get_string(v)); + break; case json_type_object: - { - struct json_object *joj = NULL; - 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; - } + struct json_object *joj = NULL; - bson_append_start_object(bb , k); + joj = json_object_object_get(v, "$oid"); - { - json_object_object_foreach(v, kk, vv) + if (joj != NULL) { - JsonToBsonAppendElement(bb, kk, vv); + 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; + } + + bson_append_start_object(bb, k); + + { + json_object_object_foreach(v, kk, vv) + { + JsonToBsonAppendElement(bb, kk, vv); + } + } + bson_append_finish_object(bb); + break; } - bson_append_finish_object(bb); - break; - } case json_type_array: - { - int i; - char buf[10]; - bson_append_start_array(bb ,k); - for (i = 0; i #ifdef META_DRIVER -MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password, - char *authenticationDatabase,char *replicaSet, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, - char *ca_dir, char *crl_file, bool weak_cert_validation); +MONGO_CONN *MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password, + char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, + char *ca_dir, char *crl_file, bool weak_cert_validation); #else -MONGO_CONN* MongoConnect(const char* host, const unsigned short port, char *databaseName, char *user, char *password); +MONGO_CONN *MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password); #endif -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); +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); +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); +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 *BsonIterString(BSON_ITERATOR *it); #ifdef META_DRIVER -const char* BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); +const char *BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); #else -const char* BsonIterBinData(BSON_ITERATOR *it); -int BsonIterBinLen(BSON_ITERATOR *it); +const char *BsonIterBinData(BSON_ITERATOR *it); +int BsonIterBinLen(BSON_ITERATOR *it); #endif #ifdef META_DRIVER const bson_oid_t *BsonIterOid(BSON_ITERATOR *it); #else -bson_oid_t * BsonIterOid(BSON_ITERATOR *it); +bson_oid_t *BsonIterOid(BSON_ITERATOR *it); #endif -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); +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 char *BsonIterCode(BSON_ITERATOR *i); +const char *BsonIterRegex(BSON_ITERATOR *i); +const char *BsonIterKey(BSON_ITERATOR *i); #ifdef META_DRIVER const bson_value_t *BsonIterValue(BSON_ITERATOR *i); #else -const char* BsonIterValue(BSON_ITERATOR *i); +const char *BsonIterValue(BSON_ITERATOR *i); #endif -void BsonIteratorFromBuffer(BSON_ITERATOR* i, const char * buffer); +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 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 BsonFinish(BSON* b); -bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v); -json_object *JsonTokenerPrase(char * s); +bool BsonAppendStartObject(BSON *b, char *key, BSON *r); +bool BsonAppendFinishObject(BSON *b, BSON *r); +bool BsonAppendBson(BSON *b, char *key, BSON *c); +bool BsonFinish(BSON *b); +bool JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v); +json_object *JsonTokenerPrase(char *s); -char* BsonAsJson(const BSON* bsonDocument); +char *BsonAsJson(const BSON *bsonDocument); void BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray); void DumpJsonObject(StringInfo output, BSON_ITERATOR *iter); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index dd95aed..a10f2ac 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -21,61 +21,58 @@ /* * Connect to MongoDB server using Host/ip and Port number. */ -MONGO_CONN* -MongoConnect(const char* host, const unsigned short port, char* databaseName, char *user, char *password, - char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, - char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) +MONGO_CONN * +MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password, + char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, + char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) { MONGO_CONN *client = NULL; - char* uri = NULL; + char *uri = NULL; if (user && password) - if (authenticationDatabase) - if (replicaSet) - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase,replicaSet); - else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase,replicaSet); - else - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",authenticationDatabase); - else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s", user, password, host, port, databaseName, ssl ? "true" : "false",authenticationDatabase); + if (authenticationDatabase) + if (replicaSet) + if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", authenticationDatabase, replicaSet); + else + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false", authenticationDatabase, replicaSet); + else if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", authenticationDatabase); + else + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&authSource=%s", user, password, host, port, databaseName, ssl ? "true" : "false", authenticationDatabase); + else if (replicaSet) + if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", replicaSet); + else + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false", replicaSet); + else if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); else - if (replicaSet) - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false",replicaSet); - else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s&replicaSet=%s", user, password, host, port, databaseName, ssl ? "true" : "false",replicaSet); - else - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); - else - uri = bson_strdup_printf ("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); + uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?ssl=%s", user, password, host, port, databaseName, ssl ? "true" : "false"); + else if (replicaSet) + if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", host, port, databaseName, readPreference, ssl ? "true" : "false", replicaSet); + else + uri = bson_strdup_printf("mongodb://%s:%hu/%s?ssl=%s&replicaSet=%s", host, port, databaseName, ssl ? "true" : "false", replicaSet); + else if (readPreference) + uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); else - if (replicaSet) - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", host, port, databaseName, readPreference, ssl ? "true" : "false",replicaSet); - else - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s&replicaSet=%s", host, port, databaseName, ssl ? "true" : "false",replicaSet); - else - if (readPreference) - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); - else - uri = bson_strdup_printf ("mongodb://%s:%hu/%s?ssl=%s", host, port, databaseName, ssl ? "true" : "false"); + uri = bson_strdup_printf("mongodb://%s:%hu/%s?ssl=%s", host, port, databaseName, ssl ? "true" : "false"); client = mongoc_client_new(uri); - if (ssl) { - mongoc_ssl_opt_t *ssl_opts = (mongoc_ssl_opt_t*) malloc(sizeof(mongoc_ssl_opt_t)); + if (ssl) + { + mongoc_ssl_opt_t *ssl_opts = (mongoc_ssl_opt_t *) malloc(sizeof(mongoc_ssl_opt_t)); + ssl_opts->pem_file = pem_file; ssl_opts->pem_pwd = pem_pwd; ssl_opts->ca_file = ca_file; ssl_opts->ca_dir = ca_dir; ssl_opts->crl_file = crl_file; ssl_opts->weak_cert_validation = weak_cert_validation; - mongoc_client_set_ssl_opts (client, ssl_opts); + mongoc_client_set_ssl_opts(client, ssl_opts); free(ssl_opts); } @@ -91,7 +88,7 @@ MongoConnect(const char* host, const unsigned short port, char* databaseName, ch * Disconnect from MongoDB server. */ void -MongoDisconnect(MONGO_CONN* conn) +MongoDisconnect(MONGO_CONN *conn) { if (conn) mongoc_client_destroy(conn); @@ -102,11 +99,11 @@ MongoDisconnect(MONGO_CONN* conn) * Insert a document 'b' into MongoDB. */ bool -MongoInsert(MONGO_CONN* conn, char *database, char* collection, BSON* b) +MongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) { mongoc_collection_t *c = NULL; bson_error_t error; - bool r = false; + bool r = false; c = mongoc_client_get_collection(conn, database, collection); @@ -123,13 +120,13 @@ MongoInsert(MONGO_CONN* conn, char *database, char* collection, BSON* b) * Update a document 'b' into MongoDB. */ bool -MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* op) +MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) { mongoc_collection_t *c = NULL; bson_error_t error; - bool r = false; + bool r = false; - c = mongoc_client_get_collection (conn, database, collection); + c = mongoc_client_get_collection(conn, database, collection); r = mongoc_collection_update(c, MONGOC_UPDATE_NONE, b, op, NULL, &error); mongoc_collection_destroy(c); @@ -144,13 +141,13 @@ MongoUpdate(MONGO_CONN* conn, char* database, char *collection, BSON* b, BSON* o * Delete MongoDB's document. */ bool -MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) +MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { mongoc_collection_t *c = NULL; bson_error_t error; - bool r = false; + bool r = false; - c = mongoc_client_get_collection (conn, database, collection); + c = mongoc_client_get_collection(conn, database, collection); r = mongoc_collection_remove(c, MONGOC_DELETE_SINGLE_REMOVE, b, NULL, &error); mongoc_collection_destroy(c); @@ -164,14 +161,14 @@ MongoDelete(MONGO_CONN* conn, char* database, char *collection, BSON* b) * 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) +MONGO_CURSOR * +MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) { mongoc_collection_t *c = NULL; MONGO_CURSOR *cur = NULL; bson_error_t error; - c = mongoc_client_get_collection (conn, database, collection); + c = mongoc_client_get_collection(conn, database, collection); cur = mongoc_collection_find_with_opts(c, q, NULL, NULL); mongoc_cursor_error(cur, &error); if (!cur) @@ -187,7 +184,7 @@ MongoCursorCreate(MONGO_CONN* conn, char* database, char *collection, BSON* q) * Destroy cursor created by calling MongoCursorCreate function. */ void -MongoCursorDestroy(MONGO_CURSOR* c) +MongoCursorDestroy(MONGO_CURSOR *c) { mongoc_cursor_destroy(c); } @@ -196,8 +193,8 @@ MongoCursorDestroy(MONGO_CURSOR* c) /* * Get the current document from cursor. */ -const BSON* -MongoCursorBson(MONGO_CURSOR* c) +const BSON * +MongoCursorBson(MONGO_CURSOR *c) { return mongoc_cursor_current(c); } @@ -206,9 +203,9 @@ MongoCursorBson(MONGO_CURSOR* c) * Get the next document from the cursor. */ bool -MongoCursorNext(MONGO_CURSOR* c, BSON *b) +MongoCursorNext(MONGO_CURSOR *c, BSON *b) { - return mongoc_cursor_next(c, (const BSON**) &b); + return mongoc_cursor_next(c, (const BSON **) &b); } @@ -218,10 +215,11 @@ MongoCursorNext(MONGO_CURSOR* c, BSON *b) * object and can be iterated. A newly allocated bson_t that should * be freed with bson_destroy(). */ -BSON* +BSON * BsonCreate(void) { - BSON *b = NULL; + BSON *b = NULL; + b = bson_new(); bson_init(b); return b; @@ -251,7 +249,7 @@ bool BsonIterSubObject(BSON_ITERATOR *it, BSON *b) { const uint8_t *buffer; - uint32_t len; + uint32_t len; bson_iter_document(it, &len, &buffer); bson_init_static(b, buffer, len); @@ -286,20 +284,22 @@ BsonIterBool(BSON_ITERATOR *it) } -const char* +const char * BsonIterString(BSON_ITERATOR *it) { - uint32_t len = 0; + uint32_t len = 0; + return bson_iter_utf8(it, &len); } -const char* +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; + + bson_iter_binary(it, &subtype, len, &binary); + return (char *) binary; } const bson_oid_t * @@ -316,7 +316,7 @@ BsonIterDate(BSON_ITERATOR *it) } -const char* +const char * BsonIterKey(BSON_ITERATOR *it) { return bson_iter_key(it); @@ -336,123 +336,125 @@ BsonIterNext(BSON_ITERATOR *it) bool -BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR* sub) +BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { return bson_iter_recurse(it, sub); } void -BsonOidFromString(bson_oid_t *o, char* str) +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) +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) +BsonAppendBool(BSON *b, const char *key, bool v) { return bson_append_bool(b, key, -1, v); } bool -BsonAppendStartObject(BSON* b, char *key, BSON* r) +BsonAppendStartObject(BSON *b, char *key, BSON *r) { return bson_append_document_begin(b, key, strlen(key), r); } bool -BsonAppendFinishObject(BSON* b, BSON* r) +BsonAppendFinishObject(BSON *b, BSON *r) { return bson_append_document_end(b, r); } bool -BsonAppendNull(BSON *b, const char* key) +BsonAppendNull(BSON *b, const char *key) { return bson_append_null(b, key, strlen(key)); } bool -BsonAppendInt32(BSON *b, const char* key, int v) +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) +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) +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) +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) +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); + 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) +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) +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) +bool +BsonAppendStartArray(BSON *b, const char *key, BSON *c) { - return bson_append_array_begin(b, key, -1, c); + return bson_append_array_begin(b, key, -1, c); } -bool BsonAppendFinishArray(BSON *b, BSON* c) +bool +BsonAppendFinishArray(BSON *b, BSON *c) { - return bson_append_array_end(b, c); + return bson_append_array_end(b, c); } bool -BsonFinish(BSON* b) +BsonFinish(BSON *b) { /* - * There is no need for bson_finish in Meta Driver. - * We are doing nothing, just because of compatiblity with legacy - * driver. + * There is no need for bson_finish in Meta Driver. We are doing nothing, + * just because of compatiblity with legacy driver. */ return true; } -bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) +bool +JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { - bool status; + bool status; status = true; if (!v) @@ -464,75 +466,78 @@ bool JsonToBsonAppendElement(BSON *bb , const char *k , struct json_object *v ) switch (json_object_get_type(v)) { case json_type_int: - BsonAppendInt32(bb, k, json_object_get_int(v)); - break; + BsonAppendInt32(bb, k, json_object_get_int(v)); + break; case json_type_boolean: - BsonAppendBool(bb , k, json_object_get_boolean(v)); - break; + BsonAppendBool(bb, k, json_object_get_boolean(v)); + break; case json_type_double: - BsonAppendDouble(bb, k, json_object_get_double(v)); - break; + BsonAppendDouble(bb, k, json_object_get_double(v)); + break; case json_type_string: - BsonAppendUTF8(bb, k, (char*)json_object_get_string(v)); - break; + BsonAppendUTF8(bb, k, (char *) json_object_get_string(v)); + break; case json_type_object: - { - BSON t; - struct json_object *joj = NULL; - 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); + BSON t; + struct json_object *joj = NULL; - { - json_object_object_foreach(v, kk, vv) + joj = json_object_object_get(v, "$oid"); + + if (joj != NULL) { - JsonToBsonAppendElement(&t, kk, vv); + 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; } - BsonAppendFinishObject(bb, &t); - break; - } case json_type_array: - { - int i; - char buf[10]; - BSON t; - BsonAppendStartArray(bb ,k, &t); - for (i = 0; idefname; - bool optionValid = false; + DefElem *optionDef = (DefElem *) lfirst(optionCell); + char *optionName = optionDef->defname; + bool optionValid = false; + + int32 optionIndex = 0; - int32 optionIndex = 0; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) { - const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); + const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); if ((optionContextId == validOption->optionContextId) && (strncmp(optionName, validOption->optionName, NAMEDATALEN) == 0)) @@ -86,7 +87,7 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) /* if invalid option, display an informative error message */ if (!optionValid) { - StringInfo optionNamesString = mongo_option_names_string(optionContextId); + StringInfo optionNamesString = mongo_option_names_string(optionContextId); ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_OPTION_NAME), errmsg("invalid option \"%s\"", optionName), @@ -97,8 +98,9 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) /* if port option is given, error out if its value isn't an integer */ if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0) { - char *optionValue = defGetString(optionDef); - int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); + char *optionValue = defGetString(optionDef); + int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); + (void) portNumber; } } @@ -112,13 +114,14 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) StringInfo mongo_option_names_string(Oid currentContextId) { - StringInfo optionNamesString = makeStringInfo(); - bool firstOptionPrinted = false; + StringInfo optionNamesString = makeStringInfo(); + bool firstOptionPrinted = false; + + int32 optionIndex = 0; - int32 optionIndex = 0; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) { - const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); + const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); /* if option belongs to current context, append option name */ if (currentContextId == validOption->optionContextId) @@ -142,25 +145,25 @@ mongo_option_names_string(Oid currentContextId) MongoFdwOptions * mongo_get_options(Oid foreignTableId) { - MongoFdwOptions *options = NULL; - char *addressName = NULL; - char *portName = NULL; - int32 portNumber = 0; - char *svr_database = NULL; - char *collectionName = NULL; - char *svr_username= NULL; - char *svr_password= NULL; + MongoFdwOptions *options = NULL; + char *addressName = NULL; + char *portName = NULL; + int32 portNumber = 0; + char *svr_database = NULL; + char *collectionName = NULL; + char *svr_username = NULL; + char *svr_password = NULL; #ifdef META_DRIVER - char *readPreference = NULL; - char *authenticationDatabase = NULL; - char *replicaSet = NULL; - bool ssl = false; - char *pem_file = NULL; - char *pem_pwd = NULL; - char *ca_file = NULL; - char *ca_dir = NULL; - char *crl_file = NULL; - bool weak_cert_validation = false; + char *readPreference = NULL; + char *authenticationDatabase = NULL; + char *replicaSet = NULL; + bool ssl = false; + char *pem_file = NULL; + char *pem_pwd = NULL; + char *ca_file = NULL; + char *ca_dir = NULL; + char *crl_file = NULL; + bool weak_cert_validation = false; readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); authenticationDatabase = mongo_get_option_value(foreignTableId, OPTION_NAME_AUTHENTICATION_DATABASE); @@ -239,12 +242,12 @@ mongo_free_options(MongoFdwOptions *options) static char * mongo_get_option_value(Oid foreignTableId, const char *optionName) { - ForeignTable *foreignTable = NULL; - ForeignServer *foreignServer = NULL; - List *optionList = NIL; - ListCell *optionCell = NULL; - UserMapping *mapping= NULL; - char *optionValue = NULL; + ForeignTable *foreignTable = NULL; + ForeignServer *foreignServer = NULL; + List *optionList = NIL; + ListCell *optionCell = NULL; + UserMapping *mapping = NULL; + char *optionValue = NULL; foreignTable = GetForeignTable(foreignTableId); foreignServer = GetForeignServer(foreignTable->serverid); @@ -256,8 +259,8 @@ mongo_get_option_value(Oid foreignTableId, const char *optionName) foreach(optionCell, optionList) { - DefElem *optionDef = (DefElem *) lfirst(optionCell); - char *optionDefName = optionDef->defname; + DefElem *optionDef = (DefElem *) lfirst(optionCell); + char *optionDefName = optionDef->defname; if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) { From b429cb678c677ced6970dcde3f09ed4897c4f1c7 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 23 Jun 2020 10:43:23 +0530 Subject: [PATCH 128/239] Do some code cleanup and refactoring. It includes - renaming few function names, and variables to match with the postgres_fdw naming conventions. - rearranging code. - removing unnecessary initializations. - removing unnecessary header files. - avoiding unnecessary variables and minimizing their scope. - auditing palloc0() calls, preferring to palloc() whenever possible. - comments, and error message improvements. - indentation, and white-spacing adjustments. FDW-83, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. --- connection.c | 50 +- mongo_fdw.c | 1334 ++++++++++++++++++++---------------------- mongo_fdw.h | 271 +++++---- mongo_query.c | 426 +++++++------- mongo_query.h | 10 +- mongo_wrapper.c | 143 ++--- mongo_wrapper.h | 32 +- mongo_wrapper_meta.c | 346 ++++++----- option.c | 242 ++++---- 9 files changed, 1388 insertions(+), 1466 deletions(-) diff --git a/connection.c b/connection.c index d1fe74a..1e20ca9 100644 --- a/connection.c +++ b/connection.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * connection.c - * Foreign-data wrapper for remote MongoDB servers + * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,23 +12,14 @@ * *------------------------------------------------------------------------- */ -#include - - #include "postgres.h" -#include "mongo_wrapper.h" -#include "mongo_fdw.h" #include "access/xact.h" #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" #endif -#include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "utils/hsearch.h" +#include "mongo_wrapper.h" #include "utils/inval.h" -#include "utils/memutils.h" -#include "utils/resowner.h" #include "utils/syscache.h" /* Length of host */ @@ -38,7 +29,7 @@ * 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, + * 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 @@ -64,13 +55,14 @@ static HTAB *ConnectionHash = NULL; static void mongo_inval_callback(Datum arg, int cacheid, uint32 hashvalue); /* - * mongo_get_connection: - * Get a mong 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_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) +mongo_get_connection(ForeignServer *server, UserMapping *user, + MongoFdwOptions *opt) { bool found; ConnCacheEntry *entry; @@ -85,7 +77,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * ctl.keysize = sizeof(ConnCacheKey); ctl.entrysize = sizeof(ConnCacheEntry); ctl.hash = tag_hash; - /* allocate ConnectionHash in the cache context */ + /* Allocate ConnectionHash in the cache context */ ctl.hcxt = CacheMemoryContext; ConnectionHash = hash_create("mongo_fdw connections", 8, &ctl, @@ -111,7 +103,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * entry = hash_search(ConnectionHash, &key, HASH_ENTER, &found); if (!found) { - /* initialize new hashtable entry (key is already filled in) */ + /* Initialize new hashtable entry (key is already filled in) */ entry->conn = NULL; } @@ -130,13 +122,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * Oid umoid; #endif -#ifdef META_DRIVER - entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password, - opt->authenticationDatabase, opt->replicaSet, opt->readPreference, - opt->ssl, opt->pem_file, opt->pem_pwd, opt->ca_file, opt->ca_dir, opt->crl_file, opt->weak_cert_validation); -#else - entry->conn = MongoConnect(opt->svr_address, opt->svr_port, opt->svr_database, opt->svr_username, opt->svr_password); -#endif + entry->conn = MongoConnect(opt); elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", entry->conn, opt->svr_address, opt->svr_port); @@ -174,8 +160,8 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, MongoFdwOptions * } /* - * mongo_cleanup_connection: - * Delete all the cache entries on backend exists. + * mongo_cleanup_connection + * Delete all the cache entries on backend exits. */ void mongo_cleanup_connection() @@ -199,19 +185,21 @@ mongo_cleanup_connection() } /* - * Release connection created by calling mongo_get_connection. + * mongo_release_connection + * Release connection created by calling mongo_get_connection. */ void mongo_release_connection(MONGO_CONN *conn) { /* - * We don't close the connection indvisually here, will do all connection + * We don't close the connection individually here, will do all connection * cleanup on the backend exit. */ } /* - * Connection invalidation callback function for mongo. + * 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 diff --git a/mongo_fdw.c b/mongo_fdw.c index 763d11b..17bc98a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -12,77 +12,43 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" -#include "bson.h" #include "mongo_wrapper.h" -#include "mongo_fdw.h" -#include "mongo_query.h" -#include "access/reloptions.h" +#if PG_VERSION_NUM >= 90300 +#include "access/htup_details.h" +#endif +#if PG_VERSION_NUM < 120000 +#include "access/sysattr.h" +#endif #if PG_VERSION_NUM >= 120000 #include "access/table.h" #endif #include "catalog/pg_type.h" -#include "commands/defrem.h" -#include "commands/explain.h" -#include "commands/vacuum.h" #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" #include "common/jsonapi.h" #endif - -#include "foreign/fdwapi.h" -#include "foreign/foreign.h" -#include "nodes/makefuncs.h" -#include "optimizer/cost.h" +#include "miscadmin.h" +#include "mongo_fdw.h" +#include "mongo_query.h" #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif -#include "optimizer/pathnode.h" -#include "optimizer/plancat.h" -#include "optimizer/planmain.h" -#include "optimizer/restrictinfo.h" -#include "storage/ipc.h" -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/date.h" -#include "utils/hsearch.h" -#include "utils/lsyscache.h" -#include "utils/rel.h" -#include "utils/memutils.h" -#include "access/sysattr.h" -#include "commands/defrem.h" -#include "commands/explain.h" -#include "commands/vacuum.h" -#include "foreign/fdwapi.h" -#include "funcapi.h" -#include "miscadmin.h" -#include "nodes/makefuncs.h" -#include "nodes/nodeFuncs.h" -#include "optimizer/cost.h" -#include "optimizer/pathnode.h" -#include "optimizer/paths.h" -#include "optimizer/planmain.h" -#include "optimizer/prep.h" -#include "optimizer/restrictinfo.h" #if PG_VERSION_NUM < 120000 #include "optimizer/var.h" #endif #include "parser/parsetree.h" -#include "utils/builtins.h" -#include "utils/guc.h" -#include "utils/lsyscache.h" -#include "utils/memutils.h" +#include "storage/ipc.h" +#include "utils/jsonb.h" #if PG_VERSION_NUM < 130000 #include "utils/jsonapi.h" #else #include "utils/jsonfuncs.h" #endif -#include "utils/jsonb.h" -#if PG_VERSION_NUM >= 90300 -#include "access/htup_details.h" -#endif + +/* Declarations for dynamic loading */ +PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, @@ -90,28 +56,32 @@ */ #define CODE_VERSION 50201 +extern PGDLLEXPORT void _PG_init(void); +const char *EscapeJsonString(const char *string); +void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); -/* Local functions forward declarations */ -static void MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); -static void MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, - Oid foreignTableId); +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 *baserel, + RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *targetlist, List *restrictionClauses, Plan *outer_plan); - -static void MongoExplainForeignScan(ForeignScanState *scanState, - ExplainState *explainState); -static void MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags); -static TupleTableSlot *MongoIterateForeignScan(ForeignScanState *scanState); -static void MongoEndForeignScan(ForeignScanState *scanState); -static void MongoReScanForeignScan(ForeignScanState *scanState); - +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, @@ -122,58 +92,56 @@ static TupleTableSlot *MongoExecForeignDelete(EState *estate, TupleTableSlot *planSlot); static void MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo); - static void MongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation); - 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 - MongoExplainForeignModify(ModifyTableState *mtstate, - ResultRelInfo *rinfo, List *fdw_private, - int subplan_index, ExplainState *es); - -/* local functions */ +/* + * Helper functions + */ static double ForeignTableDocumentCount(Oid foreignTableId); static HTAB *ColumnMappingHash(Oid foreignTableId, List *columnList); -static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, +static void FillTupleSlot(const BSON *bsonDocument, + const char *bsonDocumentKey, + HTAB *columnMappingHash, + Datum *columnValues, bool *columnNulls); static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); -static Datum ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, +static Datum ColumnValue(BSON_ITERATOR *bsonIterator, + Oid columnTypeId, int32 columnTypeMod); static void MongoFreeScanState(MongoFdwModifyState *fmstate); -static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount); -static int MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount); +static int MongoAcquireSampleRows(Relation relation, + int errorLevel, + HeapTuple *sampleRows, + int targetRowCount, + double *totalRowCount, + double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); -extern PGDLLEXPORT void _PG_init(void); - -const char *EscapeJsonString(const char *string); - -void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); - -/* the null action object used for pure validation */ +/* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 static JsonSemAction nullSemAction = { @@ -188,12 +156,6 @@ JsonSemAction nullSemAction = }; #endif -/* declarations for dynamic loading */ -PG_MODULE_MAGIC; - -PG_FUNCTION_INFO_V1(mongo_fdw_handler); -PG_FUNCTION_INFO_V1(mongo_fdw_version); - /* * Library load-time initalization, sets on_proc_exit() callback for * backend shutdown. @@ -205,14 +167,16 @@ _PG_init(void) } /* - * mongo_fdw_handler creates and returns a struct with pointers to foreign table - * callback functions. + * 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; @@ -221,27 +185,28 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->ReScanForeignScan = MongoReScanForeignScan; fdwRoutine->EndForeignScan = MongoEndForeignScan; - /* support for insert / update / delete */ - fdwRoutine->ExecForeignInsert = MongoExecForeignInsert; - fdwRoutine->BeginForeignModify = MongoBeginForeignModify; - fdwRoutine->PlanForeignModify = MongoPlanForeignModify; + /* 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 */ + /* Support for EXPLAIN */ fdwRoutine->ExplainForeignScan = MongoExplainForeignScan; fdwRoutine->ExplainForeignModify = MongoExplainForeignModify; - /* support for ANALYSE */ + /* Support for ANALYZE */ fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; PG_RETURN_POINTER(fdwRoutine); } /* - * Exit callback function. + * mongo_fdw_exit + * Exit callback function. */ static void mongo_fdw_exit(int code, Datum arg) @@ -249,67 +214,70 @@ mongo_fdw_exit(int code, Datum arg) mongo_cleanup_connection(); } - /* - * MongoGetForeignRelSize obtains relation size estimates for mongo foreign table. + * MongoGetForeignRelSize + * Obtains relation size estimates for mongo foreign table. */ static void -MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) +MongoGetForeignRelSize(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid) { - double documentCount = ForeignTableDocumentCount(foreignTableId); + double documentCount = ForeignTableDocumentCount(foreigntableid); - if (documentCount >0.0) + 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 + * qualifiers are applied. This will be more accurate if analyze is * run on this relation. */ - List *rowClauseList = baserel->baserestrictinfo; - double rowSelectivity = clauselist_selectivity(root, rowClauseList, - 0, JOIN_INNER, NULL); - - double outputRowCount = clamp_row_est(documentCount *rowSelectivity); - - baserel->rows = outputRowCount; + 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"))); - } + ereport(DEBUG1, + (errmsg("could not retrieve document count for collection"), + errhint("Falling back to default estimates in planning."))); } - /* - * MongoGetForeignPaths creates the only scan path used to execute the query. + * 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 + * 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) +MongoGetForeignPaths(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid) { double tupleFilterCost = baserel->baserestrictcost.per_tuple; - double inputRowCount = 0.0; - double documentSelectivity = 0.0; - double foreignTableSize = 0; - int32 documentWidth = 0; - BlockNumber pageCount = 0; - double totalDiskAccessCost = 0.0; - double cpuCostPerDoc = 0.0; - double cpuCostPerRow = 0.0; - double totalCpuCost = 0.0; - double connectionCost = 0.0; - double documentCount = 0.0; - List *opExpressionList = NIL; + double inputRowCount; + double documentSelectivity; + double foreignTableSize; + int32 documentWidth; + BlockNumber pageCount; + double totalDiskAccessCost; + double cpuCostPerDoc; + double cpuCostPerRow; + double totalCpuCost; + double connectionCost; + double documentCount; + List *opExpressionList; Cost startupCost = 0.0; Cost totalCost = 0.0; - Path *foreignPath = NULL; + Path *foreignPath; - documentCount = ForeignTableDocumentCount(foreignTableId); + documentCount = ForeignTableDocumentCount(foreigntableid); - if (documentCount >0.0) + if (documentCount > 0.0) { /* * We estimate the number of rows returned after restriction @@ -318,16 +286,17 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) opExpressionList = ApplicableOpExpressionList(baserel); documentSelectivity = clauselist_selectivity(root, opExpressionList, 0, JOIN_INNER, NULL); - inputRowCount = clamp_row_est(documentCount *documentSelectivity); + 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, + * 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; + documentWidth = get_relation_data_width(foreigntableid, + baserel->attr_widths); + foreignTableSize = documentCount * documentWidth; pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ); totalDiskAccessCost = seq_page_cost * pageCount; @@ -345,13 +314,11 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) totalCost = startupCost + totalDiskAccessCost + totalCpuCost; } else - { - ereport(DEBUG1, (errmsg("could not retrieve document count for collection"), - errhint("Falling back to default estimates in planning"))); - } - - /* create a foreign path node */ + ereport(DEBUG1, + (errmsg("could not retrieve document count for collection"), + errhint("Falling back to default estimates in planning."))); + /* Create a foreign path node */ foreignPath = (Path *) create_foreignscan_path(root, baserel, #if PG_VERSION_NUM >= 90600 NULL, /* default pathtarget */ @@ -366,37 +333,37 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreignTableId) #endif NULL); /* no fdw_private data */ - /* add foreign path as the only possible path */ + /* Add foreign path as the only possible path */ add_path(baserel, foreignPath); } - /* - * MongoGetForeignPlan creates a foreign scan plan node for scanning the MongoDB - * collection. Note that MongoDB may decide to use an underlying index for this + * 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 *baserel, + RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, List *targetList, List *restrictionClauses, Plan *outer_plan) - { - Index scanRangeTableIndex = baserel->relid; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - List *opExpressionList = NIL; - BSON *queryDocument = NULL; - List *columnList = NIL; + Index scanRangeTableIndex = foreignrel->relid; + ForeignScan *foreignScan; + List *foreignPrivateList; + List *opExpressionList; + BSON *queryDocument; + List *columnList; /* * We push down applicable restriction clauses to MongoDB, but for * simplicity we currently put all the restrictionClauses into the plan - * node's qual list for the executor to re-check. So all we have to do + * node's qual list for the executor to re-check. So all we have to do * here is strip RestrictInfo nodes from the clauses and ignore * pseudoconstants (which will be handled elsewhere). */ @@ -409,22 +376,22 @@ MongoGetForeignPlan(PlannerInfo *root, * performance on the MongoDB server-side, so we instead filter out * columns on our side. */ - opExpressionList = ApplicableOpExpressionList(baserel); + opExpressionList = ApplicableOpExpressionList(foreignrel); queryDocument = QueryDocument(foreigntableid, opExpressionList, NULL); - /* we don't need to serialize column list as lists are copiable */ - columnList = ColumnList(baserel); + /* We don't need to serialize column list as lists are copiable */ + columnList = ColumnList(foreignrel); - /* construct foreign plan with query document and column list */ + /* Construct foreign plan with query document and column list */ foreignPrivateList = list_make2(columnList, opExpressionList); - /* only clean up the query struct */ + /* Only clean up the query struct */ BsonDestroy(queryDocument); - /* create the foreign scan node */ + /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, - NIL, /* no expressions to evaluate */ + NIL, /* No expressions to evaluate */ foreignPrivateList #if PG_VERSION_NUM >= 90500 ,NIL @@ -436,28 +403,28 @@ MongoGetForeignPlan(PlannerInfo *root, return foreignScan; } - /* - * MongoExplainForeignScan produces extra output for the Explain command. + * MongoExplainForeignScan + * Produces extra output for the Explain command. */ static void -MongoExplainForeignScan(ForeignScanState *scanState, ExplainState *explainState) +MongoExplainForeignScan(ForeignScanState *node, ExplainState *es) { - MongoFdwOptions *options = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *options; + StringInfo namespaceName; + Oid foreignTableId; - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); options = mongo_get_options(foreignTableId); - /* construct fully qualified collection name */ + /* 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, explainState); + ExplainPropertyText("Foreign Namespace", namespaceName->data, es); } static void @@ -467,14 +434,14 @@ MongoExplainForeignModify(ModifyTableState *mtstate, int subplan_index, ExplainState *es) { - MongoFdwOptions *options = NULL; - StringInfo namespaceName = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *options; + StringInfo namespaceName; + Oid foreignTableId; foreignTableId = RelationGetRelid(rinfo->ri_RelationDesc); options = mongo_get_options(foreignTableId); - /* construct fully qualified collection name */ + /* Construct fully qualified collection name */ namespaceName = makeStringInfo(); appendStringInfo(namespaceName, "%s.%s", options->svr_database, options->collectionName); @@ -483,41 +450,42 @@ MongoExplainForeignModify(ModifyTableState *mtstate, 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. + * 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 *scanState, int executorFlags) +MongoBeginForeignScan(ForeignScanState *node, int eflags) { - MONGO_CONN *mongoConnection = NULL; - MONGO_CURSOR *mongoCursor = NULL; - Oid foreignTableId = InvalidOid; - List *columnList = NIL; - HTAB *columnMappingHash = NULL; - ForeignScan *foreignScan = NULL; - List *foreignPrivateList = NIL; - BSON *queryDocument = NULL; - MongoFdwOptions *options = NULL; - MongoFdwModifyState *fmstate = NULL; - List *opExpressionList = NIL; + MONGO_CONN *mongoConnection; + MONGO_CURSOR *mongoCursor; + Oid foreignTableId; + List *columnList; + HTAB *columnMappingHash; + ForeignScan *foreignScan; + List *foreignPrivateList; + BSON *queryDocument; + MongoFdwOptions *options; + MongoFdwModifyState *fmstate; + List *opExpressionList; RangeTblEntry *rte; - EState *estate = scanState->ss.ps.state; - ForeignScan *fsplan = (ForeignScan *) scanState->ss.ps.plan; + EState *estate = node->ss.ps.state; + ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan; Oid userid; ForeignServer *server; UserMapping *user; ForeignTable *table; - - /* if Explain with no Analyze, do nothing */ - if (executorFlags & EXEC_FLAG_EXPLAIN_ONLY) + /* If Explain with no Analyze, do nothing */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) return; - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); options = mongo_get_options(foreignTableId); fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); @@ -530,52 +498,53 @@ MongoBeginForeignScan(ForeignScanState *scanState, int executorFlags) userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ - fmstate->rel = scanState->ss.ss_currentRelation; + fmstate->rel = node->ss.ss_currentRelation; table = GetForeignTable(RelationGetRelid(fmstate->rel)); server = GetForeignServer(table->serverid); user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will establish + * Get connection to the foreign server. Connection manager will establish * new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); - foreignScan = (ForeignScan *) scanState->ss.ps.plan; + foreignScan = (ForeignScan *) node->ss.ps.plan; foreignPrivateList = foreignScan->fdw_private; Assert(list_length(foreignPrivateList) == 2); columnList = list_nth(foreignPrivateList, 0); opExpressionList = list_nth(foreignPrivateList, 1); - queryDocument = QueryDocument(foreignTableId, opExpressionList, scanState); + queryDocument = QueryDocument(foreignTableId, opExpressionList, node); columnMappingHash = ColumnMappingHash(foreignTableId, columnList); - /* create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); + /* Create cursor for collection name and set query */ + mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, + options->collectionName, queryDocument); - /* create and set foreign execution state */ + /* Create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; fmstate->mongoConnection = mongoConnection; fmstate->mongoCursor = mongoCursor; fmstate->queryDocument = queryDocument; fmstate->options = options; - scanState->fdw_state = (void *) fmstate; + node->fdw_state = (void *) fmstate; } - /* - * MongoIterateForeignScan reads the next document from MongoDB, converts it to - * a PostgreSQL tuple, and stores the converted tuple into the ScanTupleSlot as - * a virtual tuple. + * MongoIterateForeignScan + * 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 *scanState) +MongoIterateForeignScan(ForeignScanState *node) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; - TupleTableSlot *tupleSlot = scanState->ss.ss_ScanTupleSlot; + 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; @@ -586,39 +555,41 @@ MongoIterateForeignScan(ForeignScanState *scanState) /* * 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. + * 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 */ + /* 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 */ + const char *bsonDocumentKey = NULL; /* Top level document */ - FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + FillTupleSlot(bsonDocument, bsonDocumentKey, columnMappingHash, + columnValues, columnNulls); ExecStoreVirtualTuple(tupleSlot); } + return tupleSlot; } - /* - * MongoEndForeignScan finishes scanning the foreign table, closes the cursor - * and the connection to MongoDB, and reclaims scan related resources. + * MongoEndForeignScan + * Finishes scanning the foreign table, closes the cursor and the + * connection to MongoDB, and reclaims scan related resources. */ static void -MongoEndForeignScan(ForeignScanState *scanState) +MongoEndForeignScan(ForeignScanState *node) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + MongoFdwModifyState *fmstate; - /* if we executed a query, reclaim mongo related resources */ + fmstate = (MongoFdwModifyState *) node->fdw_state; + /* If we executed a query, reclaim mongo related resources */ if (fmstate != NULL) { if (fmstate->options) @@ -630,28 +601,30 @@ MongoEndForeignScan(ForeignScanState *scanState) } } - /* - * 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. + * 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 *scanState) +MongoReScanForeignScan(ForeignScanState *node) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanState->fdw_state; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state; MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *options = NULL; - Oid foreignTableId = InvalidOid; + MongoFdwOptions *options; + Oid foreignTableId; - /* close down the old cursor */ + /* Close down the old cursor */ MongoCursorDestroy(fmstate->mongoCursor); - /* reconstruct full collection name */ - foreignTableId = RelationGetRelid(scanState->ss.ss_currentRelation); + /* Reconstruct full collection name */ + foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); options = mongo_get_options(foreignTableId); - /* reconstruct cursor for collection name and set query */ + /* Reconstruct cursor for collection name and set query */ fmstate->mongoCursor = MongoCursorCreate(mongoConnection, fmstate->options->svr_database, fmstate->options->collectionName, @@ -674,7 +647,7 @@ MongoPlanForeignModify(PlannerInfo *root, * Core code already has some lock on each rel being planned, so we can * use NoLock here. */ -#if PG_VERSION_NUM < 120000 +#if PG_VERSION_NUM < 130000 rel = heap_open(rte->relid, NoLock); #else rel = table_open(rte->relid, NoLock); @@ -708,14 +681,14 @@ MongoPlanForeignModify(PlannerInfo *root, while ((col = bms_first_member(tmpset)) >= 0) { col += FirstLowInvalidHeapAttributeNumber; - if (col <= InvalidAttrNumber) /* shouldn't happen */ + 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 */ + if (col == 1) /* Shouldn't happen */ elog(ERROR, "row identifier column update is not supported"); targetAttrs = lappend_int(targetAttrs, col); @@ -724,9 +697,7 @@ MongoPlanForeignModify(PlannerInfo *root, targetAttrs = lcons_int(1, targetAttrs); } else - { targetAttrs = lcons_int(1, targetAttrs); - } /* * RETURNING list not supported @@ -743,9 +714,9 @@ MongoPlanForeignModify(PlannerInfo *root, return list_make1(targetAttrs); } - /* - * Begin an insert/update/delete operation on a foreign table + * MongoBeginForeignModify + * Begin an insert/update/delete operation on a foreign table. */ static void MongoBeginForeignModify(ModifyTableState *mtstate, @@ -754,16 +725,16 @@ MongoBeginForeignModify(ModifyTableState *mtstate, int subplan_index, int eflags) { - MongoFdwModifyState *fmstate = NULL; + MongoFdwModifyState *fmstate; Relation rel = resultRelInfo->ri_RelationDesc; - AttrNumber n_params = 0; + AttrNumber n_params; Oid typefnoid = InvalidOid; bool isvarlena = false; - ListCell *lc = NULL; - Oid foreignTableId = InvalidOid; + ListCell *lc; + Oid foreignTableId; /* - * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState + * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState * stays NULL. */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) @@ -780,7 +751,7 @@ MongoBeginForeignModify(ModifyTableState *mtstate, fmstate->target_attrs = (List *) list_nth(fdw_private, 0); n_params = list_length(fmstate->target_attrs) + 1; - fmstate->p_flinfo = (FmgrInfo *) palloc0(sizeof(FmgrInfo) * n_params); + fmstate->p_flinfo = (FmgrInfo *) palloc(sizeof(FmgrInfo) * n_params); fmstate->p_nums = 0; /* Set up for remaining transmittable parameters */ @@ -790,7 +761,8 @@ MongoBeginForeignModify(ModifyTableState *mtstate, #if PG_VERSION_NUM < 110000 Form_pg_attribute attr = RelationGetDescr(rel)->attrs[attnum - 1]; #else - Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1); + Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(rel), + attnum - 1); #endif Assert(!attr->attisdropped); @@ -804,9 +776,9 @@ MongoBeginForeignModify(ModifyTableState *mtstate, resultRelInfo->ri_FdwState = fmstate; } - /* - * Insert one row into a foreign table. + * MongoExecForeignInsert + * Insert one row into a foreign table. */ static TupleTableSlot * MongoExecForeignInsert(EState *estate, @@ -814,10 +786,10 @@ MongoExecForeignInsert(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; + MongoFdwOptions *options; + MONGO_CONN *mongoConnection; Oid foreignTableId = InvalidOid; - BSON *b = NULL; + BSON *bsonDoc; Oid typoid; Datum value; bool isnull = false; @@ -825,9 +797,9 @@ MongoExecForeignInsert(EState *estate, ForeignServer *server; UserMapping *user; ForeignTable *table; + MongoFdwModifyState *fmstate; - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - + fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); userid = GetUserId(); @@ -838,17 +810,17 @@ MongoExecForeignInsert(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will establish - * new connection if necessary. + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. */ options = fmstate->options; mongoConnection = mongo_get_connection(server, user, options); - b = BsonCreate(); + bsonDoc = BsonCreate(); typoid = get_atttype(foreignTableId, 1); - /* get following parameters from slot */ + /* Get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) { ListCell *lc; @@ -859,7 +831,7 @@ MongoExecForeignInsert(EState *estate, value = slot_getattr(slot, attnum, &isnull); - /* first column of MongoDB's foreign table must be _id */ + /* First column of MongoDB's foreign table must be _id */ #if PG_VERSION_NUM < 110000 if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) #else @@ -876,50 +848,54 @@ MongoExecForeignInsert(EState *estate, #endif 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) - { - /* - * Ignore the value of first column which is row identifier in - * MongoDb (_id) and let MongoDB to insert the unique value - * for that column. - */ - } - else - { + continue; + #if PG_VERSION_NUM < 110000 - AppenMongoValue(b, slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, value, - isnull, slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + AppendMongoValue(bsonDoc, + slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, + value, + isnull, + slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); #else - AppenMongoValue(b, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, value, - isnull, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); + AppendMongoValue(bsonDoc, + TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, + value, + isnull, + TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); #endif - } } } - BsonFinish(b); + BsonFinish(bsonDoc); - /* Now we are ready to insert tuple / document into MongoDB */ - MongoInsert(mongoConnection, options->svr_database, options->collectionName, b); + /* Now we are ready to insert tuple/document into MongoDB */ + MongoInsert(mongoConnection, options->svr_database, + options->collectionName, bsonDoc); - BsonDestroy(b); + BsonDestroy(bsonDoc); return slot; } - /* - * 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. + * 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. */ static void MongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) { - Var *var = NULL; - const char *attrname = NULL; - TargetEntry *tle = NULL; + Var *var; + const char *attrname; + TargetEntry *tle; /* * What we need is the rowid which is the first column @@ -927,7 +903,8 @@ MongoAddForeignUpdateTargets(Query *parsetree, #if PG_VERSION_NUM < 110000 Form_pg_attribute attr = RelationGetDescr(target_relation)->attrs[0]; #else - Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(target_relation), 0); + Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(target_relation), + 0); #endif /* Make a Var representing the desired value */ @@ -946,38 +923,36 @@ MongoAddForeignUpdateTargets(Query *parsetree, pstrdup(attrname), true); - /* ... and add it to the query's targetlist */ + /* ... And add it to the query's targetlist */ parsetree->targetList = lappend(parsetree->targetList, tle); } - static TupleTableSlot * MongoExecForeignUpdate(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; + MongoFdwOptions *options; + MONGO_CONN *mongoConnection; + Datum datum; bool isNull = false; - Oid foreignTableId = InvalidOid; - char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; + Oid foreignTableId; + char *columnName; + Oid typoid; + BSON *document; BSON *op = NULL; BSON set; Oid userid = GetUserId(); ForeignServer *server; UserMapping *user; ForeignTable *table; + MongoFdwModifyState *fmstate; - - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - + fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ + /* Resolve foreign table options; and connect to mongo server */ options = fmstate->options; /* Get info about foreign table. */ @@ -986,8 +961,8 @@ MongoExecForeignUpdate(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will establish - * new connection if necessary. + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1002,10 +977,10 @@ MongoExecForeignUpdate(EState *estate, typoid = get_atttype(foreignTableId, 1); - b = BsonCreate(); - BsonAppendStartObject(b, "$set", &set); + document = BsonCreate(); + BsonAppendStartObject(document, "$set", &set); - /* get following parameters from slot */ + /* Get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) { ListCell *lc; @@ -1016,7 +991,8 @@ MongoExecForeignUpdate(EState *estate, #if PG_VERSION_NUM < 110000 Form_pg_attribute attr = slot->tts_tupleDescriptor->attrs[attnum - 1]; #else - Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1); + Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, + attnum - 1); #endif Datum value; bool isnull; @@ -1029,30 +1005,31 @@ MongoExecForeignUpdate(EState *estate, value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER - AppenMongoValue(&set, attr->attname.data, value, - isnull ? true : false, attr->atttypid); + AppendMongoValue(&set, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #else - AppenMongoValue(b, attr->attname.data, value, - isnull ? true : false, attr->atttypid); + AppendMongoValue(document, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #endif } } - BsonAppendFinishObject(b, &set); - BsonFinish(b); + BsonAppendFinishObject(document, &set); + BsonFinish(document); op = BsonCreate(); - if (!AppenMongoValue(op, columnName, datum, false, typoid)) + if (!AppendMongoValue(op, columnName, datum, false, typoid)) { - BsonDestroy(b); + BsonDestroy(document); return NULL; } BsonFinish(op); /* We are ready to update the row into MongoDB */ - MongoUpdate(mongoConnection, options->svr_database, options->collectionName, op, b); + MongoUpdate(mongoConnection, options->svr_database, + options->collectionName, op, document); BsonDestroy(op); - BsonDestroy(b); + BsonDestroy(document); /* Return NULL if nothing was updated on the remote end */ return slot; @@ -1068,25 +1045,25 @@ MongoExecForeignDelete(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; - Datum datum = 0; + MongoFdwOptions *options; + MONGO_CONN *mongoConnection; + Datum datum; bool isNull = false; - Oid foreignTableId = InvalidOid; + Oid foreignTableId; char *columnName = NULL; - Oid typoid = InvalidOid; - BSON *b = NULL; + Oid typoid; + BSON *document; Oid userid = GetUserId(); ForeignServer *server; UserMapping *user; ForeignTable *table; + MongoFdwModifyState *fmstate; - - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* resolve foreign table options; and connect to mongo server */ + /* Resolve foreign table options; and connect to mongo server */ options = fmstate->options; /* Get info about foreign table. */ @@ -1095,8 +1072,8 @@ MongoExecForeignDelete(EState *estate, user = GetUserMapping(userid, server->serverid); /* - * Get connection to the foreign server. Connection manager will establish - * new connection if necessary. + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1111,18 +1088,19 @@ MongoExecForeignDelete(EState *estate, typoid = get_atttype(foreignTableId, 1); - b = BsonCreate(); - if (!AppenMongoValue(b, columnName, datum, false, typoid)) + document = BsonCreate(); + if (!AppendMongoValue(document, columnName, datum, false, typoid)) { - BsonDestroy(b); + BsonDestroy(document); return NULL; } - BsonFinish(b); + BsonFinish(document); /* Now we are ready to delete a single document from MongoDB */ - MongoDelete(mongoConnection, options->svr_database, options->collectionName, b); + MongoDelete(mongoConnection, options->svr_database, + options->collectionName, document); - BsonDestroy(b); + BsonDestroy(document); /* Return NULL if nothing was updated on the remote end */ return slot; @@ -1135,8 +1113,9 @@ MongoExecForeignDelete(EState *estate, static void MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; + MongoFdwModifyState *fmstate; + fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; if (fmstate) { if (fmstate->options) @@ -1150,59 +1129,60 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) } /* - * ForeignTableDocumentCount 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. + * ForeignTableDocumentCount + * 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 ForeignTableDocumentCount(Oid foreignTableId) { - MongoFdwOptions *options = NULL; - MONGO_CONN *mongoConnection = NULL; + MongoFdwOptions *options; + MONGO_CONN *mongoConnection; const BSON *emptyQuery = NULL; - double documentCount = 0.0; + 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 */ + /* 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. + * 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); + documentCount = MongoAggregateCount(mongoConnection, options->svr_database, + options->collectionName, emptyQuery); mongo_free_options(options); return documentCount; } - /* - * ColumnMappingHash 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. + * ColumnMappingHash + * 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 * ColumnMappingHash(Oid foreignTableId, List *columnList) { - ListCell *columnCell = NULL; + ListCell *columnCell; const long hashTableSize = 2048; - HTAB *columnMappingHash = NULL; + HTAB *columnMappingHash; - /* create hash table */ + /* Create hash table */ HASHCTL hashInfo; memset(&hashInfo, 0, sizeof(hashInfo)); @@ -1211,7 +1191,8 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) hashInfo.hash = string_hash; hashInfo.hcxt = CurrentMemoryContext; - columnMappingHash = hash_create("Column Mapping Hash", hashTableSize, &hashInfo, + columnMappingHash = hash_create("Column Mapping Hash", hashTableSize, + &hashInfo, (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT)); Assert(columnMappingHash != NULL); @@ -1219,11 +1200,10 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) { Var *column = (Var *) lfirst(columnCell); AttrNumber columnId = column->varattno; - - ColumnMapping *columnMapping = NULL; + ColumnMapping *columnMapping; char *columnName = NULL; bool handleFound = false; - void *hashKey = NULL; + void *hashKey; #if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, columnId); @@ -1232,8 +1212,10 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) #endif hashKey = (void *) columnName; - columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, - HASH_ENTER, &handleFound); + columnMapping = (ColumnMapping *) hash_search(columnMappingHash, + hashKey, + HASH_ENTER, + &handleFound); Assert(columnMapping != NULL); columnMapping->columnIndex = columnId - 1; @@ -1245,22 +1227,26 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) return columnMappingHash; } - /* - * FillTupleSlot 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. + * FillTupleSlot + * 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 -FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, - HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls) +FillTupleSlot(const BSON *bsonDocument, + const char *bsonDocumentKey, + HTAB *columnMappingHash, + Datum *columnValues, + bool *columnNulls) { - ColumnMapping *columnMapping = NULL; + ColumnMapping *columnMapping; bool handleFound = false; - void *hashKey = NULL; + void *hashKey; BSON_ITERATOR bsonIterator = {NULL, 0}; if (BsonIterInit(&bsonIterator, (BSON *) bsonDocument) == false) @@ -1270,11 +1256,12 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, HASH_FIND, &handleFound); - if (columnMapping != NULL && handleFound == true && columnValues[columnMapping->columnIndex] == 0) + if (columnMapping != NULL && handleFound == true && + columnValues[columnMapping->columnIndex] == 0) { - JsonLexContext *lex = NULL; - text *result = NULL; - Datum columnValue = 0; + JsonLexContext *lex; + text *result; + Datum columnValue; char *str; str = BsonAsJson(bsonDocument); @@ -1310,15 +1297,16 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, case BPCHAROID: columnValue = PointerGetDatum(result); break; - case JSONBOID: - columnValue = DirectFunctionCall1(jsonb_in, PointerGetDatum(str)); + columnValue = DirectFunctionCall1(jsonb_in, + PointerGetDatum(str)); break; - default: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("un-supported type for column __doc"), - errhint("Column type: %u", (uint32) columnMapping->columnTypeId))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("unsupported type for column __doc"), + errhint("Column type: %u", + (uint32) columnMapping->columnTypeId))); break; } @@ -1328,7 +1316,6 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, return; } - while (BsonIterNext(&bsonIterator)) { const char *bsonKey = BsonIterKey(&bsonIterator); @@ -1337,8 +1324,9 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, Oid columnArrayTypeId = InvalidOid; bool compatibleTypes = false; bool handleFound = false; - const char *bsonFullKey = NULL; - void *hashKey = NULL; + const char *bsonFullKey; + void *hashKey; + int32 columnIndex; columnMapping = NULL; if (bsonDocumentKey != NULL) @@ -1349,25 +1337,26 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, */ StringInfo bsonFullKeyString = makeStringInfo(); - appendStringInfo(bsonFullKeyString, "%s.%s", bsonDocumentKey, bsonKey); + appendStringInfo(bsonFullKeyString, "%s.%s", bsonDocumentKey, + bsonKey); bsonFullKey = bsonFullKeyString->data; } else - { bsonFullKey = bsonKey; - } - /* look up the corresponding column for this bson key */ + /* Look up the corresponding column for this bson key */ hashKey = (void *) bsonFullKey; - columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, - HASH_FIND, &handleFound); + columnMapping = (ColumnMapping *) hash_search(columnMappingHash, + hashKey, + HASH_FIND, + &handleFound); if (columnMapping != NULL) { columnTypeId = columnMapping->columnTypeId; columnArrayTypeId = columnMapping->columnArrayTypeId; } - /* recurse into nested objects */ + /* Recurse into nested objects */ if (bsonType == BSON_TYPE_DOCUMENT) { if (columnTypeId != JSONOID) @@ -1375,69 +1364,53 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, BSON subObject; BsonIterSubObject(&bsonIterator, &subObject); - FillTupleSlot(&subObject, bsonFullKey, - columnMappingHash, columnValues, columnNulls); + FillTupleSlot(&subObject, bsonFullKey, columnMappingHash, + columnValues, columnNulls); continue; } } - /* if no corresponding column or null BSON value, continue */ + /* If no corresponding column or null BSON value, continue */ if (columnMapping == NULL || bsonType == BSON_TYPE_NULL) - { continue; - } - - /* check if columns have compatible types */ + /* Check if columns have compatible types */ if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) - { compatibleTypes = true; - } else - { compatibleTypes = ColumnTypesCompatible(bsonType, columnTypeId); - } - - /* if types are incompatible, leave this column null */ + /* If types are incompatible, leave this column null */ if (!compatibleTypes) - { continue; - } - /* fill in corresponding column value and null flag */ + columnIndex = columnMapping->columnIndex; + /* Fill in corresponding column value and null flag */ if (OidIsValid(columnArrayTypeId)) - { - int32 columnIndex = columnMapping->columnIndex; - columnValues[columnIndex] = ColumnValueArray(&bsonIterator, columnArrayTypeId); - columnNulls[columnIndex] = false; - } else - { - int32 columnIndex = columnMapping->columnIndex; - Oid columnTypeMod = columnMapping->columnTypeMod; - columnValues[columnIndex] = ColumnValue(&bsonIterator, - columnTypeId, columnTypeMod); - columnNulls[columnIndex] = false; - } + columnTypeId, + columnMapping->columnTypeMod); + columnNulls[columnIndex] = false; } } - /* - * ColumnTypesCompatible 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. + * ColumnTypesCompatible + * 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 ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) { bool compatibleTypes = false; - /* we consider the PostgreSQL column type as authoritative */ + /* We consider the PostgreSQL column type as authoritative */ switch (columnTypeId) { case INT2OID: @@ -1446,123 +1419,92 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) case FLOAT4OID: case FLOAT8OID: case NUMERICOID: - { - if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || - bsonType == BSON_TYPE_DOUBLE) - { - compatibleTypes = true; - } - break; - } + if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || + bsonType == BSON_TYPE_DOUBLE) + 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; - } + 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; - } + if (bsonType == BSON_TYPE_UTF8) + compatibleTypes = true; + break; case BYTEAOID: - { - if (bsonType == BSON_TYPE_BINDATA) - { - compatibleTypes = true; - } + if (bsonType == BSON_TYPE_BINDATA) + compatibleTypes = true; #ifdef META_DRIVER - if (bsonType == BSON_TYPE_OID) - { - compatibleTypes = true; - } + if (bsonType == BSON_TYPE_OID) + compatibleTypes = true; #endif - break; - } + break; case NAMEOID: - { - /* - * 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. - */ - if (bsonType == BSON_TYPE_OID) - { - compatibleTypes = true; - } - break; - } + + /* + * 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. + */ + if (bsonType == BSON_TYPE_OID) + compatibleTypes = true; + break; case DATEOID: case TIMESTAMPOID: case TIMESTAMPTZOID: - { - if (bsonType == BSON_TYPE_DATE_TIME) - { - compatibleTypes = true; - } - break; - } + if (bsonType == BSON_TYPE_DATE_TIME) + compatibleTypes = true; + break; case NUMERICARRAY_OID: - { - if (bsonType == BSON_TYPE_ARRAY) - compatibleTypes = true; - break; - } + if (bsonType == BSON_TYPE_ARRAY) + compatibleTypes = true; + break; case JSONOID: - { - if (bsonType == BSON_TYPE_DOCUMENT || bsonType == BSON_TYPE_ARRAY) - { - compatibleTypes = true; - } - break; - } + 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; - } + /* + * 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; } - /* - * ColumnValueArray 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. + * ColumnValueArray + * 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 ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { - Datum *columnValueArray = palloc0(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); + Datum *columnValueArray = palloc(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; - uint32 arrayGrowthFactor = 2; uint32 arrayIndex = 0; - - ArrayType *columnValueObject = NULL; - Datum columnValueDatum = 0; - bool typeByValue = false; - char typeAlignment = 0; - int16 typeLength = 0; + ArrayType *columnValueObject; + Datum columnValueDatum; + bool typeByValue; + char typeAlignment; + int16 typeLength; BSON_ITERATOR bsonSubIterator = {NULL, 0}; @@ -1574,39 +1516,48 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); if (bsonType == BSON_TYPE_NULL || !compatibleTypes) - { continue; - } if (arrayIndex >= arrayCapacity) { - arrayCapacity *= arrayGrowthFactor; - columnValueArray = repalloc(columnValueArray, arrayCapacity * sizeof(Datum)); + /* Double the array capacity. */ + arrayCapacity *= 2; + columnValueArray = repalloc(columnValueArray, + arrayCapacity * sizeof(Datum)); } - /* use default type modifier (0) to convert column value */ - columnValueArray[arrayIndex] = ColumnValue(&bsonSubIterator, valueTypeId, 0); + /* Use default type modifier (0) to convert column value */ + columnValueArray[arrayIndex] = ColumnValue(&bsonSubIterator, + valueTypeId, 0); arrayIndex++; } - get_typlenbyvalalign(valueTypeId, &typeLength, &typeByValue, &typeAlignment); - columnValueObject = construct_array(columnValueArray, arrayIndex, valueTypeId, - typeLength, typeByValue, typeAlignment); + get_typlenbyvalalign(valueTypeId, &typeLength, &typeByValue, + &typeAlignment); + columnValueObject = construct_array(columnValueArray, + arrayIndex, + valueTypeId, + typeLength, + typeByValue, + typeAlignment); columnValueDatum = PointerGetDatum(columnValueObject); + + pfree(columnValueArray); + return columnValueDatum; } - /* - * ColumnValue 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. + * ColumnValue + * 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 ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { - Datum columnValue = 0; + Datum columnValue; switch (columnTypeId) { @@ -1615,52 +1566,52 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) int16 value = (int16) BsonIterInt32(bsonIterator); columnValue = Int16GetDatum(value); - break; } + break; case INT4OID: { int32 value = BsonIterInt32(bsonIterator); columnValue = Int32GetDatum(value); - break; } + break; case INT8OID: { int64 value = BsonIterInt64(bsonIterator); columnValue = Int64GetDatum(value); - break; } + break; case FLOAT4OID: { float4 value = (float4) BsonIterDouble(bsonIterator); columnValue = Float4GetDatum(value); - break; } + break; case FLOAT8OID: { float8 value = BsonIterDouble(bsonIterator); columnValue = Float8GetDatum(value); - break; } + break; case NUMERICOID: { float8 value = BsonIterDouble(bsonIterator); Datum valueDatum = Float8GetDatum(value); - /* overlook type modifiers for numeric */ + /* Overlook type modifiers for numeric */ columnValue = DirectFunctionCall1(float8_numeric, valueDatum); - break; } + break; case BOOLOID: { bool value = BsonIterBool(bsonIterator); columnValue = BoolGetDatum(value); - break; } + break; case BPCHAROID: { const char *value = BsonIterString(bsonIterator); @@ -1669,8 +1620,8 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = DirectFunctionCall3(bpcharin, valueDatum, ObjectIdGetDatum(InvalidOid), Int32GetDatum(columnTypeMod)); - break; } + break; case VARCHAROID: { const char *value = BsonIterString(bsonIterator); @@ -1679,15 +1630,15 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = DirectFunctionCall3(varcharin, valueDatum, ObjectIdGetDatum(InvalidOid), Int32GetDatum(columnTypeMod)); - break; } + break; case TEXTOID: { const char *value = BsonIterString(bsonIterator); columnValue = CStringGetTextDatum(value); - break; } + break; case NAMEOID: { char value[NAMEDATALEN]; @@ -1701,8 +1652,8 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) columnValue = DirectFunctionCall3(namein, valueDatum, ObjectIdGetDatum(InvalidOid), Int32GetDatum(columnTypeMod)); - break; } + break; case BYTEAOID: { int value_len; @@ -1716,7 +1667,8 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) value_len = 12; break; default: - value = (char *) BsonIterBinData(bsonIterator, (uint32_t *) &value_len); + value = (char *) BsonIterBinData(bsonIterator, + (uint32_t *) &value_len); break; } #else @@ -1727,27 +1679,28 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) memcpy(VARDATA(result), value, value_len); SET_VARSIZE(result, value_len + VARHDRSZ); columnValue = PointerGetDatum(result); - break; } + 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; + 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 */ + /* Overlook type modifiers for timestamp */ columnValue = TimestampGetDatum(timestamp); - break; } + break; case JSONOID: { JsonLexContext *lex; @@ -1757,28 +1710,30 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) BSON_TYPE type = BSON_ITER_TYPE(bsonIterator); if (type != BSON_TYPE_ARRAY && type != BSON_TYPE_DOCUMENT) - ereport(ERROR, (errmsg("cannot convert scolar to json"))); + ereport(ERROR, + (errmsg("cannot convert to json"))); #ifdef META_DRIVER /* Convert BSON to JSON value */ - BsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); + BsonToJsonStringValue(buffer, bsonIterator, + BSON_TYPE_ARRAY == type); #else /* Convert BSON to JSON value */ - BsonToJsonString(buffer, *bsonIterator, BSON_TYPE_ARRAY == type); + BsonToJsonString(buffer, *bsonIterator, + BSON_TYPE_ARRAY == type); #endif result = cstring_to_text_with_len(buffer->data, buffer->len); lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); columnValue = PointerGetDatum(result); - break; } + 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; - } + 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; @@ -1787,11 +1742,11 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) void BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) { - const char *key = NULL; + const char *key; bool isFirstElement; char beginSymbol = '{'; char endSymbol = '}'; - BSON_TYPE t; + BSON_TYPE bsonType; if (isArray) { @@ -1815,8 +1770,8 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) if (!isFirstElement) appendStringInfoChar(output, ','); - t = BsonIterType(&i); - if (t == 0) + bsonType = BsonIterType(&i); + if (bsonType == 0) break; key = BsonIterKey(&i); @@ -1824,7 +1779,7 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) if (!isArray) appendStringInfo(output, "\"%s\":", key); - switch (t) + switch (bsonType) { case BSON_TYPE_DOUBLE: appendStringInfo(output, "%f", BsonIterDouble(&i)); @@ -1834,10 +1789,10 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) EscapeJsonString(BsonIterString(&i))); break; case BSON_TYPE_SYMBOL: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("`symbol` BSON type is deprecated and " - "unsupported"), - errhint("Symbol: %s", BsonIterString(&i)))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("\"symbol\" BSON type is deprecated and unsupported"), + errhint("Symbol: %s", BsonIterString(&i)))); break; case BSON_TYPE_OID: { @@ -1848,8 +1803,8 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) break; } case BSON_TYPE_BOOL: - appendStringInfoString( - output, BsonIterBool(&i) ? "true" : "false"); + appendStringInfoString(output, + BsonIterBool(&i) ? "true" : "false"); break; case BSON_TYPE_DATE_TIME: appendStringInfo(output, "{\"$date\":%ld}", @@ -1857,34 +1812,34 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) break; case BSON_TYPE_BINDATA: /* It's possible to encode the data with base64 here. */ - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `binary data` BSON type " - "is not implemented"))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for \"binary data\" BSON type is not implemented"))); break; case BSON_TYPE_UNDEFINED: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("`undefined` BSON type is deprecated " - "and unsupported"))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("\"undefined\" BSON type is deprecated and unsupported"))); break; case BSON_TYPE_NULL: appendStringInfoString(output, "null"); break; case BSON_TYPE_REGEX: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `regex` BSON type is " - "not implemented"), - errhint("Regex: %s", BsonIterRegex(&i)))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for \"regex\" BSON type is not implemented"), + errhint("Regex: %s", BsonIterRegex(&i)))); break; case BSON_TYPE_CODE: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `code` BSON type is " - "not implemented"), - errhint("Code: %s", BsonIterCode(&i)))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for \"code\" BSON type is not implemented"), + errhint("Code: %s", BsonIterCode(&i)))); break; case BSON_TYPE_CODEWSCOPE: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for `code with scope` BSON " - "type is not implemented"))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("support for \"code\" with scope` BSON type is not implemented"))); break; case BSON_TYPE_INT32: appendStringInfo(output, "%d", BsonIterInt32(&i)); @@ -1893,9 +1848,9 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) appendStringInfo(output, "%lu", (uint64_t) BsonIterInt64(&i)); break; case BSON_TYPE_TIMESTAMP: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("internal `timestamp` BSON type is " - "and unsupported"))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("internal `timestamp` BSON type is and unsupported"))); break; case BSON_TYPE_DOCUMENT: BsonToJsonString(output, i, false); @@ -1904,8 +1859,9 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) BsonToJsonString(output, i, true); break; default: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("unsupported BSON type: %x", t))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("unsupported BSON type: %x", bsonType))); } isFirstElement = false; } @@ -1913,25 +1869,26 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) } /* - * EscapeJsonString escapes a string for safe inclusion in JSON. + * EscapeJsonString + * Escapes a string for safe inclusion in JSON. */ const char * EscapeJsonString(const char *string) { StringInfo buffer; const char *ptr; - int i, - segmentStartIdx, - len; - + int i; + int segmentStartIdx; + int len; bool needsEscaping = false; for (ptr = string; *ptr; ++ptr) { - if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t' \ - ||*ptr == '\\') + if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t' || + *ptr == '\\') { needsEscaping = true; + break; } } @@ -1943,14 +1900,12 @@ EscapeJsonString(const char *string) segmentStartIdx = 0; for (i = 0; i < len; ++i) { - if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' - || string[i] == '\t' || string[i] == '\\') + if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' || + string[i] == '\t' || string[i] == '\\') { if (segmentStartIdx < i) - { appendBinaryStringInfo(buffer, string + segmentStartIdx, i - segmentStartIdx); - } appendStringInfoChar(buffer, '\\'); if (string[i] == '"') @@ -1968,17 +1923,15 @@ EscapeJsonString(const char *string) } } if (segmentStartIdx < len) - { appendBinaryStringInfo(buffer, string + segmentStartIdx, len - segmentStartIdx); - } return buffer->data; } - /* - * MongoFreeScanState closes the cursor and connection to MongoDB, and reclaims - * all Mongo related resources allocated for the foreign scan. + * MongoFreeScanState + * Closes the cursor and connection to MongoDB, and reclaims all Mongo + * related resources allocated for the foreign scan. */ static void MongoFreeScanState(MongoFdwModifyState *fmstate) @@ -2002,28 +1955,27 @@ MongoFreeScanState(MongoFdwModifyState *fmstate) mongo_release_connection(fmstate->mongoConnection); } - /* - * MongoAnalyzeForeignTable collects statistics for the given foreign table. + * MongoAnalyzeForeignTable + * Collects statistics for the given foreign table. */ static bool MongoAnalyzeForeignTable(Relation relation, - AcquireSampleRowsFunc *acquireSampleRowsFunc, - BlockNumber *totalPageCount) + AcquireSampleRowsFunc *func, + BlockNumber *totalpages) { BlockNumber pageCount = 0; - int attributeCount = 0; - int32 *attributeWidths = NULL; - Oid foreignTableId = InvalidOid; - int32 documentWidth = 0; - double documentCount = 0.0; - double foreignTableSize = 0; + int attributeCount; + int32 *attributeWidths; + Oid foreignTableId; + int32 documentWidth; + double documentCount; + double foreignTableSize; foreignTableId = RelationGetRelid(relation); - documentCount = ForeignTableDocumentCount(foreignTableId); - if (documentCount >0.0) + if (documentCount > 0.0) { attributeCount = RelationGetNumberOfAttributes(relation); attributeWidths = (int32 *) palloc0((attributeCount + 1) * sizeof(int32)); @@ -2031,70 +1983,74 @@ MongoAnalyzeForeignTable(Relation relation, /* * 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, + * 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; + documentWidth = get_relation_data_width(foreignTableId, + attributeWidths); + foreignTableSize = documentCount * documentWidth; pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ); } else - { - ereport(ERROR, (errmsg("could not retrieve document count for collection"), - errhint("could not collect statistics about foreign table"))); - } + ereport(DEBUG1, + (errmsg("could not retrieve document count for collection"), + errhint("Could not collect statistics about foreign table."))); - (*totalPageCount) = pageCount; - (*acquireSampleRowsFunc) = MongoAcquireSampleRows; + (*totalpages) = pageCount; + (*func) = MongoAcquireSampleRows; return true; } - /* - * MongoAcquireSampleRows 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. + * MongoAcquireSampleRows + * 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 + * 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 -MongoAcquireSampleRows(Relation relation, int errorLevel, - HeapTuple *sampleRows, int targetRowCount, - double *totalRowCount, double *totalDeadRowCount) +MongoAcquireSampleRows(Relation relation, + int errorLevel, + HeapTuple *sampleRows, + int targetRowCount, + double *totalRowCount, + double *totalDeadRowCount) { int sampleRowCount = 0; double rowCount = 0; double rowCountToSkip = -1; /* -1 means not set yet */ - double randomState = 0; - Datum *columnValues = NULL; - bool *columnNulls = NULL; - Oid foreignTableId = InvalidOid; - TupleDesc tupleDescriptor = NULL; - AttrNumber columnCount = 0; - AttrNumber columnId = 0; - HTAB *columnMappingHash = NULL; - MONGO_CURSOR *mongoCursor = NULL; - BSON *queryDocument = NULL; - List *columnList = NIL; - ForeignScanState *scanState = NULL; - List *foreignPrivateList = NIL; - ForeignScan *foreignScan = NULL; - MongoFdwModifyState *fmstate = NULL; - char *relationName = NULL; + double randomState; + Datum *columnValues; + bool *columnNulls; + Oid foreignTableId; + TupleDesc tupleDescriptor; + AttrNumber columnCount; + AttrNumber columnId; + HTAB *columnMappingHash; + MONGO_CURSOR *mongoCursor; + BSON *queryDocument; + List *columnList; + ForeignScanState *scanState; + List *foreignPrivateList; + ForeignScan *foreignScan; + MongoFdwModifyState *fmstate; + char *relationName; int executorFlags = 0; MemoryContext oldContext = CurrentMemoryContext; - MemoryContext tupleContext = NULL; + MemoryContext tupleContext; - /* create list of columns in the relation */ + /* Create list of columns in the relation */ tupleDescriptor = RelationGetDescr(relation); columnCount = tupleDescriptor->natts; @@ -2108,7 +2064,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, column->vartype = attr->atttypid; column->vartypmod = attr->atttypmod; #else - /* only assign required fields for column mapping hash */ + /* Only assign required fields for column mapping hash */ column->varattno = columnId; column->vartype = tupleDescriptor->attrs[columnId - 1]->atttypid; column->vartypmod = tupleDescriptor->attrs[columnId - 1]->atttypmod; @@ -2117,7 +2073,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, columnList = lappend(columnList, column); } - /* create state structure */ + /* Create state structure */ scanState = makeNode(ForeignScanState); scanState->ss.ss_currentRelation = relation; @@ -2125,7 +2081,7 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, queryDocument = QueryDocument(foreignTableId, NIL, NULL); foreignPrivateList = list_make2(columnList, NULL); - /* only clean up the query struct, but not its data */ + /* Only clean up the query struct, but not its data */ BsonDestroy(queryDocument); foreignScan = makeNode(ForeignScan); @@ -2155,27 +2111,27 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, ALLOCSET_DEFAULT_SIZES); #endif - /* prepare for sampling rows */ + /* Prepare for sampling rows */ randomState = anl_init_selection_state(targetRowCount); - columnValues = (Datum *) palloc0(columnCount * sizeof(Datum)); - columnNulls = (bool *) palloc0(columnCount * sizeof(bool)); + columnValues = (Datum *) palloc(columnCount * sizeof(Datum)); + columnNulls = (bool *) palloc(columnCount * sizeof(bool)); for (;;) { - /* check for user-requested abort or sleep */ + /* Check for user-requested abort or sleep */ vacuum_delay_point(); - /* initialize all values for this row to null */ + /* 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 */ + const char *bsonDocumentKey = NULL; /* Top level document */ - /* fetch next tuple */ + /* Fetch next tuple */ MemoryContextReset(tupleContext); MemoryContextSwitchTo(tupleContext); @@ -2192,8 +2148,9 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, if (mongoc_cursor_error(mongoCursor, &error)) { MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver error: %s", error.message))); + ereport(ERROR, + (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver error: %s", error.message))); } #else mongo_cursor_error_t errorCode = mongoCursor->err; @@ -2201,8 +2158,10 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, if (errorCode != MONGO_CURSOR_EXHAUSTED) { MongoFreeScanState(fmstate); - ereport(ERROR, (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", errorCode))); + ereport(ERROR, + (errmsg("could not iterate over mongo collection"), + errhint("Mongo driver cursor error code: %d", + errorCode))); } #endif break; @@ -2210,16 +2169,14 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, /* * 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). + * 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 { /* @@ -2228,10 +2185,8 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, * incremented" value of rowCount as t. */ if (rowCountToSkip < 0) - { rowCountToSkip = anl_get_next_S(rowCount, targetRowCount, &randomState); - } if (rowCountToSkip <= 0) { @@ -2256,17 +2211,18 @@ MongoAcquireSampleRows(Relation relation, int errorLevel, rowCount += 1; } - /* clean up */ + /* Clean up */ MemoryContextDelete(tupleContext); MongoFreeScanState(fmstate); pfree(columnValues); pfree(columnNulls); - /* emit some interesting relation info */ + /* Emit some interesting relation info */ relationName = RelationGetRelationName(relation); - ereport(errorLevel, (errmsg("\"%s\": collection contains %.0f rows; %d rows in sample", - relationName, rowCount, sampleRowCount))); + ereport(errorLevel, + (errmsg("\"%s\": collection contains %.0f rows; %d rows in sample", + relationName, rowCount, sampleRowCount))); (*totalRowCount) = rowCount; (*totalDeadRowCount) = 0; diff --git a/mongo_fdw.h b/mongo_fdw.h index 6978fe1..e6be514 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -12,37 +12,33 @@ * *------------------------------------------------------------------------- */ - #ifndef MONGO_FDW_H #define MONGO_FDW_H #include "config.h" #include "mongo_wrapper.h" -#include "bson.h" #ifdef META_DRIVER #include "mongoc.h" #else #include "mongo.h" #endif - -#include "fmgr.h" +#include "access/reloptions.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" -#include "utils/datetime.h" -#include "nodes/pg_list.h" -#if PG_VERSION_NUM < 120000 -#include "nodes/relation.h" -#endif -#include "utils/timestamp.h" -#include "access/reloptions.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" +#if PG_VERSION_NUM < 120000 +#include "nodes/relation.h" +#endif #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/plancat.h" @@ -51,143 +47,140 @@ #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/rel.h" #include "utils/memutils.h" -#include "catalog/pg_user_mapping.h" +#include "utils/timestamp.h" #ifdef META_DRIVER #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 +#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 #else -#define BSON bson -#define BSON_TYPE bson_type -#define BSON_ITERATOR bson_iterator -#define MONGO_CONN mongo -#define MONGO_CURSOR mongo_cursor -#define BSON_TYPE_DOCUMENT BSON_OBJECT -#define BSON_TYPE_NULL BSON_NULL -#define BSON_TYPE_ARRAY BSON_ARRAY -#define BSON_TYPE_INT32 BSON_INT -#define BSON_TYPE_INT64 BSON_LONG -#define BSON_TYPE_DOUBLE BSON_DOUBLE -#define BSON_TYPE_BINDATA BSON_BINDATA -#define BSON_TYPE_BOOL BSON_BOOL -#define BSON_TYPE_UTF8 BSON_STRING -#define BSON_TYPE_OID BSON_OID -#define BSON_TYPE_DATE_TIME BSON_DATE -#define BSON_TYPE_SYMBOL BSON_SYMBOL -#define BSON_TYPE_UNDEFINED BSON_UNDEFINED -#define BSON_TYPE_REGEX BSON_REGEX -#define BSON_TYPE_CODE BSON_CODE -#define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE -#define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP - -#define BSON_ITER_BOOL bson_iterator_bool -#define BSON_ITER_DOUBLE bson_iterator_double -#define BSON_ITER_INT32 bson_iterator_int -#define BSON_ITER_INT64 bson_iterator_long -#define BSON_ITER_OID bson_iterator_oid -#define BSON_ITER_UTF8 bson_iterator_string -#define BSON_ITER_REGEX bson_iterator_regex -#define BSON_ITER_DATE_TIME bson_iterator_date -#define BSON_ITER_CODE bson_iterator_code -#define BSON_ITER_VALUE bson_iterator_value -#define BSON_ITER_KEY bson_iterator_key -#define BSON_ITER_NEXT bson_iterator_next -#define BSON_ITER_TYPE bson_iterator_type -#define BSON_ITER_BINARY bson_iterator_bin_data +#define BSON bson +#define BSON_TYPE bson_type +#define BSON_ITERATOR bson_iterator +#define MONGO_CONN mongo +#define MONGO_CURSOR mongo_cursor +#define BSON_TYPE_DOCUMENT BSON_OBJECT +#define BSON_TYPE_NULL BSON_NULL +#define BSON_TYPE_ARRAY BSON_ARRAY +#define BSON_TYPE_INT32 BSON_INT +#define BSON_TYPE_INT64 BSON_LONG +#define BSON_TYPE_DOUBLE BSON_DOUBLE +#define BSON_TYPE_BINDATA BSON_BINDATA +#define BSON_TYPE_BOOL BSON_BOOL +#define BSON_TYPE_UTF8 BSON_STRING +#define BSON_TYPE_OID BSON_OID +#define BSON_TYPE_DATE_TIME BSON_DATE +#define BSON_TYPE_SYMBOL BSON_SYMBOL +#define BSON_TYPE_UNDEFINED BSON_UNDEFINED +#define BSON_TYPE_REGEX BSON_REGEX +#define BSON_TYPE_CODE BSON_CODE +#define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE +#define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP + +#define BSON_ITER_BOOL bson_iterator_bool +#define BSON_ITER_DOUBLE bson_iterator_double +#define BSON_ITER_INT32 bson_iterator_int +#define BSON_ITER_INT64 bson_iterator_long +#define BSON_ITER_OID bson_iterator_oid +#define BSON_ITER_UTF8 bson_iterator_string +#define BSON_ITER_REGEX bson_iterator_regex +#define BSON_ITER_DATE_TIME bson_iterator_date +#define BSON_ITER_CODE bson_iterator_code +#define BSON_ITER_VALUE bson_iterator_value +#define BSON_ITER_KEY bson_iterator_key +#define BSON_ITER_NEXT bson_iterator_next +#define BSON_ITER_TYPE bson_iterator_type +#define BSON_ITER_BINARY bson_iterator_bin_data #endif /* 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_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" #ifdef META_DRIVER -#define OPTION_NAME_READ_PREFERENCE "read_preference" +#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_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" #endif /* Default values for option parameters */ -#define DEFAULT_IP_ADDRESS "127.0.0.1" -#define DEFAULT_PORT_NUMBER 27017 -#define DEFAULT_DATABASE_NAME "test" +#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) - +#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) /* - * 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. + * 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 */ #ifdef META_DRIVER static const uint32 ValidOptionCount = 16; @@ -196,7 +189,7 @@ static const uint32 ValidOptionCount = 6; #endif static const MongoValidOption ValidOptionArray[] = { - /* foreign server options */ + /* Foreign server options */ {OPTION_NAME_ADDRESS, ForeignServerRelationId}, {OPTION_NAME_PORT, ForeignServerRelationId}, @@ -213,17 +206,15 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_WEAK_CERT, ForeignServerRelationId}, #endif - /* foreign table options */ + /* Foreign table options */ {OPTION_NAME_DATABASE, ForeignTableRelationId}, {OPTION_NAME_COLLECTION, 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 @@ -251,12 +242,10 @@ typedef struct MongoFdwOptions #endif } MongoFdwOptions; - -/* - * MongoFdwExecState keeps foreign data wrapper specific execution state that we - * create and hold onto when executing the query. - */ /* + * 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 @@ -264,7 +253,7 @@ 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 */ + /* Info about parameters for prepared statement */ int p_nums; /* number of parameters to transmit */ FmgrInfo *p_flinfo; /* output conversion functions for them */ @@ -276,16 +265,16 @@ typedef struct MongoFdwModifyState MongoFdwOptions *options; - /* working memory context */ + /* Working memory context */ MemoryContext temp_cxt; /* context for per-tuple temporary data */ } MongoFdwModifyState; - /* * ColumnMapping reprents 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. + * 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 { @@ -302,14 +291,17 @@ 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); +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 List *ApplicableOpExpressionList(RelOptInfo *baserel); -extern BSON *QueryDocument(Oid relationId, List *opExpressionList, +extern BSON *QueryDocument(Oid relationId, + List *opExpressionList, ForeignScanState *scanStateNode); extern List *ColumnList(RelOptInfo *baserel); @@ -317,5 +309,4 @@ extern List *ColumnList(RelOptInfo *baserel); extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); extern Datum mongo_fdw_validator(PG_FUNCTION_ARGS); - #endif /* MONGO_FDW_H */ diff --git a/mongo_query.c b/mongo_query.c index d6920be..a4f3972 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_query.c - * Foreign-data wrapper for remote MongoDB servers + * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,25 +12,19 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" #include "mongo_wrapper.h" +#include +#include +#include + #ifdef META_DRIVER #include "mongoc.h" #else #include "mongo.h" #endif - -#include -#include -#include - -#include "mongo_fdw.h" #include "mongo_query.h" - -#include "catalog/pg_type.h" -#include "nodes/makefuncs.h" #if PG_VERSION_NUM < 120000 #include "nodes/relation.h" #include "optimizer/var.h" @@ -38,12 +32,6 @@ #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/date.h" -#include "utils/lsyscache.h" -#include "utils/numeric.h" -#include "utils/timestamp.h" /* Local functions forward declarations */ static Expr *FindArgumentOfType(List *argumentList, NodeTag argumentType); @@ -54,63 +42,57 @@ static List *ColumnOperatorList(Var *column, List *operatorList); static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant); static void AppendParamValue(BSON *queryDocument, const char *keyName, - Param *paramNode, ForeignScanState *scanStateNode); + Param *paramNode, + ForeignScanState *scanStateNode); /* - * ApplicableOpExpressionList walks over all filter clauses that relate to this - * foreign table, and chooses applicable clauses that we know we can translate - * into Mongo queries. Currently, these clauses include comparison expressions - * that have a column and a constant as arguments. For example, "o_orderdate >= - * date '1994-01-01' + interval '1' year" is an applicable expression. + * ApplicableOpExpressionList + * Walks over all filter clauses that relate to this foreign table, and + * chooses applicable clauses that we know we can translate into Mongo + * queries. + * + * Currently, these clauses include comparison expressions + * that have a column and a constant as arguments. For example, + * "o_orderdate >= date '1994-01-01' + interval '1' year" is an applicable + * expression. */ List * ApplicableOpExpressionList(RelOptInfo *baserel) { List *opExpressionList = NIL; List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell = NULL; + ListCell *restrictInfoCell; foreach(restrictInfoCell, restrictInfoList) { RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); Expr *expression = restrictInfo->clause; - NodeTag expressionType = 0; - - OpExpr *opExpression = NULL; - char *operatorName = NULL; - char *mongoOperatorName = NULL; - List *argumentList = NIL; - Var *column = NULL; - Const *constant = NULL; + OpExpr *opExpression; + char *operatorName; + List *argumentList; + Var *column; + Const *constant; bool equalsOperator = false; bool constantIsArray = false; - Param *paramNode = NULL; + Param *paramNode; - /* we only support operator expressions */ - expressionType = nodeTag(expression); - if (expressionType != T_OpExpr) - { + /* We only support operator expressions */ + if (!IsA(expression, OpExpr)) continue; - } opExpression = (OpExpr *) expression; operatorName = get_opname(opExpression->opno); - /* we only support =, <, >, <=, >=, and <> operators */ + /* We only support =, <, >, <=, >=, and <> operators */ if (strncmp(operatorName, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) - { equalsOperator = true; - } - mongoOperatorName = MongoOperatorName(operatorName); - if (!equalsOperator && mongoOperatorName == NULL) - { + if (!equalsOperator && MongoOperatorName(operatorName) == NULL) continue; - } /* * We only support simple binary operators that compare a column - * against a constant. If the expression is a tree, we don't recurse + * against a constant. If the expression is a tree, we don't recurse * into it. */ argumentList = opExpression->args; @@ -121,45 +103,35 @@ ApplicableOpExpressionList(RelOptInfo *baserel) /* * We don't push down operators where the constant is an array, since * conditional operators for arrays in MongoDB aren't properly - * defined. For example, {similar_products : [ "B0009S4IJW", + * defined. For example, {similar_products : [ "B0009S4IJW", * "6301964144" ]} finds results that are equal to the array, but * {similar_products: {$gte: [ "B0009S4IJW", "6301964144" ]}} returns * an empty set. */ if (constant != NULL) - { - Oid constantArrayTypeId = get_element_type(constant->consttype); - - if (constantArrayTypeId != InvalidOid) - { + if (OidIsValid(get_element_type(constant->consttype))) constantIsArray = true; - } - } if (column != NULL && constant != NULL && !constantIsArray) - { opExpressionList = lappend(opExpressionList, opExpression); - } if (column != NULL && paramNode != NULL) - { opExpressionList = lappend(opExpressionList, opExpression); - } } return opExpressionList; } - /* - * FindArgumentOfType walks over the given argument list, looks for an argument - * with the given type, and returns the argument if it is found. + * FindArgumentOfType + * Walks over the given argument list, looks for an argument with the + * given type, and returns the argument if it is found. */ static Expr * FindArgumentOfType(List *argumentList, NodeTag argumentType) { Expr *foundArgument = NULL; - ListCell *argumentCell = NULL; + ListCell *argumentCell; foreach(argumentCell, argumentList) { @@ -175,26 +147,27 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) return foundArgument; } - /* - * QueryDocument takes in the applicable operator expressions for a relation and - * converts these expressions into equivalent queries in MongoDB. For now, this - * function can only transform simple comparison expressions, and returns these - * transformed expressions in a BSON document. For example, simple expressions + * QueryDocument + * Takes in the applicable operator expressions for a relation and + * converts these expressions into equivalent queries in MongoDB. + * + * For now, this function can only transform simple comparison expressions, and + * returns these transformed expressions in a BSON document. For example, + * simple expressions: * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" become * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". */ BSON * -QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode) +QueryDocument(Oid relationId, List *opExpressionList, + ForeignScanState *scanStateNode) { - List *equalityOperatorList = NIL; - List *comparisonOperatorList = NIL; - List *columnList = NIL; - ListCell *equalityOperatorCell = NULL; - ListCell *columnCell = NULL; - BSON *queryDocument = NULL; - - queryDocument = BsonCreate(); + List *equalityOperatorList; + List *comparisonOperatorList; + List *columnList; + ListCell *equalityOperatorCell; + ListCell *columnCell; + BSON *queryDocument = BsonCreate(); /* * We distinguish between equality expressions and others since we need to @@ -202,17 +175,17 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat * BSON query object. */ equalityOperatorList = EqualityOperatorList(opExpressionList); - comparisonOperatorList = list_difference(opExpressionList, equalityOperatorList); + comparisonOperatorList = list_difference(opExpressionList, + equalityOperatorList); - /* append equality expressions to the query */ + /* Append equality expressions to the query */ foreach(equalityOperatorCell, equalityOperatorList) { OpExpr *equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); Oid columnId = InvalidOid; - char *columnName = NULL; - Const *constant = NULL; - Param *paramNode = NULL; - + char *columnName; + Const *constant; + Param *paramNode; List *argumentList = equalityOperator->args; Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); @@ -229,27 +202,29 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat if (constant != NULL) AppendConstantValue(queryDocument, columnName, constant); else - AppendParamValue(queryDocument, columnName, paramNode, scanStateNode); + AppendParamValue(queryDocument, columnName, paramNode, + scanStateNode); } /* * For comparison expressions, we need to group them by their columns and * append all expressions that correspond to a column as one sub-document. + * * Otherwise, even when we have two expressions to define the upper- and * lower-bound of a range, Mongo uses only one of these expressions during * an index search. */ columnList = UniqueColumnList(comparisonOperatorList); - /* append comparison expressions, grouped by columns, to the query */ + /* Append comparison expressions, grouped by columns, to the query */ foreach(columnCell, columnList) { Var *column = (Var *) lfirst(columnCell); Oid columnId = InvalidOid; - char *columnName = NULL; - List *columnOperatorList = NIL; - ListCell *columnOperatorCell = NULL; - BSON r; + char *columnName; + List *columnOperatorList; + ListCell *columnOperatorCell; + BSON childDocument; columnId = column->varattno; #if PG_VERSION_NUM < 110000 @@ -258,50 +233,53 @@ QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStat columnName = get_attname(relationId, columnId, false); #endif - /* find all expressions that correspond to the column */ - columnOperatorList = ColumnOperatorList(column, comparisonOperatorList); + /* Find all expressions that correspond to the column */ + columnOperatorList = ColumnOperatorList(column, + comparisonOperatorList); - /* for comparison expressions, start a sub-document */ - BsonAppendStartObject(queryDocument, columnName, &r); + /* For comparison expressions, start a sub-document */ + BsonAppendStartObject(queryDocument, columnName, &childDocument); foreach(columnOperatorCell, columnOperatorList) { OpExpr *columnOperator = (OpExpr *) lfirst(columnOperatorCell); - char *operatorName = NULL; - char *mongoOperatorName = NULL; - + char *operatorName; + char *mongoOperatorName; List *argumentList = columnOperator->args; - Const *constant = (Const *) FindArgumentOfType(argumentList, T_Const); + Const *constant = (Const *) FindArgumentOfType(argumentList, + T_Const); operatorName = get_opname(columnOperator->opno); mongoOperatorName = MongoOperatorName(operatorName); #ifdef META_DRIVER - AppendConstantValue(&r, mongoOperatorName, constant); + AppendConstantValue(&childDocument, mongoOperatorName, constant); #else AppendConstantValue(queryDocument, mongoOperatorName, constant); #endif } - BsonAppendFinishObject(queryDocument, &r); + BsonAppendFinishObject(queryDocument, &childDocument); } if (!BsonFinish(queryDocument)) { #ifdef META_DRIVER - ereport(ERROR, (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); #else - ereport(ERROR, (errmsg("could not create document for query"), - errhint("BSON error: %d", queryDocument->err))); + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON error: %d", queryDocument->err))); #endif } return queryDocument; } - /* - * MongoOperatorName takes in the given PostgreSQL comparison operator name, and - * returns its equivalent in MongoDB. + * MongoOperatorName + * Takes in the given PostgreSQL comparison operator name, and returns its + * equivalent in MongoDB. */ static char * MongoOperatorName(const char *operatorName) @@ -313,8 +291,7 @@ MongoOperatorName(const char *operatorName) {"<=", "$lte"}, {">=", "$gte"}, {"<>", "$ne"}}; - - int32 nameIndex = 0; + int32 nameIndex; for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { @@ -330,43 +307,42 @@ MongoOperatorName(const char *operatorName) return (char *) mongoOperatorName; } - /* - * EqualityOperatorList finds the equality (=) operators in the given list, and - * returns these operators in a new list. + * EqualityOperatorList + * Finds the equality (=) operators in the given list, and returns these + * operators in a new list. */ static List * EqualityOperatorList(List *operatorList) { List *equalityOperatorList = NIL; - ListCell *operatorCell = NULL; + ListCell *operatorCell; foreach(operatorCell, operatorList) { OpExpr *operator = (OpExpr *) lfirst(operatorCell); - char *operatorName = NULL; - operatorName = get_opname(operator->opno); - if (strncmp(operatorName, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) - { + if (strncmp(get_opname(operator->opno), EQUALITY_OPERATOR_NAME, + NAMEDATALEN) == 0) equalityOperatorList = lappend(equalityOperatorList, operator); - } } return equalityOperatorList; } - /* - * UniqueColumnList walks over the given operator list, and extracts the column - * argument in each operator. The function then de-duplicates extracted columns, - * and returns them in a new list. + * UniqueColumnList + * Walks over the given operator list, and extracts the column argument in + * each operator. + * + * The function then de-duplicates extracted columns, and returns them in a new + * list. */ static List * UniqueColumnList(List *operatorList) { List *uniqueColumnList = NIL; - ListCell *operatorCell = NULL; + ListCell *operatorCell; foreach(operatorCell, operatorList) { @@ -374,35 +350,33 @@ UniqueColumnList(List *operatorList) List *argumentList = operator->args; Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); - /* list membership is determined via column's equal() function */ + /* List membership is determined via column's equal() function */ uniqueColumnList = list_append_unique(uniqueColumnList, column); } return uniqueColumnList; } - /* - * ColumnOperatorList finds all expressions that correspond to the given column, - * and returns them in a new list. + * ColumnOperatorList + * Finds all expressions that correspond to the given column, and returns + * them in a new list. */ static List * ColumnOperatorList(Var *column, List *operatorList) { List *columnOperatorList = NIL; - ListCell *operatorCell = NULL; + ListCell *operatorCell; foreach(operatorCell, operatorList) { OpExpr *operator = (OpExpr *) lfirst(operatorCell); List *argumentList = operator->args; - - Var *foundColumn = (Var *) FindArgumentOfType(argumentList, T_Var); + Var *foundColumn = (Var *) FindArgumentOfType(argumentList, + T_Var); if (equal(column, foundColumn)) - { columnOperatorList = lappend(columnOperatorList, operator); - } } return columnOperatorList; @@ -432,15 +406,16 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, param_value = ExecEvalExpr(param_expr, econtext, &isNull, NULL); #endif - - AppenMongoValue(queryDocument, keyName, param_value, isNull, - paramNode->paramtype); + AppendMongoValue(queryDocument, keyName, param_value, isNull, + paramNode->paramtype); } /* - * AppendConstantValue 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. + * AppendConstantValue + * 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. */ static void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) @@ -450,11 +425,14 @@ AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) BsonAppendNull(queryDocument, keyName); return; } - AppenMongoValue(queryDocument, keyName, constant->constvalue, false, constant->consttype); + + AppendMongoValue(queryDocument, keyName, constant->constvalue, false, + constant->consttype); } bool -AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id) +AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, + bool isnull, Oid id) { bool status = false; @@ -470,65 +448,69 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu { int16 valueInt = DatumGetInt16(value); - status = BsonAppendInt32(queryDocument, keyName, (int) valueInt); - break; + status = BsonAppendInt32(queryDocument, keyName, + (int) valueInt); } + break; case INT4OID: { int32 valueInt = DatumGetInt32(value); status = BsonAppendInt32(queryDocument, keyName, valueInt); - break; } + break; case INT8OID: { int64 valueLong = DatumGetInt64(value); status = BsonAppendInt64(queryDocument, keyName, valueLong); - break; } + break; case FLOAT4OID: { float4 valueFloat = DatumGetFloat4(value); - status = BsonAppendDouble(queryDocument, keyName, (double) valueFloat); - break; + status = BsonAppendDouble(queryDocument, keyName, + (double) valueFloat); } + break; case FLOAT8OID: { float8 valueFloat = DatumGetFloat8(value); status = BsonAppendDouble(queryDocument, keyName, valueFloat); - break; } + break; case NUMERICOID: { - Datum valueDatum = DirectFunctionCall1(numeric_float8, value); + Datum valueDatum = DirectFunctionCall1(numeric_float8, + value); float8 valueFloat = DatumGetFloat8(valueDatum); status = BsonAppendDouble(queryDocument, keyName, valueFloat); - break; } + break; case BOOLOID: { bool valueBool = DatumGetBool(value); - status = BsonAppendBool(queryDocument, keyName, (int) valueBool); - break; + status = BsonAppendBool(queryDocument, keyName, + (int) valueBool); } + break; case BPCHAROID: case VARCHAROID: case TEXTOID: { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; + char *outputString; + Oid outputFunctionId; + bool typeVarLength; getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); status = BsonAppendUTF8(queryDocument, keyName, outputString); - break; } + break; case BYTEAOID: { int len; @@ -554,19 +536,18 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu status = BsonAppendOid(queryDocument, keyName, &oid); } else - { - status = BsonAppendBinary(queryDocument, keyName, data, len); - } + status = BsonAppendBinary(queryDocument, keyName, data, + len); #else status = BsonAppendBinary(queryDocument, keyName, data, len); #endif - break; } + break; case NAMEOID: { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; + char *outputString; + Oid outputFunctionId; + bool typeVarLength; bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); @@ -574,18 +555,20 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu outputString = OidOutputFunctionCall(outputFunctionId, value); BsonOidFromString(&bsonObjectId, outputString); status = BsonAppendOid(queryDocument, keyName, &bsonObjectId); - break; } + break; case DATEOID: { - Datum valueDatum = DirectFunctionCall1(date_timestamp, value); + 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; + status = BsonAppendDate(queryDocument, keyName, + valueMilliSecs); } + break; case TIMESTAMPOID: case TIMESTAMPTZOID: { @@ -593,9 +576,10 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - status = BsonAppendDate(queryDocument, keyName, valueMilliSecs); - break; + status = BsonAppendDate(queryDocument, keyName, + valueMilliSecs); } + break; case NUMERICARRAY_OID: { ArrayType *array; @@ -607,15 +591,16 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu Datum *elem_values; bool *elem_nulls; int i; - BSON t; + 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); + deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, + &elem_values, &elem_nulls, &num_elems); - BsonAppendStartArray(queryDocument, keyName, &t); + BsonAppendStartArray(queryDocument, keyName, &childDocument); for (i = 0; i < num_elems; i++) { Datum valueDatum; @@ -624,19 +609,22 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu if (elem_nulls[i]) continue; - valueDatum = DirectFunctionCall1(numeric_float8, elem_values[i]); + valueDatum = DirectFunctionCall1(numeric_float8, + elem_values[i]); valueFloat = DatumGetFloat8(valueDatum); #ifdef META_DRIVER - status = BsonAppendDouble(&t, keyName, valueFloat); + status = BsonAppendDouble(&childDocument, keyName, + valueFloat); #else - status = BsonAppendDouble(queryDocument, keyName, valueFloat); + status = BsonAppendDouble(queryDocument, keyName, + valueFloat); #endif } - BsonAppendFinishArray(queryDocument, &t); + BsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); pfree(elem_nulls); - break; } + break; case TEXTARRAYOID: { ArrayType *array; @@ -648,38 +636,43 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu Datum *elem_values; bool *elem_nulls; int i; - BSON t; + 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); + deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, + &elem_values, &elem_nulls, &num_elems); - BsonAppendStartArray(queryDocument, keyName, &t); + BsonAppendStartArray(queryDocument, keyName, &childDocument); for (i = 0; i < num_elems; i++) { - char *valueString = NULL; - Oid outputFunctionId = InvalidOid; - bool typeVarLength = false; + char *valueString; + Oid outputFunctionId; + bool typeVarLength; if (elem_nulls[i]) continue; - getTypeOutputInfo(TEXTOID, &outputFunctionId, &typeVarLength); - valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); - status = BsonAppendUTF8(queryDocument, keyName, valueString); + + getTypeOutputInfo(TEXTOID, &outputFunctionId, + &typeVarLength); + valueString = OidOutputFunctionCall(outputFunctionId, + elem_values[i]); + status = BsonAppendUTF8(queryDocument, keyName, + valueString); } - BsonAppendFinishArray(queryDocument, &t); + BsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); pfree(elem_nulls); - break; } + break; case JSONOID: { - char *outputString = NULL; - Oid outputFunctionId = InvalidOid; + char *outputString; + Oid outputFunctionId; struct json_object *o; - bool typeVarLength = false; + bool typeVarLength; getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); @@ -693,38 +686,39 @@ AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnu } status = JsonToBsonAppendElement(queryDocument, keyName, o); - break; } + 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; - } + /* + * 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; } - /* - * ColumnList takes in the planner's information about this foreign table. The - * function then finds all columns needed for query execution, including those - * used in projections, joins, and filter clauses, de-duplicates these columns, - * and returns them in a new list. + * ColumnList + * Takes in the planner's information about this foreign table. The + * function then finds all columns needed for query execution, including + * those used in projections, joins, and filter clauses, de-duplicates + * these columns, and returns them in a new list. */ List * ColumnList(RelOptInfo *baserel) { List *columnList = NIL; - List *neededColumnList = NIL; - AttrNumber columnIndex = 1; + List *neededColumnList; + AttrNumber columnIndex; AttrNumber columnCount = baserel->max_attr; #if PG_VERSION_NUM >= 90600 @@ -733,19 +727,19 @@ ColumnList(RelOptInfo *baserel) List *targetColumnList = baserel->reltargetlist; #endif List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell = NULL; + ListCell *restrictInfoCell; - /* first add the columns used in joins and projections */ + /* First add the columns used in joins and projections */ neededColumnList = list_copy(targetColumnList); - /* then walk over all restriction clauses, and pull up any used columns */ + /* Then walk over all restriction clauses, and pull up any used columns */ foreach(restrictInfoCell, restrictInfoList) { RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); Node *restrictClause = (Node *) restrictInfo->clause; List *clauseColumnList = NIL; - /* recursively pull up any columns used in the restriction clause */ + /* Recursively pull up any columns used in the restriction clause */ clauseColumnList = pull_var_clause(restrictClause, #if PG_VERSION_NUM < 90600 PVC_RECURSE_AGGREGATES, @@ -755,13 +749,13 @@ ColumnList(RelOptInfo *baserel) neededColumnList = list_union(neededColumnList, clauseColumnList); } - /* walk over all column definitions, and de-duplicate column list */ + /* Walk over all column definitions, and de-duplicate column list */ for (columnIndex = 1; columnIndex <= columnCount; columnIndex++) { - ListCell *neededColumnCell = NULL; + ListCell *neededColumnCell; Var *column = NULL; - /* look for this column in the needed column list */ + /* Look for this column in the needed column list */ foreach(neededColumnCell, neededColumnList) { Var *neededColumn = (Var *) lfirst(neededColumnCell); @@ -774,9 +768,7 @@ ColumnList(RelOptInfo *baserel) } if (column != NULL) - { columnList = lappend(columnList, column); - } } return columnList; diff --git a/mongo_query.h b/mongo_query.h index aa25db8..6ed4a31 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_query.h - * Foreign-data wrapper for remote MongoDB servers + * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,13 +12,15 @@ * *------------------------------------------------------------------------- */ - #ifndef MONGO_QUERY_H #define MONGO_QUERY_H - #define NUMERICARRAY_OID 1231 -bool AppenMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); +bool AppendMongoValue(BSON *queryDocument, + const char *keyName, + Datum value, + bool isnull, + Oid id); #endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 0b98f4d..3054d2b 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_wrapper.c - * Foreign-data wrapper for remote MongoDB servers + * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,53 +12,52 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" -#include "mongo_wrapper.h" #ifdef META_DRIVER #include "mongoc.h" #else #include "mongo.h" #endif - -#include -#include -#include - -#include "mongo_fdw.h" +#include "mongo_wrapper.h" #define QUAL_STRING_LEN 512 MONGO_CONN * -MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password) +MongoConnect(MongoFdwOptions *opt) { MONGO_CONN *conn; conn = mongo_alloc(); mongo_init(conn); - if (mongo_connect(conn, host, port) != MONGO_OK) + if (mongo_connect(conn, opt->svr_address, opt->svr_port) != MONGO_OK) { int err = conn->err; mongo_destroy(conn); mongo_dealloc(conn); - ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), - errhint("Mongo driver connection error: %d", err))); + ereport(ERROR, + (errmsg("could not connect to %s:%d", opt->svr_address, + opt->svr_port), + errhint("Mongo driver connection error: %d.", err))); } - if (user && password) + if (opt->svr_username && opt->svr_password) { - if (mongo_cmd_authenticate(conn, databaseName, user, password) != MONGO_OK) + if (mongo_cmd_authenticate(conn, opt->svr_database, opt->svr_username, + opt->svr_password) != MONGO_OK) { char *str = pstrdup(conn->errstr); mongo_destroy(conn); mongo_dealloc(conn); - ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), - errhint("Mongo driver connection error: %s", str))); + ereport(ERROR, + (errmsg("could not connect to %s:%d", opt->svr_address, + opt->svr_port), + errhint("Mongo driver connection error: %s", str))); } } + return conn; } @@ -76,25 +75,28 @@ MongoInsert(MONGO_CONN *conn, char *database, char *collection, bson *b) snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); if (mongo_insert(conn, qual, b, NULL) != MONGO_OK) - ereport(ERROR, (errmsg("failed to insert row"), - errhint("Mongo driver insert error: %d", conn->err))); + ereport(ERROR, + (errmsg("failed to insert row"), + errhint("Mongo driver insert error: %d.", conn->err))); + return true; } - bool -MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) +MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, + BSON *op) { char qual[QUAL_STRING_LEN]; snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); if (mongo_update(conn, qual, b, op, MONGO_UPDATE_BASIC, 0) != MONGO_OK) - ereport(ERROR, (errmsg("failed to update row"), - errhint("Mongo driver update error: %d", conn->err))); + ereport(ERROR, + (errmsg("failed to update row"), + errhint("Mongo driver update error: %d.", conn->err))); + return true; } - bool MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { @@ -102,13 +104,13 @@ MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); if (mongo_remove(conn, qual, b, NULL) != MONGO_OK) - ereport(ERROR, (errmsg("failed to delete row"), - errhint("Mongo driver delete error: %d", conn->err))); + ereport(ERROR, + (errmsg("failed to delete row"), + errhint("Mongo driver delete error: %d.", conn->err))); return true; } - MONGO_CURSOR * MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) { @@ -119,17 +121,16 @@ MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) c = mongo_cursor_alloc(); mongo_cursor_init(c, conn, qual); mongo_cursor_set_query(c, q); + return c; } - const bson * MongoCursorBson(MONGO_CURSOR *c) { return mongo_cursor_bson(c); } - bool MongoCursorNext(MONGO_CURSOR *c, BSON *b) { @@ -146,11 +147,12 @@ MongoCursorDestroy(MONGO_CURSOR *c) BSON * BsonCreate() { - BSON *b = NULL; + BSON *doc = NULL; + + doc = bson_alloc(); + bson_init(doc); - b = bson_alloc(); - bson_init(b); - return b; + return doc; } void @@ -164,6 +166,7 @@ bool BsonIterInit(BSON_ITERATOR *it, BSON *b) { bson_iterator_init(it, b); + return true; } @@ -171,6 +174,7 @@ bool BsonIterSubObject(BSON_ITERATOR *it, BSON *b) { bson_iterator_subobject_init(it, b, 0); + return true; } @@ -180,28 +184,24 @@ BsonIterInt32(BSON_ITERATOR *it) return bson_iterator_int(it); } - int64_t BsonIterInt64(BSON_ITERATOR *it) { return bson_iterator_long(it); } - double BsonIterDouble(BSON_ITERATOR *it) { return bson_iterator_double(it); } - bool BsonIterBool(BSON_ITERATOR *it) { return bson_iterator_bool(it); } - const char * BsonIterString(BSON_ITERATOR *it) { @@ -220,21 +220,18 @@ BsonIterBinLen(BSON_ITERATOR *it) return bson_iterator_bin_len(it); } - bson_oid_t * BsonIterOid(BSON_ITERATOR *it) { return bson_iterator_oid(it); } - time_t BsonIterDate(BSON_ITERATOR *it) { return bson_iterator_date(it); } - int BsonIterType(BSON_ITERATOR *it) { @@ -247,22 +244,20 @@ BsonIterNext(BSON_ITERATOR *it) return bson_iterator_next(it); } - bool BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { bson_iterator_subiterator(it, sub); + return true; } - void BsonOidFromString(bson_oid_t *o, char *str) { bson_oid_from_string(o, str); } - bool BsonAppendOid(BSON *b, const char *key, bson_oid_t *v) { @@ -281,14 +276,12 @@ BsonAppendNull(BSON *b, const char *key) return (bson_append_null(b, key) == MONGO_OK); } - bool BsonAppendInt32(BSON *b, const char *key, int v) { return (bson_append_int(b, key, v) == MONGO_OK); } - bool BsonAppendInt64(BSON *b, const char *key, int64_t v) { @@ -318,14 +311,12 @@ BsonAppendDate(BSON *b, const char *key, time_t v) return (bson_append_date(b, key, v) == MONGO_OK); } - bool BsonAppendStartArray(BSON *b, const char *key, BSON *c) { return (bson_append_start_array(b, key) == MONGO_OK); } - bool BsonAppendFinishArray(BSON *b, BSON *c) { @@ -344,16 +335,12 @@ BsonAppendFinishObject(BSON *b, BSON *r) return (bson_append_finish_object(b) == MONGO_OK); } - bool BsonAppendBson(BSON *b, char *key, BSON *c) { return (bson_append_bson(b, key, c) == MONGO_OK); } - - - bool BsonFinish(BSON *b) { @@ -369,9 +356,8 @@ JsonTokenerPrase(char *s) bool JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { - bool status; + bool status = true; - status = true; if (!v) { bson_append_null(bb, k); @@ -383,22 +369,18 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) case json_type_int: bson_append_int(bb, k, json_object_get_int(v)); break; - case json_type_boolean: bson_append_bool(bb, k, json_object_get_boolean(v)); break; - case json_type_double: bson_append_double(bb, k, json_object_get_double(v)); break; - case json_type_string: bson_append_string(bb, k, json_object_get_string(v)); break; - case json_type_object: { - struct json_object *joj = NULL; + struct json_object *joj; joj = json_object_object_get(v, "$oid"); @@ -423,13 +405,11 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { json_object_object_foreach(v, kk, vv) - { JsonToBsonAppendElement(bb, kk, vv); - } } bson_append_finish_object(bb); - break; } + break; case json_type_array: { int i; @@ -439,26 +419,29 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) for (i = 0; i < json_object_array_length(v); i++) { sprintf(buf, "%d", i); - JsonToBsonAppendElement(bb, buf, json_object_array_get_idx(v, i)); + JsonToBsonAppendElement(bb, buf, + json_object_array_get_idx(v, i)); } bson_append_finish_object(bb); - break; } + break; default: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("can't handle type for : %s", json_object_to_json_string(v)))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("can't handle type for : %s", + json_object_to_json_string(v)))); } + return status; } - double -MongoAggregateCount(MONGO_CONN *conn, const char *database, const char *collection, const BSON *b) +MongoAggregateCount(MONGO_CONN *conn, const char *database, + const char *collection, const BSON *b) { return mongo_count(conn, database, collection, b); } - void BsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer) { @@ -495,29 +478,9 @@ BsonIterValue(BSON_ITERATOR *i) return bson_iterator_value(i); } -void -BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) -{ - if (isArray) - DumpJsonArray(output, iter); - else - DumpJsonObject(output, iter); -} - -void -DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) -{ - -} - -void -DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) -{ - -} - char * BsonAsJson(const BSON *bsonDocument) { - elog(ERROR, "Full document retrival only available in MongoC meta driver"); + ereport(ERROR, + (errmsg("full document retrival only available in MongoC meta driver"))); } diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 4e2394b..3faee07 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_wrapper.h - * Foreign-data wrapper for remote MongoDB servers + * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -15,9 +15,7 @@ #ifndef MONGO_WRAPPER_H #define MONGO_WRAPPER_H - #include "mongo_fdw.h" -#include "bson.h" #ifdef META_DRIVER #include "mongoc.h" @@ -26,26 +24,27 @@ #endif #define json_object json_object_tmp -#include #include #include #ifdef META_DRIVER -MONGO_CONN *MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password, - char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, char *pem_pwd, char *ca_file, - char *ca_dir, char *crl_file, bool weak_cert_validation); +MONGO_CONN *MongoConnect(MongoFdwOptions *opt); #else -MONGO_CONN *MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password); +MONGO_CONN *MongoConnect(MongoFdwOptions *opt); #endif 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); +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); +double MongoAggregateCount(MONGO_CONN *conn, const char *database, + const char *collection, const BSON *b); BSON *BsonCreate(void); void BsonDestroy(BSON *b); @@ -68,7 +67,7 @@ const bson_oid_t *BsonIterOid(BSON_ITERATOR *it); #else bson_oid_t *BsonIterOid(BSON_ITERATOR *it); #endif -time_t BsonIterDate(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); @@ -85,7 +84,6 @@ const char *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); @@ -107,10 +105,10 @@ json_object *JsonTokenerPrase(char *s); char *BsonAsJson(const BSON *bsonDocument); -void BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray); +void BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, + bool isArray); void DumpJsonObject(StringInfo output, BSON_ITERATOR *iter); void DumpJsonArray(StringInfo output, BSON_ITERATOR *iter); - -#endif +#endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index a10f2ac..b8c1366 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * mongo_wrapper_meta.c - * Foreign-data wrapper for remote MongoDB servers + * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,66 +12,133 @@ * *------------------------------------------------------------------------- */ - - #include "postgres.h" + #include #include "mongo_wrapper.h" /* - * Connect to MongoDB server using Host/ip and Port number. + * MongoConnect + * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN * -MongoConnect(const char *host, const unsigned short port, char *databaseName, char *user, char *password, - char *authenticationDatabase, char *replicaSet, char *readPreference, bool ssl, char *pem_file, - char *pem_pwd, char *ca_file, char *ca_dir, char *crl_file, bool weak_cert_validation) -{ - MONGO_CONN *client = NULL; - char *uri = NULL; - - if (user && password) - if (authenticationDatabase) - if (replicaSet) - if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", authenticationDatabase, replicaSet); +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", user, password, host, port, databaseName, ssl ? "true" : "false", authenticationDatabase, replicaSet); - else if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&authSource=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", authenticationDatabase); + 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", user, password, host, port, databaseName, ssl ? "true" : "false", authenticationDatabase); - else if (replicaSet) - if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false", replicaSet); + 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", user, password, host, port, databaseName, ssl ? "true" : "false", replicaSet); - else if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%s@%s:%hu/%s?readPreference=%s&ssl=%s", user, password, host, port, databaseName, readPreference, ssl ? "true" : "false"); + 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", user, password, host, port, databaseName, ssl ? "true" : "false"); - else if (replicaSet) - if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s&replicaSet=%s", host, port, databaseName, readPreference, ssl ? "true" : "false", replicaSet); + 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", host, port, databaseName, ssl ? "true" : "false", replicaSet); - else if (readPreference) - uri = bson_strdup_printf("mongodb://%s:%hu/%s?readPreference=%s&ssl=%s", host, port, databaseName, readPreference, ssl ? "true" : "false"); + 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", host, port, databaseName, ssl ? "true" : "false"); + 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 (ssl) + if (opt->ssl) { mongoc_ssl_opt_t *ssl_opts = (mongoc_ssl_opt_t *) malloc(sizeof(mongoc_ssl_opt_t)); - ssl_opts->pem_file = pem_file; - ssl_opts->pem_pwd = pem_pwd; - ssl_opts->ca_file = ca_file; - ssl_opts->ca_dir = ca_dir; - ssl_opts->crl_file = crl_file; - ssl_opts->weak_cert_validation = weak_cert_validation; + 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); } @@ -79,13 +146,17 @@ MongoConnect(const char *host, const unsigned short port, char *databaseName, ch bson_free(uri); if (client == NULL) - ereport(ERROR, (errmsg("could not connect to %s:%d", host, port), - errhint("Mongo driver connection error"))); + ereport(ERROR, + (errmsg("could not connect to %s:%d", opt->svr_address, + opt->svr_port), + errhint("Mongo driver connection error."))); + return client; } /* - * Disconnect from MongoDB server. + * MongoDisconnect + * Disconnect from MongoDB server. */ void MongoDisconnect(MONGO_CONN *conn) @@ -94,14 +165,14 @@ MongoDisconnect(MONGO_CONN *conn) mongoc_client_destroy(conn); } - /* - * Insert a document 'b' into MongoDB. + * MongoInsert + * Insert a document 'b' into MongoDB. */ bool MongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) { - mongoc_collection_t *c = NULL; + mongoc_collection_t *c; bson_error_t error; bool r = false; @@ -110,19 +181,22 @@ MongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) 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))); + ereport(ERROR, + (errmsg("failed to insert row"), + errhint("Mongo error: \"%s\"", error.message))); + return true; } - /* - * Update a document 'b' into MongoDB. + * MongoUpdate + * Update a document 'b' into MongoDB. */ bool -MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) +MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, + BSON *op) { - mongoc_collection_t *c = NULL; + mongoc_collection_t *c; bson_error_t error; bool r = false; @@ -131,57 +205,65 @@ MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *o 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))); + ereport(ERROR, + (errmsg("failed to update row"), + errhint("Mongo error: \"%s\"", error.message))); + return true; } - /* - * Delete MongoDB's document. + * MongoDelete + * Delete MongoDB's document. */ bool MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { - mongoc_collection_t *c = NULL; + 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); + 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))); + ereport(ERROR, + (errmsg("failed to delete row"), + errhint("Mongo error: \"%s\"", error.message))); + return true; } /* - * Performs a query against the configured MongoDB server and return - * cursor which can be destroyed by calling mongoc_cursor_current. + * 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 = NULL; - MONGO_CURSOR *cur = NULL; + mongoc_collection_t *c; + MONGO_CURSOR *cur; bson_error_t error; c = mongoc_client_get_collection(conn, database, collection); cur = mongoc_collection_find_with_opts(c, q, NULL, NULL); mongoc_cursor_error(cur, &error); if (!cur) - ereport(ERROR, (errmsg("failed to create cursor"), - errhint("Mongo error: \"%s\"", error.message))); + ereport(ERROR, + (errmsg("failed to create cursor"), + errhint("Mongo error: \"%s\"", error.message))); mongoc_collection_destroy(c); + return cur; } - /* - * Destroy cursor created by calling MongoCursorCreate function. + * MongoCursorDestroy + * Destroy cursor created by calling MongoCursorCreate function. */ void MongoCursorDestroy(MONGO_CURSOR *c) @@ -191,7 +273,8 @@ MongoCursorDestroy(MONGO_CURSOR *c) /* - * Get the current document from cursor. + * MongoCursorNext + * Get the current document from cursor. */ const BSON * MongoCursorBson(MONGO_CURSOR *c) @@ -200,7 +283,8 @@ MongoCursorBson(MONGO_CURSOR *c) } /* - * Get the next document from the cursor. + * MongoCursorNext + * Get the next document from the cursor. */ bool MongoCursorNext(MONGO_CURSOR *c, BSON *b) @@ -208,25 +292,27 @@ MongoCursorNext(MONGO_CURSOR *c, BSON *b) return mongoc_cursor_next(c, (const BSON **) &b); } - /* - * 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(). + * 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 *b = NULL; + BSON *doc; + + doc = bson_new(); + bson_init(doc); - b = bson_new(); - bson_init(b); - return b; + return doc; } /* - * Destroy Bson objected created by BsonCreate function. + * BsonDestroy + * Destroy Bson object created by BsonCreate function. */ void BsonDestroy(BSON *b) @@ -234,9 +320,9 @@ BsonDestroy(BSON *b) bson_destroy(b); } - /* - * Initialize the bson Iterator. + * BsonIterInit + * Initialize the bson Iterator. */ bool BsonIterInit(BSON_ITERATOR *it, BSON *b) @@ -244,7 +330,6 @@ BsonIterInit(BSON_ITERATOR *it, BSON *b) return bson_iter_init(it, b); } - bool BsonIterSubObject(BSON_ITERATOR *it, BSON *b) { @@ -253,6 +338,7 @@ BsonIterSubObject(BSON_ITERATOR *it, BSON *b) bson_iter_document(it, &len, &buffer); bson_init_static(b, buffer, len); + return true; } @@ -262,28 +348,24 @@ BsonIterInt32(BSON_ITERATOR *it) return bson_iter_int32(it); } - int64_t BsonIterInt64(BSON_ITERATOR *it) { return bson_iter_int64(it); } - double BsonIterDouble(BSON_ITERATOR *it) { return bson_iter_double(it); } - bool BsonIterBool(BSON_ITERATOR *it) { return bson_iter_bool(it); } - const char * BsonIterString(BSON_ITERATOR *it) { @@ -299,6 +381,7 @@ BsonIterBinData(BSON_ITERATOR *it, uint32_t *len) bson_subtype_t subtype = BSON_SUBTYPE_BINARY; bson_iter_binary(it, &subtype, len, &binary); + return (char *) binary; } @@ -308,14 +391,12 @@ 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) { @@ -334,21 +415,18 @@ 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) { @@ -367,28 +445,24 @@ 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) { @@ -411,7 +485,8 @@ BsonAppendUTF8(BSON *b, const char *key, char *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); + return bson_append_binary(b, key, (int) strlen(key), BSON_SUBTYPE_BINARY, + (const uint8_t *) v, len); } bool @@ -420,7 +495,6 @@ 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) { @@ -433,20 +507,18 @@ 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 BsonFinish(BSON *b) { /* - * There is no need for bson_finish in Meta Driver. We are doing nothing, - * just because of compatiblity with legacy driver. + * There is no need for bson_finish in Meta Driver. We are doing nothing, + * just because of compatibility with legacy driver. */ return true; } @@ -454,9 +526,8 @@ BsonFinish(BSON *b) bool JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { - bool status; + bool status = true; - status = true; if (!v) { BsonAppendNull(bb, k); @@ -468,23 +539,19 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *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 = NULL; + struct json_object *joj; joj = json_object_object_get(v, "$oid"); @@ -507,13 +574,11 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { json_object_object_foreach(v, kk, vv) - { JsonToBsonAppendElement(&t, kk, vv); - } } BsonAppendFinishObject(bb, &t); - break; } + break; case json_type_array: { int i; @@ -527,12 +592,15 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) JsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i)); } BsonAppendFinishObject(bb, &t); - break; } + break; default: - ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("can't handle type for : %s", json_object_to_json_string(v)))); + ereport(ERROR, + (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), + errmsg("can't handle type for : %s", + json_object_to_json_string(v)))); } + return status; } @@ -543,29 +611,33 @@ JsonTokenerPrase(char *s) } /* - * Count the number of documents. + * MongoAggregateCount + * Count the number of documents. */ double -MongoAggregateCount(MONGO_CONN *conn, const char *database, const char *collection, const BSON *b) +MongoAggregateCount(MONGO_CONN *conn, const char *database, + const char *collection, const BSON *b) { - BSON *command = NULL; - BSON *reply = NULL; - BSON *doc = NULL; + BSON *command; + BSON *reply; double count = 0; - mongoc_cursor_t *cursor = NULL; - bool ret = false; + mongoc_cursor_t *cursor; command = BsonCreate(); reply = BsonCreate(); BsonAppendUTF8(command, "count", (char *) collection); - if (b) /* not empty */ + if (b) /* Not empty */ BsonAppendBson(command, "query", (BSON *) b); BsonFinish(command); - cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, 0, command, NULL, NULL); + cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, + 0, command, NULL, NULL); if (cursor) { + BSON *doc; + bool ret; + ret = mongoc_cursor_next(cursor, (const BSON **) &doc); if (ret) { @@ -579,13 +651,8 @@ MongoAggregateCount(MONGO_CONN *conn, const char *database, const char *collecti } BsonDestroy(reply); BsonDestroy(command); - return count; -} - -void -BsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer) -{ + return count; } void @@ -622,7 +689,9 @@ BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) } /* - * DumpJson converts BSON document to a JSON string. + * 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]. @@ -632,15 +701,15 @@ BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) void DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) { - char *json; uint32_t len; - const uint8_t *data = NULL; - BSON bson; + const uint8_t *data; + BSON bson; bson_iter_document(iter, &len, &data); if (bson_init_static(&bson, data, len)) { - json = bson_as_json(&bson, NULL); + char *json = bson_as_json(&bson, NULL); + if (json != NULL) { appendStringInfoString(output, json); @@ -652,14 +721,15 @@ DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) void DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { - char *json; uint32_t len; const uint8_t *data; - BSON bson; + BSON bson; bson_iter_array(iter, &len, &data); if (bson_init_static(&bson, data, len)) { + char *json; + if ((json = bson_array_as_json(&bson, NULL))) { appendStringInfoString(output, json); diff --git a/option.c b/option.c index faa8752..c5878c4 100644 --- a/option.c +++ b/option.c @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------- * * option.c - * Foreign-data wrapper for remote MongoDB servers + * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. @@ -12,34 +12,12 @@ * *------------------------------------------------------------------------- */ - #include "postgres.h" -#include "mongo_wrapper.h" -#include "mongo_fdw.h" - -#include "access/reloptions.h" -#include "catalog/pg_type.h" -#include "commands/defrem.h" -#include "commands/explain.h" -#include "commands/vacuum.h" -#include "foreign/fdwapi.h" -#include "foreign/foreign.h" -#include "nodes/makefuncs.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/hsearch.h" -#include "utils/lsyscache.h" -#include "utils/rel.h" -#include "utils/memutils.h" + #include "miscadmin.h" +#include "mongo_wrapper.h" -static char *mongo_get_option_value(Oid foreignTableId, const char *optionName); +static char *mongo_get_option_value(List *optionList, const char *optionName); /* * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER, @@ -52,9 +30,12 @@ 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. + * 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) @@ -62,19 +43,20 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) Datum optionArray = PG_GETARG_DATUM(0); Oid optionContextId = PG_GETARG_OID(1); List *optionList = untransformRelOptions(optionArray); - ListCell *optionCell = NULL; + ListCell *optionCell; foreach(optionCell, optionList) { DefElem *optionDef = (DefElem *) lfirst(optionCell); char *optionName = optionDef->defname; bool optionValid = false; - - int32 optionIndex = 0; + int32 optionIndex; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) { - const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); + const MongoValidOption *validOption; + + validOption = &(ValidOptionArray[optionIndex]); if ((optionContextId == validOption->optionContextId) && (strncmp(optionName, validOption->optionName, NAMEDATALEN) == 0)) @@ -84,46 +66,46 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) } } - /* if invalid option, display an informative error message */ + /* If invalid option, display an informative error message */ if (!optionValid) { - StringInfo 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))); + 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 port option is given, error out if its value isn't an integer */ if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0) - { - char *optionValue = defGetString(optionDef); - int32 portNumber = pg_atoi(optionValue, sizeof(int32), 0); - - (void) portNumber; - } + (void) pg_atoi(defGetString(optionDef), sizeof(int32), 0); } + 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. + * 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 = 0; + int32 optionIndex; for (optionIndex = 0; optionIndex < ValidOptionCount; optionIndex++) { - const MongoValidOption *validOption = &(ValidOptionArray[optionIndex]); + const MongoValidOption *validOption; + + validOption = &(ValidOptionArray[optionIndex]); - /* if option belongs to current context, append option name */ + /* If option belongs to current context, append option name */ if (currentContextId == validOption->optionContextId) { if (firstOptionPrinted) @@ -133,92 +115,82 @@ mongo_option_names_string(Oid currentContextId) 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. + * 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) { - MongoFdwOptions *options = NULL; - char *addressName = NULL; - char *portName = NULL; - int32 portNumber = 0; - char *svr_database = NULL; - char *collectionName = NULL; - char *svr_username = NULL; - char *svr_password = NULL; -#ifdef META_DRIVER - char *readPreference = NULL; - char *authenticationDatabase = NULL; - char *replicaSet = NULL; - bool ssl = false; - char *pem_file = NULL; - char *pem_pwd = NULL; - char *ca_file = NULL; - char *ca_dir = NULL; - char *crl_file = NULL; - bool weak_cert_validation = false; - - readPreference = mongo_get_option_value(foreignTableId, OPTION_NAME_READ_PREFERENCE); - authenticationDatabase = mongo_get_option_value(foreignTableId, OPTION_NAME_AUTHENTICATION_DATABASE); - replicaSet = mongo_get_option_value(foreignTableId, OPTION_NAME_REPLICA_SET); - ssl = mongo_get_option_value(foreignTableId, OPTION_NAME_SSL); - pem_file = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_FILE); - pem_pwd = mongo_get_option_value(foreignTableId, OPTION_NAME_PEM_PWD); - ca_file = mongo_get_option_value(foreignTableId, OPTION_NAME_CA_FILE); - ca_dir = mongo_get_option_value(foreignTableId, OPTION_NAME_CA_DIR); - crl_file = mongo_get_option_value(foreignTableId, OPTION_NAME_CRL_FILE); - weak_cert_validation = mongo_get_option_value(foreignTableId, OPTION_NAME_WEAK_CERT); -#endif + ForeignTable *foreignTable; + ForeignServer *foreignServer; + UserMapping *mapping; + char *portName; + List *optionList = NIL; + MongoFdwOptions *options; - addressName = mongo_get_option_value(foreignTableId, OPTION_NAME_ADDRESS); - if (addressName == NULL) - addressName = pstrdup(DEFAULT_IP_ADDRESS); + foreignTable = GetForeignTable(foreignTableId); + foreignServer = GetForeignServer(foreignTable->serverid); + mapping = GetUserMapping(GetUserId(), foreignTable->serverid); - portName = mongo_get_option_value(foreignTableId, OPTION_NAME_PORT); - if (portName == NULL) - portNumber = DEFAULT_PORT_NUMBER; - else - portNumber = pg_atoi(portName, sizeof(int32), 0); + optionList = list_concat(optionList, foreignTable->options); + optionList = list_concat(optionList, foreignServer->options); + optionList = list_concat(optionList, mapping->options); - svr_database = mongo_get_option_value(foreignTableId, OPTION_NAME_DATABASE); - if (svr_database == NULL) - svr_database = pstrdup(DEFAULT_DATABASE_NAME); + options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); - collectionName = mongo_get_option_value(foreignTableId, OPTION_NAME_COLLECTION); - if (collectionName == NULL) - collectionName = get_rel_name(foreignTableId); +#ifdef META_DRIVER + options->readPreference = mongo_get_option_value(optionList, + OPTION_NAME_READ_PREFERENCE); + options->authenticationDatabase = mongo_get_option_value(optionList, + OPTION_NAME_AUTHENTICATION_DATABASE); + options->replicaSet = mongo_get_option_value(optionList, + OPTION_NAME_REPLICA_SET); + options->ssl = mongo_get_option_value(optionList, OPTION_NAME_SSL); + options->pem_file = mongo_get_option_value(optionList, + OPTION_NAME_PEM_FILE); + options->pem_pwd = mongo_get_option_value(optionList, OPTION_NAME_PEM_PWD); + options->ca_file = mongo_get_option_value(optionList, OPTION_NAME_CA_FILE); + options->ca_dir = mongo_get_option_value(optionList, + OPTION_NAME_CA_DIR); + options->crl_file = mongo_get_option_value(optionList, + OPTION_NAME_CRL_FILE); + options->weak_cert_validation = mongo_get_option_value(optionList, + OPTION_NAME_WEAK_CERT); +#endif + options->svr_address = mongo_get_option_value(optionList, + OPTION_NAME_ADDRESS); + if (options->svr_address == NULL) + options->svr_address = pstrdup(DEFAULT_IP_ADDRESS); - svr_username = mongo_get_option_value(foreignTableId, OPTION_NAME_USERNAME); - svr_password = mongo_get_option_value(foreignTableId, OPTION_NAME_PASSWORD); + portName = mongo_get_option_value(optionList, OPTION_NAME_PORT); + if (portName == NULL) + options->svr_port = DEFAULT_PORT_NUMBER; + else + options->svr_port = pg_atoi(portName, sizeof(int32), 0); - options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); + options->svr_database = mongo_get_option_value(optionList, + OPTION_NAME_DATABASE); + if (options->svr_database == NULL) + options->svr_database = pstrdup(DEFAULT_DATABASE_NAME); - options->svr_address = addressName; - options->svr_port = portNumber; - options->svr_database = svr_database; - options->collectionName = collectionName; - options->svr_username = svr_username; - options->svr_password = svr_password; + options->collectionName = mongo_get_option_value(optionList, + OPTION_NAME_COLLECTION); + if (options->collectionName == NULL) + options->collectionName = get_rel_name(foreignTableId); -#ifdef META_DRIVER - options->readPreference = readPreference; - options->authenticationDatabase = authenticationDatabase; - options->replicaSet = replicaSet; - options->ssl = ssl; - options->pem_file = pem_file; - options->pem_pwd = pem_pwd; - options->ca_file = ca_file; - options->ca_dir = ca_dir; - options->crl_file = crl_file; - options->weak_cert_validation = weak_cert_validation; -#endif + options->svr_username = mongo_get_option_value(optionList, + OPTION_NAME_USERNAME); + options->svr_password = mongo_get_option_value(optionList, + OPTION_NAME_PASSWORD); return options; } @@ -235,28 +207,17 @@ mongo_free_options(MongoFdwOptions *options) } /* - * mongo_get_option_value walks over foreign table and foreign server options, and - * looks for the option with the given name. If found, the function returns the - * option's value. + * mongo_get_option_value + * Walks over foreign table and foreign server options, and looks for the + * option with the given name. If found, the function returns the + * option's value. */ static char * -mongo_get_option_value(Oid foreignTableId, const char *optionName) +mongo_get_option_value(List *optionList, const char *optionName) { - ForeignTable *foreignTable = NULL; - ForeignServer *foreignServer = NULL; - List *optionList = NIL; - ListCell *optionCell = NULL; - UserMapping *mapping = NULL; + ListCell *optionCell; char *optionValue = NULL; - foreignTable = GetForeignTable(foreignTableId); - foreignServer = GetForeignServer(foreignTable->serverid); - mapping = GetUserMapping(GetUserId(), foreignTable->serverid); - - optionList = list_concat(optionList, foreignTable->options); - optionList = list_concat(optionList, foreignServer->options); - optionList = list_concat(optionList, mapping->options); - foreach(optionCell, optionList) { DefElem *optionDef = (DefElem *) lfirst(optionCell); @@ -268,5 +229,6 @@ mongo_get_option_value(Oid foreignTableId, const char *optionName) break; } } + return optionValue; } From 278ce1bc24cbe8a695ba030fd69cfe4c78b205e7 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 23 Jun 2020 16:26:00 +0530 Subject: [PATCH 129/239] Add one missing header file, and fix compilation warning. Found when compiled against PostgreSQL. Jeevan Chalke. --- mongo_fdw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 17bc98a..a1c763f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -46,6 +46,7 @@ #else #include "utils/jsonfuncs.h" #endif +#include "utils/rel.h" /* Declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -2040,7 +2041,7 @@ MongoAcquireSampleRows(Relation relation, HTAB *columnMappingHash; MONGO_CURSOR *mongoCursor; BSON *queryDocument; - List *columnList; + List *columnList = NIL; ForeignScanState *scanState; List *foreignPrivateList; ForeignScan *foreignScan; From 8ce1225d85e23c5bc8b5d4409a3282f8d3ad0412 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 7 Jul 2020 19:36:39 +0530 Subject: [PATCH 130/239] Check that the existing or new connection is reachable/active or not. mongoc_client_new() function does not return NULL when it fails to connect with the server. So we need to ping the server to make sure that connection is reachable with the parameters given. Along the way, move the connection establishment logic from MongoExecForeignInsert/Update/Delete functions to the MongoBeginForeignModify() function so that we won't try to ping the server for every insert/update/delete operations. FDW-127, Vaibhav Dalvi, reviewed by Suraj Kharage. --- connection.c | 19 +++++++++ expected/mongo_fdw.out | 22 +++++++++- mongo_fdw.c | 93 +++++++++++------------------------------- sql/mongo_fdw.sql | 6 +++ 4 files changed, 69 insertions(+), 71 deletions(-) diff --git a/connection.c b/connection.c index 1e20ca9..5e5d976 100644 --- a/connection.c +++ b/connection.c @@ -156,6 +156,25 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, #endif } +#ifdef META_DRIVER + /* 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))); + } +#endif return entry->conn; } diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index ad839e6..1b66709 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -780,7 +780,16 @@ execute test_where_pd(9); ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); -- Should fail with an error INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); -ERROR: failed to insert row +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +UPDATE test_numbers SET a = 11 WHERE a = 10; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +DELETE FROM test_numbers WHERE a = 10; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +SELECT * FROM test_numbers; +ERROR: could not connect to server mongo_server HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" -- Set correct address for mongo_server ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); @@ -792,7 +801,16 @@ DROP USER MAPPING FOR postgres SERVER mongo_server; CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); -- Should fail with an error INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); -ERROR: failed to insert row +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +UPDATE test_numbers SET a = 11 WHERE a = 10; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +DELETE FROM test_numbers WHERE a = 10; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +SELECT * FROM test_numbers; +ERROR: could not connect to server mongo_server HINT: Mongo error: "Authentication failed." -- Set default username, password for postgres user DROP USER MAPPING FOR postgres SERVER mongo_server; diff --git a/mongo_fdw.c b/mongo_fdw.c index a1c763f..8d5f569 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -733,6 +733,10 @@ MongoBeginForeignModify(ModifyTableState *mtstate, bool isvarlena = false; ListCell *lc; Oid foreignTableId; + Oid userid; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* * Do nothing in EXPLAIN (no ANALYZE) case. resultRelInfo->ri_FdwState @@ -742,6 +746,12 @@ MongoBeginForeignModify(ModifyTableState *mtstate, return; foreignTableId = RelationGetRelid(rel); + userid = GetUserId(); + + /* 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)); @@ -749,6 +759,13 @@ MongoBeginForeignModify(ModifyTableState *mtstate, 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; @@ -787,39 +804,17 @@ MongoExecForeignInsert(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options; - MONGO_CONN *mongoConnection; - Oid foreignTableId = InvalidOid; BSON *bsonDoc; Oid typoid; Datum value; bool isnull = false; - Oid userid; - ForeignServer *server; - UserMapping *user; - ForeignTable *table; MongoFdwModifyState *fmstate; fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - - userid = GetUserId(); - - /* Get info about foreign table. */ - table = GetForeignTable(RelationGetRelid(fmstate->rel)); - server = GetForeignServer(table->serverid); - user = GetUserMapping(userid, server->serverid); - - /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. - */ - options = fmstate->options; - mongoConnection = mongo_get_connection(server, user, options); bsonDoc = BsonCreate(); - typoid = get_atttype(foreignTableId, 1); + typoid = get_atttype(RelationGetRelid(resultRelInfo->ri_RelationDesc), 1); /* Get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) @@ -875,8 +870,8 @@ MongoExecForeignInsert(EState *estate, BsonFinish(bsonDoc); /* Now we are ready to insert tuple/document into MongoDB */ - MongoInsert(mongoConnection, options->svr_database, - options->collectionName, bsonDoc); + MongoInsert(fmstate->mongoConnection, fmstate->options->svr_database, + fmstate->options->collectionName, bsonDoc); BsonDestroy(bsonDoc); @@ -934,8 +929,6 @@ MongoExecForeignUpdate(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options; - MONGO_CONN *mongoConnection; Datum datum; bool isNull = false; Oid foreignTableId; @@ -944,29 +937,11 @@ MongoExecForeignUpdate(EState *estate, BSON *document; BSON *op = NULL; BSON set; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; MongoFdwModifyState *fmstate; fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* Resolve foreign table options; and connect to mongo server */ - options = fmstate->options; - - /* Get info about foreign table. */ - table = GetForeignTable(foreignTableId); - server = GetForeignServer(table->serverid); - user = GetUserMapping(userid, server->serverid); - - /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. - */ - mongoConnection = mongo_get_connection(server, user, options); - /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -1026,8 +1001,8 @@ MongoExecForeignUpdate(EState *estate, BsonFinish(op); /* We are ready to update the row into MongoDB */ - MongoUpdate(mongoConnection, options->svr_database, - options->collectionName, op, document); + MongoUpdate(fmstate->mongoConnection, fmstate->options->svr_database, + fmstate->options->collectionName, op, document); BsonDestroy(op); BsonDestroy(document); @@ -1046,38 +1021,18 @@ MongoExecForeignDelete(EState *estate, TupleTableSlot *slot, TupleTableSlot *planSlot) { - MongoFdwOptions *options; - MONGO_CONN *mongoConnection; Datum datum; bool isNull = false; Oid foreignTableId; char *columnName = NULL; Oid typoid; BSON *document; - Oid userid = GetUserId(); - ForeignServer *server; - UserMapping *user; - ForeignTable *table; MongoFdwModifyState *fmstate; fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); - /* Resolve foreign table options; and connect to mongo server */ - options = fmstate->options; - - /* Get info about foreign table. */ - table = GetForeignTable(foreignTableId); - server = GetForeignServer(table->serverid); - user = GetUserMapping(userid, server->serverid); - - /* - * Get connection to the foreign server. Connection manager will - * establish new connection if necessary. - */ - mongoConnection = mongo_get_connection(server, user, options); - /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); @@ -1098,8 +1053,8 @@ MongoExecForeignDelete(EState *estate, BsonFinish(document); /* Now we are ready to delete a single document from MongoDB */ - MongoDelete(mongoConnection, options->svr_database, - options->collectionName, document); + MongoDelete(fmstate->mongoConnection, fmstate->options->svr_database, + fmstate->options->collectionName, document); BsonDestroy(document); diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index 56eb166..fa9213d 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -141,6 +141,9 @@ execute test_where_pd(9); ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); -- Should fail with an error INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); +UPDATE test_numbers SET a = 11 WHERE a = 10; +DELETE FROM test_numbers WHERE a = 10; +SELECT * FROM test_numbers; -- Set correct address for mongo_server ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); -- Should able to insert the data @@ -152,6 +155,9 @@ DROP USER MAPPING FOR postgres SERVER mongo_server; CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); -- Should fail with an error INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); +UPDATE test_numbers SET a = 11 WHERE a = 10; +DELETE FROM test_numbers WHERE a = 10; +SELECT * FROM test_numbers; -- Set default username, password for postgres user DROP USER MAPPING FOR postgres SERVER mongo_server; CREATE USER MAPPING FOR postgres SERVER mongo_server; From 61f2bdb4d60682c77a76a4435e1119c3502df2ac Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 8 Jul 2020 09:53:17 +0530 Subject: [PATCH 131/239] Fix ANALYZE. Earlier it was giving an error for meta driver and crashing for the legacy driver. Fix those. MongoAcquireSampleRows() function was calling MongoBeginForeignScan() to get a few relevant details by passing fake scanState which was wrong. Change that entire logic so that the relevant details are fetched directly inside MongoAcquireSampleRows() itself. FDW-158, Jeevan Chalke, reviewed by Vaibhav Dalvi, an earlier version of the patch posted by Vaibhav Dalvi is reviewed by Suraj Kharage, but I have completely revised it. --- expected/mongo_fdw.out | 22 ++++++++++++++++++ mongo_fdw.c | 52 ++++++++++++++++++------------------------ sql/mongo_fdw.sql | 8 +++++++ 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out index 1b66709..32d0bf1 100644 --- a/expected/mongo_fdw.out +++ b/expected/mongo_fdw.out @@ -817,6 +817,28 @@ DROP USER MAPPING FOR postgres SERVER mongo_server; CREATE USER MAPPING FOR postgres SERVER mongo_server; -- Should able to insert the data INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); +-- FDW-158: Fix server crash when analyzing a foreign table. +SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; + reltuples +----------- + 0 +(1 row) + +ANALYZE test_numbers; +-- Should give correct number of rows now. +SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; + reltuples +----------- + 12 +(1 row) + +-- Check with this... +SELECT count(*) FROM test_numbers; + count +------- + 12 +(1 row) + DELETE FROM test_numbers; DROP FOREIGN TABLE test_numbers; DROP FOREIGN TABLE test_json; diff --git a/mongo_fdw.c b/mongo_fdw.c index 8d5f569..79e68e2 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1983,6 +1983,7 @@ MongoAcquireSampleRows(Relation relation, double *totalRowCount, double *totalDeadRowCount) { + MONGO_CONN *mongoConnection; int sampleRowCount = 0; double rowCount = 0; double rowCountToSkip = -1; /* -1 means not set yet */ @@ -1997,14 +1998,13 @@ MongoAcquireSampleRows(Relation relation, MONGO_CURSOR *mongoCursor; BSON *queryDocument; List *columnList = NIL; - ForeignScanState *scanState; - List *foreignPrivateList; - ForeignScan *foreignScan; - MongoFdwModifyState *fmstate; char *relationName; - int executorFlags = 0; MemoryContext oldContext = CurrentMemoryContext; MemoryContext tupleContext; + MongoFdwOptions *options; + ForeignServer *server; + UserMapping *user; + ForeignTable *table; /* Create list of columns in the relation */ tupleDescriptor = RelationGetDescr(relation); @@ -2029,27 +2029,23 @@ MongoAcquireSampleRows(Relation relation, columnList = lappend(columnList, column); } - /* Create state structure */ - scanState = makeNode(ForeignScanState); - scanState->ss.ss_currentRelation = relation; - foreignTableId = RelationGetRelid(relation); - queryDocument = QueryDocument(foreignTableId, NIL, NULL); - foreignPrivateList = list_make2(columnList, NULL); - - /* Only clean up the query struct, but not its data */ - BsonDestroy(queryDocument); - - foreignScan = makeNode(ForeignScan); - foreignScan->fdw_private = foreignPrivateList; - - scanState->ss.ps.plan = (Plan *) foreignScan; + table = GetForeignTable(foreignTableId); + server = GetForeignServer(table->serverid); + user = GetUserMapping(GetUserId(), server->serverid); + options = mongo_get_options(foreignTableId); - MongoBeginForeignScan(scanState, executorFlags); + /* + * Get connection to the foreign server. Connection manager will establish + * new connection if necessary. + */ + mongoConnection = mongo_get_connection(server, user, options); - fmstate = (MongoFdwModifyState *) scanState->fdw_state; - mongoCursor = fmstate->mongoCursor; - columnMappingHash = fmstate->columnMappingHash; + queryDocument = QueryDocument(foreignTableId, NIL, NULL); + /* Create cursor for collection name and set query */ + mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, + options->collectionName, queryDocument); + columnMappingHash = ColumnMappingHash(foreignTableId, columnList); /* * Use per-tuple memory context to prevent leak of memory used to read @@ -2102,23 +2098,17 @@ MongoAcquireSampleRows(Relation relation, bson_error_t error; if (mongoc_cursor_error(mongoCursor, &error)) - { - MongoFreeScanState(fmstate); ereport(ERROR, (errmsg("could not iterate over mongo collection"), errhint("Mongo driver error: %s", error.message))); - } #else mongo_cursor_error_t errorCode = mongoCursor->err; if (errorCode != MONGO_CURSOR_EXHAUSTED) - { - MongoFreeScanState(fmstate); ereport(ERROR, (errmsg("could not iterate over mongo collection"), errhint("Mongo driver cursor error code: %d", errorCode))); - } #endif break; } @@ -2167,9 +2157,11 @@ MongoAcquireSampleRows(Relation relation, rowCount += 1; } + /* Only clean up the query struct, but not its data */ + BsonDestroy(queryDocument); + /* Clean up */ MemoryContextDelete(tupleContext); - MongoFreeScanState(fmstate); pfree(columnValues); pfree(columnNulls); diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql index fa9213d..43fb133 100644 --- a/sql/mongo_fdw.sql +++ b/sql/mongo_fdw.sql @@ -164,6 +164,14 @@ CREATE USER MAPPING FOR postgres SERVER mongo_server; -- Should able to insert the data INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); +-- FDW-158: Fix server crash when analyzing a foreign table. +SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; +ANALYZE test_numbers; +-- Should give correct number of rows now. +SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; +-- Check with this... +SELECT count(*) FROM test_numbers; + DELETE FROM test_numbers; DROP FOREIGN TABLE test_numbers; From 08c62edd70929783bdcfdf1fdaaadba741e27571 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 22 Jul 2020 19:54:47 +0530 Subject: [PATCH 132/239] Split a single testcase file into multiple files. If the testcase fails, it becomes difficult to identify which area got broken. Having separate testcase files helps a bit. Also, added an ORDER BY clause at missing places to ensure unique output order. Along the way, through mongodb_init.sh importing a few data into the Mongo server which will be used in the testcases. FDW-143, Rajkumar Raghuwanshi, reviewed by Vaibhav Dalvi and Jeevan Chalke, few cosmetic changes by me. --- Makefile | 2 +- Makefile.legacy | 2 +- Makefile.meta | 2 +- data/mongo_fixture.json | 6 +- data/mongo_test_data.js | 38 ++ data/mongo_warehouse.json | 14 + expected/connection_validation.out | 76 +++ expected/connection_validation_1.out | 76 +++ expected/dml.out | 175 +++++ expected/mongo_fdw.out | 855 ------------------------ expected/pushdown.out | 326 +++++++++ expected/select.out | 962 +++++++++++++++++++++++++++ expected/select_1.out | 926 ++++++++++++++++++++++++++ expected/server_options.out | 86 +++ expected/server_options_1.out | 93 +++ mongodb_init.sh | 17 + sql/connection_validation.sql | 62 ++ sql/dml.sql | 90 +++ sql/mongo_fdw.sql | 189 ------ sql/pushdown.sql | 123 ++++ sql/select.sql | 243 +++++++ sql/server_options.sql | 57 ++ 22 files changed, 3370 insertions(+), 1050 deletions(-) create mode 100644 data/mongo_test_data.js create mode 100644 data/mongo_warehouse.json create mode 100644 expected/connection_validation.out create mode 100644 expected/connection_validation_1.out create mode 100644 expected/dml.out delete mode 100644 expected/mongo_fdw.out create mode 100644 expected/pushdown.out create mode 100644 expected/select.out create mode 100644 expected/select_1.out create mode 100644 expected/server_options.out create mode 100644 expected/server_options_1.out create mode 100755 mongodb_init.sh create mode 100644 sql/connection_validation.sql create mode 100644 sql/dml.sql delete mode 100644 sql/mongo_fdw.sql create mode 100644 sql/pushdown.sql create mode 100644 sql/select.sql create mode 100644 sql/server_options.sql diff --git a/Makefile b/Makefile index 0776b11..f2771eb 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_ EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql -REGRESS = mongo_fdw +REGRESS = server_options connection_validation dml select pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) $(MONGO_DRIVER)/%.os: diff --git a/Makefile.legacy b/Makefile.legacy index 0776b11..f2771eb 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -25,7 +25,7 @@ OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_ EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql -REGRESS = mongo_fdw +REGRESS = server_options connection_validation dml select pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) $(MONGO_DRIVER)/%.os: diff --git a/Makefile.meta b/Makefile.meta index 08513b4..3e1bf00 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o $(LI EXTENSION = mongo_fdw DATA = mongo_fdw--1.0.sql mongo_fdw--1.1.sql mongo_fdw--1.0--1.1.sql -REGRESS = mongo_fdw +REGRESS = server_options connection_validation dml select pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/data/mongo_fixture.json b/data/mongo_fixture.json index e54bdca..1804624 100644 --- a/data/mongo_fixture.json +++ b/data/mongo_fixture.json @@ -4,7 +4,7 @@ "population": 45590000, "capital": "Kyiv", "hdi": 0.74, - "lastElections": {"type": "presedential", "date": {"$date": 1400976000000}}, + "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"] @@ -14,7 +14,7 @@ "population": 38540000, "capital": "Warsaw", "hdi": 0.821, - "lastElections": {"type": "presedential", "date": {"$date": 1400976000000}}, + "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", @@ -27,6 +27,6 @@ "hdi": 0.66, "lastElections": {"type": "parliamentary", "date": {"$date": 1290902400000}}, "mainExports": ["Wine of fresh grapes, including fortified wines", - "Insulated (including enamelled or anodised) wire, cable", + "Insulated (including enameled or anodized) wire, cable", "Sunflower seeds, whether or not broken"] }] diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js new file mode 100644 index 0000000..6287dce --- /dev/null +++ b/data/mongo_test_data.js @@ -0,0 +1,38 @@ +// 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.mongo_test.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) }, +]); diff --git a/data/mongo_warehouse.json b/data/mongo_warehouse.json new file mode 100644 index 0000000..e8e4a38 --- /dev/null +++ b/data/mongo_warehouse.json @@ -0,0 +1,14 @@ +[ + { + "_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} + } +] diff --git a/expected/connection_validation.out b/expected/connection_validation.out new file mode 100644 index 0000000..0134ec8 --- /dev/null +++ b/expected/connection_validation.out @@ -0,0 +1,76 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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.5'); +-- Should fail with an error +INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED'); +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +DELETE FROM f_mongo_test WHERE a = 2; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +-- Set correct address for mongo_server +ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST); +-- 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 +HINT: Mongo error: "Authentication failed." +UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +DELETE FROM f_mongo_test WHERE a = 3; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; +ERROR: could not connect to server mongo_server +HINT: Mongo error: "Authentication failed." +-- 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; diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out new file mode 100644 index 0000000..b788466 --- /dev/null +++ b/expected/connection_validation_1.out @@ -0,0 +1,76 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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.5'); +-- Should fail with an error +INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED'); +ERROR: could not connect to 127.0.0.5:27017 +HINT: Mongo driver connection error: 2. +UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; +ERROR: could not connect to 127.0.0.5:27017 +HINT: Mongo driver connection error: 2. +DELETE FROM f_mongo_test WHERE a = 2; +ERROR: could not connect to 127.0.0.5:27017 +HINT: Mongo driver connection error: 2. +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; +ERROR: could not connect to 127.0.0.5:27017 +HINT: Mongo driver connection error: 2. +-- Set correct address for mongo_server +ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST); +-- 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 localhost:27017 +HINT: Mongo driver connection error: +UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3; +ERROR: could not connect to localhost:27017 +HINT: Mongo driver connection error: +DELETE FROM f_mongo_test WHERE a = 3; +ERROR: could not connect to localhost:27017 +HINT: Mongo driver connection error: +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; +ERROR: could not connect to localhost:27017 +HINT: Mongo driver connection error: +-- 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; diff --git a/expected/dml.out b/expected/dml.out new file mode 100644 index 0000000..81e0d91 --- /dev/null +++ b/expected/dml.out @@ -0,0 +1,175 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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'); +-- 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. +SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; + reltuples +----------- + 0 +(1 row) + +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 +-- 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/mongo_fdw.out b/expected/mongo_fdw.out deleted file mode 100644 index 32d0bf1..0000000 --- a/expected/mongo_fdw.out +++ /dev/null @@ -1,855 +0,0 @@ -\c postgres postgres -CREATE EXTENSION mongo_fdw; -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); -\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --maintainInsertionOrder --host='127.0.0.1' --port=27017 --quiet < data/mongo_fixture.json -CREATE USER MAPPING FOR postgres SERVER mongo_server; -CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); -CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); -INSERT INTO department SELECT 0, i, 'dept - ' || i FROM generate_series(1,10) i; -INSERT INTO employee SELECT 0, i, 'emp - ' || i, (i - 1)%10 + 1 FROM generate_series(1,100) i; -SELECT count(*) FROM department; - count -------- - 10 -(1 row) - -SELECT count(*) FROM employee; - count -------- - 100 -(1 row) - -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; - QUERY PLAN ----------------------------------------------------------- - Sort - Sort Key: e.emp_id - -> Merge Join - Merge Cond: (d.department_id = e.emp_dept_id) - -> Sort - Sort Key: d.department_id - -> Foreign Scan on department d - Foreign Namespace: testdb.department - -> Sort - Sort Key: e.emp_dept_id - -> Foreign Scan on employee e - Foreign Namespace: testdb.employee -(12 rows) - -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; - QUERY PLAN ----------------------------------------------------------------------- - Sort - Sort Key: e.emp_id - -> Merge Join - Merge Cond: (department.department_id = d.department_id) - -> Sort - Sort Key: department.department_id - -> Nested Loop - -> HashAggregate - Group Key: department.department_id - -> Foreign Scan on department - Foreign Namespace: testdb.department - -> Foreign Scan on employee e - Foreign Namespace: testdb.employee - -> Sort - Sort Key: d.department_id - -> Foreign Scan on department d - Foreign Namespace: testdb.department -(17 rows) - -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id AND e.emp_dept_id > 5 ORDER by emp_id, department_id; - emp_id | emp_name | emp_dept_id | department_id | department_name ---------+-----------+-------------+---------------+----------------- - 6 | emp - 6 | 6 | 6 | dept - 6 - 7 | emp - 7 | 7 | 7 | dept - 7 - 8 | emp - 8 | 8 | 8 | dept - 8 - 9 | emp - 9 | 9 | 9 | dept - 9 - 10 | emp - 10 | 10 | 10 | dept - 10 - 16 | emp - 16 | 6 | 6 | dept - 6 - 17 | emp - 17 | 7 | 7 | dept - 7 - 18 | emp - 18 | 8 | 8 | dept - 8 - 19 | emp - 19 | 9 | 9 | dept - 9 - 20 | emp - 20 | 10 | 10 | dept - 10 - 26 | emp - 26 | 6 | 6 | dept - 6 - 27 | emp - 27 | 7 | 7 | dept - 7 - 28 | emp - 28 | 8 | 8 | dept - 8 - 29 | emp - 29 | 9 | 9 | dept - 9 - 30 | emp - 30 | 10 | 10 | dept - 10 - 36 | emp - 36 | 6 | 6 | dept - 6 - 37 | emp - 37 | 7 | 7 | dept - 7 - 38 | emp - 38 | 8 | 8 | dept - 8 - 39 | emp - 39 | 9 | 9 | dept - 9 - 40 | emp - 40 | 10 | 10 | dept - 10 - 46 | emp - 46 | 6 | 6 | dept - 6 - 47 | emp - 47 | 7 | 7 | dept - 7 - 48 | emp - 48 | 8 | 8 | dept - 8 - 49 | emp - 49 | 9 | 9 | dept - 9 - 50 | emp - 50 | 10 | 10 | dept - 10 - 56 | emp - 56 | 6 | 6 | dept - 6 - 57 | emp - 57 | 7 | 7 | dept - 7 - 58 | emp - 58 | 8 | 8 | dept - 8 - 59 | emp - 59 | 9 | 9 | dept - 9 - 60 | emp - 60 | 10 | 10 | dept - 10 - 66 | emp - 66 | 6 | 6 | dept - 6 - 67 | emp - 67 | 7 | 7 | dept - 7 - 68 | emp - 68 | 8 | 8 | dept - 8 - 69 | emp - 69 | 9 | 9 | dept - 9 - 70 | emp - 70 | 10 | 10 | dept - 10 - 76 | emp - 76 | 6 | 6 | dept - 6 - 77 | emp - 77 | 7 | 7 | dept - 7 - 78 | emp - 78 | 8 | 8 | dept - 8 - 79 | emp - 79 | 9 | 9 | dept - 9 - 80 | emp - 80 | 10 | 10 | dept - 10 - 86 | emp - 86 | 6 | 6 | dept - 6 - 87 | emp - 87 | 7 | 7 | dept - 7 - 88 | emp - 88 | 8 | 8 | dept - 8 - 89 | emp - 89 | 9 | 9 | dept - 9 - 90 | emp - 90 | 10 | 10 | dept - 10 - 96 | emp - 96 | 6 | 6 | dept - 6 - 97 | emp - 97 | 7 | 7 | dept - 7 - 98 | emp - 98 | 8 | 8 | dept - 8 - 99 | emp - 99 | 9 | 9 | dept - 9 - 100 | emp - 100 | 10 | 10 | dept - 10 -(50 rows) - -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department WHERE department_id < 3) ORDER by emp_id, department_id; - emp_id | emp_name | emp_dept_id | department_id | department_name ---------+-----------+-------------+---------------+----------------- - 1 | emp - 1 | 1 | 1 | dept - 1 - 1 | emp - 1 | 1 | 2 | dept - 2 - 2 | emp - 2 | 2 | 1 | dept - 1 - 2 | emp - 2 | 2 | 2 | dept - 2 - 3 | emp - 3 | 3 | 1 | dept - 1 - 3 | emp - 3 | 3 | 2 | dept - 2 - 4 | emp - 4 | 4 | 1 | dept - 1 - 4 | emp - 4 | 4 | 2 | dept - 2 - 5 | emp - 5 | 5 | 1 | dept - 1 - 5 | emp - 5 | 5 | 2 | dept - 2 - 6 | emp - 6 | 6 | 1 | dept - 1 - 6 | emp - 6 | 6 | 2 | dept - 2 - 7 | emp - 7 | 7 | 1 | dept - 1 - 7 | emp - 7 | 7 | 2 | dept - 2 - 8 | emp - 8 | 8 | 1 | dept - 1 - 8 | emp - 8 | 8 | 2 | dept - 2 - 9 | emp - 9 | 9 | 1 | dept - 1 - 9 | emp - 9 | 9 | 2 | dept - 2 - 10 | emp - 10 | 10 | 1 | dept - 1 - 10 | emp - 10 | 10 | 2 | dept - 2 - 11 | emp - 11 | 1 | 1 | dept - 1 - 11 | emp - 11 | 1 | 2 | dept - 2 - 12 | emp - 12 | 2 | 1 | dept - 1 - 12 | emp - 12 | 2 | 2 | dept - 2 - 13 | emp - 13 | 3 | 1 | dept - 1 - 13 | emp - 13 | 3 | 2 | dept - 2 - 14 | emp - 14 | 4 | 1 | dept - 1 - 14 | emp - 14 | 4 | 2 | dept - 2 - 15 | emp - 15 | 5 | 1 | dept - 1 - 15 | emp - 15 | 5 | 2 | dept - 2 - 16 | emp - 16 | 6 | 1 | dept - 1 - 16 | emp - 16 | 6 | 2 | dept - 2 - 17 | emp - 17 | 7 | 1 | dept - 1 - 17 | emp - 17 | 7 | 2 | dept - 2 - 18 | emp - 18 | 8 | 1 | dept - 1 - 18 | emp - 18 | 8 | 2 | dept - 2 - 19 | emp - 19 | 9 | 1 | dept - 1 - 19 | emp - 19 | 9 | 2 | dept - 2 - 20 | emp - 20 | 10 | 1 | dept - 1 - 20 | emp - 20 | 10 | 2 | dept - 2 - 21 | emp - 21 | 1 | 1 | dept - 1 - 21 | emp - 21 | 1 | 2 | dept - 2 - 22 | emp - 22 | 2 | 1 | dept - 1 - 22 | emp - 22 | 2 | 2 | dept - 2 - 23 | emp - 23 | 3 | 1 | dept - 1 - 23 | emp - 23 | 3 | 2 | dept - 2 - 24 | emp - 24 | 4 | 1 | dept - 1 - 24 | emp - 24 | 4 | 2 | dept - 2 - 25 | emp - 25 | 5 | 1 | dept - 1 - 25 | emp - 25 | 5 | 2 | dept - 2 - 26 | emp - 26 | 6 | 1 | dept - 1 - 26 | emp - 26 | 6 | 2 | dept - 2 - 27 | emp - 27 | 7 | 1 | dept - 1 - 27 | emp - 27 | 7 | 2 | dept - 2 - 28 | emp - 28 | 8 | 1 | dept - 1 - 28 | emp - 28 | 8 | 2 | dept - 2 - 29 | emp - 29 | 9 | 1 | dept - 1 - 29 | emp - 29 | 9 | 2 | dept - 2 - 30 | emp - 30 | 10 | 1 | dept - 1 - 30 | emp - 30 | 10 | 2 | dept - 2 - 31 | emp - 31 | 1 | 1 | dept - 1 - 31 | emp - 31 | 1 | 2 | dept - 2 - 32 | emp - 32 | 2 | 1 | dept - 1 - 32 | emp - 32 | 2 | 2 | dept - 2 - 33 | emp - 33 | 3 | 1 | dept - 1 - 33 | emp - 33 | 3 | 2 | dept - 2 - 34 | emp - 34 | 4 | 1 | dept - 1 - 34 | emp - 34 | 4 | 2 | dept - 2 - 35 | emp - 35 | 5 | 1 | dept - 1 - 35 | emp - 35 | 5 | 2 | dept - 2 - 36 | emp - 36 | 6 | 1 | dept - 1 - 36 | emp - 36 | 6 | 2 | dept - 2 - 37 | emp - 37 | 7 | 1 | dept - 1 - 37 | emp - 37 | 7 | 2 | dept - 2 - 38 | emp - 38 | 8 | 1 | dept - 1 - 38 | emp - 38 | 8 | 2 | dept - 2 - 39 | emp - 39 | 9 | 1 | dept - 1 - 39 | emp - 39 | 9 | 2 | dept - 2 - 40 | emp - 40 | 10 | 1 | dept - 1 - 40 | emp - 40 | 10 | 2 | dept - 2 - 41 | emp - 41 | 1 | 1 | dept - 1 - 41 | emp - 41 | 1 | 2 | dept - 2 - 42 | emp - 42 | 2 | 1 | dept - 1 - 42 | emp - 42 | 2 | 2 | dept - 2 - 43 | emp - 43 | 3 | 1 | dept - 1 - 43 | emp - 43 | 3 | 2 | dept - 2 - 44 | emp - 44 | 4 | 1 | dept - 1 - 44 | emp - 44 | 4 | 2 | dept - 2 - 45 | emp - 45 | 5 | 1 | dept - 1 - 45 | emp - 45 | 5 | 2 | dept - 2 - 46 | emp - 46 | 6 | 1 | dept - 1 - 46 | emp - 46 | 6 | 2 | dept - 2 - 47 | emp - 47 | 7 | 1 | dept - 1 - 47 | emp - 47 | 7 | 2 | dept - 2 - 48 | emp - 48 | 8 | 1 | dept - 1 - 48 | emp - 48 | 8 | 2 | dept - 2 - 49 | emp - 49 | 9 | 1 | dept - 1 - 49 | emp - 49 | 9 | 2 | dept - 2 - 50 | emp - 50 | 10 | 1 | dept - 1 - 50 | emp - 50 | 10 | 2 | dept - 2 - 51 | emp - 51 | 1 | 1 | dept - 1 - 51 | emp - 51 | 1 | 2 | dept - 2 - 52 | emp - 52 | 2 | 1 | dept - 1 - 52 | emp - 52 | 2 | 2 | dept - 2 - 53 | emp - 53 | 3 | 1 | dept - 1 - 53 | emp - 53 | 3 | 2 | dept - 2 - 54 | emp - 54 | 4 | 1 | dept - 1 - 54 | emp - 54 | 4 | 2 | dept - 2 - 55 | emp - 55 | 5 | 1 | dept - 1 - 55 | emp - 55 | 5 | 2 | dept - 2 - 56 | emp - 56 | 6 | 1 | dept - 1 - 56 | emp - 56 | 6 | 2 | dept - 2 - 57 | emp - 57 | 7 | 1 | dept - 1 - 57 | emp - 57 | 7 | 2 | dept - 2 - 58 | emp - 58 | 8 | 1 | dept - 1 - 58 | emp - 58 | 8 | 2 | dept - 2 - 59 | emp - 59 | 9 | 1 | dept - 1 - 59 | emp - 59 | 9 | 2 | dept - 2 - 60 | emp - 60 | 10 | 1 | dept - 1 - 60 | emp - 60 | 10 | 2 | dept - 2 - 61 | emp - 61 | 1 | 1 | dept - 1 - 61 | emp - 61 | 1 | 2 | dept - 2 - 62 | emp - 62 | 2 | 1 | dept - 1 - 62 | emp - 62 | 2 | 2 | dept - 2 - 63 | emp - 63 | 3 | 1 | dept - 1 - 63 | emp - 63 | 3 | 2 | dept - 2 - 64 | emp - 64 | 4 | 1 | dept - 1 - 64 | emp - 64 | 4 | 2 | dept - 2 - 65 | emp - 65 | 5 | 1 | dept - 1 - 65 | emp - 65 | 5 | 2 | dept - 2 - 66 | emp - 66 | 6 | 1 | dept - 1 - 66 | emp - 66 | 6 | 2 | dept - 2 - 67 | emp - 67 | 7 | 1 | dept - 1 - 67 | emp - 67 | 7 | 2 | dept - 2 - 68 | emp - 68 | 8 | 1 | dept - 1 - 68 | emp - 68 | 8 | 2 | dept - 2 - 69 | emp - 69 | 9 | 1 | dept - 1 - 69 | emp - 69 | 9 | 2 | dept - 2 - 70 | emp - 70 | 10 | 1 | dept - 1 - 70 | emp - 70 | 10 | 2 | dept - 2 - 71 | emp - 71 | 1 | 1 | dept - 1 - 71 | emp - 71 | 1 | 2 | dept - 2 - 72 | emp - 72 | 2 | 1 | dept - 1 - 72 | emp - 72 | 2 | 2 | dept - 2 - 73 | emp - 73 | 3 | 1 | dept - 1 - 73 | emp - 73 | 3 | 2 | dept - 2 - 74 | emp - 74 | 4 | 1 | dept - 1 - 74 | emp - 74 | 4 | 2 | dept - 2 - 75 | emp - 75 | 5 | 1 | dept - 1 - 75 | emp - 75 | 5 | 2 | dept - 2 - 76 | emp - 76 | 6 | 1 | dept - 1 - 76 | emp - 76 | 6 | 2 | dept - 2 - 77 | emp - 77 | 7 | 1 | dept - 1 - 77 | emp - 77 | 7 | 2 | dept - 2 - 78 | emp - 78 | 8 | 1 | dept - 1 - 78 | emp - 78 | 8 | 2 | dept - 2 - 79 | emp - 79 | 9 | 1 | dept - 1 - 79 | emp - 79 | 9 | 2 | dept - 2 - 80 | emp - 80 | 10 | 1 | dept - 1 - 80 | emp - 80 | 10 | 2 | dept - 2 - 81 | emp - 81 | 1 | 1 | dept - 1 - 81 | emp - 81 | 1 | 2 | dept - 2 - 82 | emp - 82 | 2 | 1 | dept - 1 - 82 | emp - 82 | 2 | 2 | dept - 2 - 83 | emp - 83 | 3 | 1 | dept - 1 - 83 | emp - 83 | 3 | 2 | dept - 2 - 84 | emp - 84 | 4 | 1 | dept - 1 - 84 | emp - 84 | 4 | 2 | dept - 2 - 85 | emp - 85 | 5 | 1 | dept - 1 - 85 | emp - 85 | 5 | 2 | dept - 2 - 86 | emp - 86 | 6 | 1 | dept - 1 - 86 | emp - 86 | 6 | 2 | dept - 2 - 87 | emp - 87 | 7 | 1 | dept - 1 - 87 | emp - 87 | 7 | 2 | dept - 2 - 88 | emp - 88 | 8 | 1 | dept - 1 - 88 | emp - 88 | 8 | 2 | dept - 2 - 89 | emp - 89 | 9 | 1 | dept - 1 - 89 | emp - 89 | 9 | 2 | dept - 2 - 90 | emp - 90 | 10 | 1 | dept - 1 - 90 | emp - 90 | 10 | 2 | dept - 2 - 91 | emp - 91 | 1 | 1 | dept - 1 - 91 | emp - 91 | 1 | 2 | dept - 2 - 92 | emp - 92 | 2 | 1 | dept - 1 - 92 | emp - 92 | 2 | 2 | dept - 2 - 93 | emp - 93 | 3 | 1 | dept - 1 - 93 | emp - 93 | 3 | 2 | dept - 2 - 94 | emp - 94 | 4 | 1 | dept - 1 - 94 | emp - 94 | 4 | 2 | dept - 2 - 95 | emp - 95 | 5 | 1 | dept - 1 - 95 | emp - 95 | 5 | 2 | dept - 2 - 96 | emp - 96 | 6 | 1 | dept - 1 - 96 | emp - 96 | 6 | 2 | dept - 2 - 97 | emp - 97 | 7 | 1 | dept - 1 - 97 | emp - 97 | 7 | 2 | dept - 2 - 98 | emp - 98 | 8 | 1 | dept - 1 - 98 | emp - 98 | 8 | 2 | dept - 2 - 99 | emp - 99 | 9 | 1 | dept - 1 - 99 | emp - 99 | 9 | 2 | dept - 2 - 100 | emp - 100 | 10 | 1 | dept - 1 - 100 | emp - 100 | 10 | 2 | dept - 2 -(200 rows) - -DELETE FROM employee WHERE emp_id = 10; -UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; -SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp' ORDER BY emp_id; - emp_id | emp_name ---------+------------- - 20 | Updated emp -(1 row) - -SELECT emp_id , emp_name , emp_dept_id FROM employee ORDER by emp_id LIMIT 10; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 1 | emp - 1 | 1 - 2 | emp - 2 | 2 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 - 6 | emp - 6 | 6 - 7 | emp - 7 | 7 - 8 | emp - 8 | 8 - 9 | emp - 9 | 9 - 11 | emp - 11 | 1 -(10 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1) ORDER by emp_id; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 1 | emp - 1 | 1 -(1 row) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5) ORDER by emp_id; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 1 | emp - 1 | 1 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 -(4 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000) ORDER by emp_id; - emp_id | emp_name | emp_dept_id ---------+----------+------------- -(0 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) ORDER by emp_id LIMIT 5; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 2 | emp - 2 | 2 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 - 6 | emp - 6 | 6 -(5 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) ORDER by emp_id LIMIT 5; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 2 | emp - 2 | 2 - 6 | emp - 6 | 6 - 7 | emp - 7 | 7 - 8 | emp - 8 | 8 - 9 | emp - 9 | 9 -(5 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) ORDER by emp_id LIMIT 5; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 1 | emp - 1 | 1 - 2 | emp - 2 | 2 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 -(5 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)) ORDER by emp_id; - emp_id | emp_name | emp_dept_id ---------+-------------+------------- - 2 | emp - 2 | 2 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 - 6 | emp - 6 | 6 - 7 | emp - 7 | 7 - 8 | emp - 8 | 8 - 9 | emp - 9 | 9 - 11 | emp - 11 | 1 - 12 | emp - 12 | 2 - 13 | emp - 13 | 3 - 14 | emp - 14 | 4 - 15 | emp - 15 | 5 - 16 | emp - 16 | 6 - 17 | emp - 17 | 7 - 18 | emp - 18 | 8 - 19 | emp - 19 | 9 - 20 | Updated emp | 10 - 21 | emp - 21 | 1 - 22 | emp - 22 | 2 - 23 | emp - 23 | 3 - 24 | emp - 24 | 4 - 25 | emp - 25 | 5 - 26 | emp - 26 | 6 - 27 | emp - 27 | 7 - 28 | emp - 28 | 8 - 29 | emp - 29 | 9 - 30 | emp - 30 | 10 - 31 | emp - 31 | 1 - 32 | emp - 32 | 2 - 33 | emp - 33 | 3 - 34 | emp - 34 | 4 - 35 | emp - 35 | 5 - 36 | emp - 36 | 6 - 37 | emp - 37 | 7 - 38 | emp - 38 | 8 - 39 | emp - 39 | 9 - 40 | emp - 40 | 10 - 41 | emp - 41 | 1 - 42 | emp - 42 | 2 - 43 | emp - 43 | 3 - 44 | emp - 44 | 4 - 45 | emp - 45 | 5 - 46 | emp - 46 | 6 - 47 | emp - 47 | 7 - 48 | emp - 48 | 8 - 49 | emp - 49 | 9 - 50 | emp - 50 | 10 - 51 | emp - 51 | 1 - 52 | emp - 52 | 2 - 53 | emp - 53 | 3 - 54 | emp - 54 | 4 - 55 | emp - 55 | 5 - 56 | emp - 56 | 6 - 57 | emp - 57 | 7 - 58 | emp - 58 | 8 - 59 | emp - 59 | 9 - 60 | emp - 60 | 10 - 61 | emp - 61 | 1 - 62 | emp - 62 | 2 - 63 | emp - 63 | 3 - 64 | emp - 64 | 4 - 65 | emp - 65 | 5 - 66 | emp - 66 | 6 - 67 | emp - 67 | 7 - 68 | emp - 68 | 8 - 69 | emp - 69 | 9 - 70 | emp - 70 | 10 - 71 | emp - 71 | 1 - 72 | emp - 72 | 2 - 73 | emp - 73 | 3 - 74 | emp - 74 | 4 - 75 | emp - 75 | 5 - 76 | emp - 76 | 6 - 77 | emp - 77 | 7 - 78 | emp - 78 | 8 - 79 | emp - 79 | 9 - 80 | emp - 80 | 10 - 81 | emp - 81 | 1 - 82 | emp - 82 | 2 - 83 | emp - 83 | 3 - 84 | emp - 84 | 4 - 85 | emp - 85 | 5 - 86 | emp - 86 | 6 - 87 | emp - 87 | 7 - 88 | emp - 88 | 8 - 89 | emp - 89 | 9 - 90 | emp - 90 | 10 - 91 | emp - 91 | 1 - 92 | emp - 92 | 2 - 93 | emp - 93 | 3 - 94 | emp - 94 | 4 - 95 | emp - 95 | 5 - 96 | emp - 96 | 6 - 97 | emp - 97 | 7 - 98 | emp - 98 | 8 - 99 | emp - 99 | 9 - 100 | emp - 100 | 10 -(98 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') ORDER by emp_id LIMIT 5; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 - 6 | emp - 6 | 6 - 7 | emp - 7 | 7 -(5 rows) - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') ORDER by emp_id LIMIT 5; - emp_id | emp_name | emp_dept_id ---------+----------+------------- - 1 | emp - 1 | 1 - 2 | emp - 2 | 2 - 3 | emp - 3 | 3 - 4 | emp - 4 | 4 - 5 | emp - 5 | 5 -(5 rows) - -DELETE FROM employee; -DELETE FROM department; -CREATE FOREIGN TABLE countries ( -_id NAME, -name VARCHAR, -population INTEGER, -capital VARCHAR, -hdi FLOAT -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -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) - --- --- Subfields and dates -CREATE FOREIGN TABLE country_elections ( -_id NAME, -"lastElections.type" VARCHAR, -"lastElections.date" TIMESTAMP -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM country_elections ORDER BY _id; - _id | lastElections.type | lastElections.date ---------------------------+--------------------+-------------------------- - 5381ccf9d6d81c8e8bf0434f | presedential | Sun May 25 00:00:00 2014 - 5381ccf9d6d81c8e8bf04350 | parliamentary | Sun Oct 09 00:00:00 2011 - 5381ccf9d6d81c8e8bf04351 | parliamentary | Sun Nov 28 00:00:00 2010 -(3 rows) - --- --- Arrays -CREATE FOREIGN TABLE main_exports ( -_id NAME, -"mainExports" TEXT[] -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -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 enamelled or anodised) wire, cable","Sunflower seeds, whether or not broken"} -(3 rows) - --- __doc tests --- the collection warehouse must contain the following data --- use testdb; --- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b90545859"),"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); --- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b9054585a"),"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); -CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -SELECT * FROM test_json ORDER BY __doc::text COLLATE "C"; - __doc ---------------------------------------------------------------------------------------------------------------------------------------------------------- - { "_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 } } -(2 rows) - -SELECT * FROM test_jsonb ORDER BY __doc::text COLLATE "C"; - __doc ---------------------------------------------------------------------------------------------------------------------------------------------- - {"_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}} -(2 rows) - -SELECT * FROM test_text ORDER BY __doc::text COLLATE "C"; - __doc ---------------------------------------------------------------------------------------------------------------------------------------------------------- - { "_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 } } -(2 rows) - -SELECT * FROM test_varchar ORDER BY __doc::text COLLATE "C"; - __doc ---------------------------------------------------------------------------------------------------------------------------------------------------------- - { "_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 } } -(2 rows) - --- where clause push down test -CREATE FOREIGN TABLE test_numbers(_id NAME, a int, b text) SERVER mongo_server OPTIONS (database 'testdb', collection 'test_numbers'); -insert into test_numbers values('1', 1, 'One'); -insert into test_numbers values('2', 2, 'Two'); -insert into test_numbers values('3', 3, 'Three'); -insert into test_numbers values('4', 4, 'Four'); -insert into test_numbers values('5', 5, 'Five'); -insert into test_numbers values('6', 6, 'Six'); -insert into test_numbers values('7', 7, 'Seven'); -insert into test_numbers values('8', 8, 'Eight'); -insert into test_numbers values('9', 9, 'Nine'); -insert into test_numbers values('10', 10, 'Ten'); -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 test_numbers where a=x; - raise notice 'Found Item %', n; - end loop; - return; -END -$$ LANGUAGE plpgsql; -SELECT test_param_where(); -NOTICE: Found Item One -NOTICE: Found Item Two -NOTICE: Found Item Three -NOTICE: Found Item Four -NOTICE: Found Item Five -NOTICE: Found Item Six -NOTICE: Found Item Seven -NOTICE: Found Item Eight -NOTICE: Found Item Nine - test_param_where ------------------- - -(1 row) - -PREPARE test_where_pd(int) as SELECT b FROM test_numbers WHERE a =$1; -explain (verbose, costs false) execute test_where_pd(1); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = 1) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(2); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = 2) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(3); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = 3) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(4); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = 4) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(5); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = 5) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(6); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = $1) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(7); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = $1) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(8); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = $1) - Foreign Namespace: testdb.test_numbers -(4 rows) - -explain (verbose, costs false) execute test_where_pd(9); - QUERY PLAN ------------------------------------------- - Foreign Scan on public.test_numbers - Output: b - Filter: (test_numbers.a = $1) - Foreign Namespace: testdb.test_numbers -(4 rows) - -execute test_where_pd(1); - b ------ - One -(1 row) - -execute test_where_pd(2); - b ------ - Two -(1 row) - -execute test_where_pd(3); - b -------- - Three -(1 row) - -execute test_where_pd(4); - b ------- - Four -(1 row) - -execute test_where_pd(5); - b ------- - Five -(1 row) - -execute test_where_pd(6); - b ------ - Six -(1 row) - -execute test_where_pd(7); - b -------- - Seven -(1 row) - -execute test_where_pd(8); - b -------- - Eight -(1 row) - -execute test_where_pd(9); - b ------- - Nine -(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.5'); --- Should fail with an error -INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); -ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" -UPDATE test_numbers SET a = 11 WHERE a = 10; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" -DELETE FROM test_numbers WHERE a = 10; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" -SELECT * FROM test_numbers; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" --- Set correct address for mongo_server -ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); --- Should able to insert the data -INSERT INTO test_numbers VALUES ('12', 12, 'Twelve'); --- Change the user mapping options --- Set wrong username, password for postgres user -DROP USER MAPPING FOR postgres SERVER mongo_server; -CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); --- Should fail with an error -INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); -ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." -UPDATE test_numbers SET a = 11 WHERE a = 10; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." -DELETE FROM test_numbers WHERE a = 10; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." -SELECT * FROM test_numbers; -ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." --- Set default username, password for postgres user -DROP USER MAPPING FOR postgres SERVER mongo_server; -CREATE USER MAPPING FOR postgres SERVER mongo_server; --- Should able to insert the data -INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); --- FDW-158: Fix server crash when analyzing a foreign table. -SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; - reltuples ------------ - 0 -(1 row) - -ANALYZE test_numbers; --- Should give correct number of rows now. -SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; - reltuples ------------ - 12 -(1 row) - --- Check with this... -SELECT count(*) FROM test_numbers; - count -------- - 12 -(1 row) - -DELETE FROM test_numbers; -DROP FOREIGN TABLE test_numbers; -DROP FOREIGN TABLE test_json; -DROP FOREIGN TABLE test_jsonb; -DROP FOREIGN TABLE test_text; -DROP FOREIGN TABLE test_varchar; -DROP FOREIGN TABLE department; -DROP FOREIGN TABLE employee; -DROP FOREIGN TABLE countries; -DROP FOREIGN TABLE country_elections; -DROP FOREIGN TABLE main_exports; -DROP USER MAPPING FOR postgres SERVER mongo_server; -DROP EXTENSION mongo_fdw CASCADE; -NOTICE: drop cascades to server mongo_server diff --git a/expected/pushdown.out b/expected/pushdown.out new file mode 100644 index 0000000..ba49a3f --- /dev/null +++ b/expected/pushdown.out @@ -0,0 +1,326 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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'); +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; + 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.c6 = ANY ('{1600,2450}'::numeric[])) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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; + QUERY PLAN +-------------------------------------------------------- + Sort + Output: c1, c2, c6 + Sort Key: e.c1 + -> Foreign Scan on public.f_test_tbl1 e + Output: c1, c2, c6 + Filter: (e.c6 > '3000'::numeric) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +SELECT c1, c2, c6 FROM f_test_tbl1 e + WHERE c6 > 3000 + ORDER BY c1; + 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; + 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.c6 = '1500'::numeric) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 = 1500 + ORDER BY c1; + 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; + 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.c6 >= '1000'::numeric) AND (e.c6 <= '4000'::numeric)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 BETWEEN 1000 AND 4000 + ORDER BY c1; + 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; + QUERY PLAN +-------------------------------------------------------- + Sort + Output: c1, c2, c5 + Sort Key: e.c1 + -> Foreign Scan on public.f_test_tbl1 e + Output: c1, c2, c5 + Filter: (e.c5 <= '1980-12-17'::date) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +SELECT c1, c2, c5 FROM f_test_tbl1 e + WHERE c5 <= '1980-12-17' + ORDER BY c1; + 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) + +-- Pushdown in prepared statement. +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'); +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 + Filter: (f_mongo_test.a = 1) + Foreign Namespace: mongo_fdw_regress.mongo_test +(7 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 + Filter: (f_mongo_test.a = 2) + Foreign Namespace: mongo_fdw_regress.mongo_test +(7 rows) + +EXECUTE pre_stmt_f_mongo_test(2); + b +----- + Two +(1 row) + +-- Cleanup +DELETE FROM f_mongo_test WHERE a != 0; +DROP FOREIGN TABLE f_mongo_test; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/select.out b/expected/select.out new file mode 100644 index 0000000..9f1fbd0 --- /dev/null +++ b/expected/select.out @@ -0,0 +1,962 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress & +-- mongo_fdw_regress1 databases on MongoDB with all permission for +-- 'edb' user with 'edb' 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 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'); +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) + +-- Cleanup +DELETE FROM f_mongo_test WHERE a != 0; +DROP TABLE l_test_tbl1; +DROP TABLE l_test_tbl2; +DROP VIEW smpl_vw; +DROP VIEW comp_vw; +DROP VIEW temp_vw; +DROP VIEW mul_tbl_view; +DROP FUNCTION 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/select_1.out b/expected/select_1.out new file mode 100644 index 0000000..38a231d --- /dev/null +++ b/expected/select_1.out @@ -0,0 +1,926 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress & +-- mongo_fdw_regress1 databases on MongoDB with all permission for +-- 'edb' user with 'edb' 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 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'); +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"; +ERROR: full document retrival only available in MongoC meta driver +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"; +ERROR: full document retrival only available in MongoC meta driver +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"; +ERROR: full document retrival only available in MongoC meta driver +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"; +ERROR: full document retrival only available in MongoC meta driver +-- 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) + +-- Cleanup +DELETE FROM f_mongo_test WHERE a != 0; +DROP TABLE l_test_tbl1; +DROP TABLE l_test_tbl2; +DROP VIEW smpl_vw; +DROP VIEW comp_vw; +DROP VIEW temp_vw; +DROP VIEW mul_tbl_view; +DROP FUNCTION 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/server_options.out b/expected/server_options.out new file mode 100644 index 0000000..a8835bf --- /dev/null +++ b/expected/server_options.out @@ -0,0 +1,86 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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; +-- Validate extension, server and mapping details +SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" + 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; + Extension | Server | Server_Options | User_Mapping_Options +-----------+--------------+--------------------------------+---------------------- + mongo_fdw | mongo_server | {address=localhost,port=27017} | +(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) + +DROP FOREIGN TABLE f_mongo_test; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +-- Create server with authentication_database option +-- authentication_database options is not supported with legacy driver +-- so below queries will fail when compiled with legacy driver. +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw + OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); +CREATE USER MAPPING FOR public SERVER mongo_server + OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); +CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); +-- 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) + +-- Cleanup +DROP FOREIGN TABLE f_mongo_test; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/server_options_1.out b/expected/server_options_1.out new file mode 100644 index 0000000..9e6b779 --- /dev/null +++ b/expected/server_options_1.out @@ -0,0 +1,93 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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; +-- Validate extension, server and mapping details +SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" + 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; + Extension | Server | Server_Options | User_Mapping_Options +-----------+--------------+--------------------------------+---------------------- + mongo_fdw | mongo_server | {address=localhost,port=27017} | +(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) + +DROP FOREIGN TABLE f_mongo_test; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +-- Create server with authentication_database option +-- authentication_database options is not supported with legacy driver +-- so below queries will fail when compiled with legacy driver. +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw + OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); +ERROR: invalid option "authentication_database" +HINT: Valid options in this context are: address, port. +CREATE USER MAPPING FOR public SERVER mongo_server + OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); +ERROR: server "mongo_server" does not exist +CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); +ERROR: server "mongo_server" does not exist +-- 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: relation "f_mongo_test" does not exist +LINE 1: 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'); +ERROR: server "mongo_server" does not exist +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; +ERROR: relation "f_mongo_test" does not exist +LINE 1: SELECT a, b FROM f_mongo_test ORDER BY 1, 2; + ^ +-- Cleanup +DROP FOREIGN TABLE f_mongo_test; +ERROR: foreign table "f_mongo_test" does not exist +DROP USER MAPPING FOR public SERVER mongo_server; +ERROR: server "mongo_server" does not exist +DROP SERVER mongo_server; +ERROR: server "mongo_server" does not exist +DROP EXTENSION mongo_fdw; diff --git a/mongodb_init.sh b/mongodb_init.sh new file mode 100755 index 0000000..46d6b07 --- /dev/null +++ b/mongodb_init.sh @@ -0,0 +1,17 @@ +#!/bin/sh +MONGO_HOST="127.0.0.1" +MONGO_PORT="27017" +MONGO_USER_NAME="edb" +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"}]}) + +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 +mongo --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --authenticationDatabase "mongo_fdw_regress" < data/mongo_test_data.js > /dev/null diff --git a/sql/connection_validation.sql b/sql/connection_validation.sql new file mode 100644 index 0000000..821d8b0 --- /dev/null +++ b/sql/connection_validation.sql @@ -0,0 +1,62 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' + +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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.5'); +-- 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); +-- 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; diff --git a/sql/dml.sql b/sql/dml.sql new file mode 100644 index 0000000..22424a0 --- /dev/null +++ b/sql/dml.sql @@ -0,0 +1,90 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' + +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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'); + +-- 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. +SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; +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; + +-- 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/sql/mongo_fdw.sql b/sql/mongo_fdw.sql deleted file mode 100644 index 43fb133..0000000 --- a/sql/mongo_fdw.sql +++ /dev/null @@ -1,189 +0,0 @@ -\c postgres postgres -CREATE EXTENSION mongo_fdw; -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw OPTIONS (address '127.0.0.1', port '27017'); -\! mongoimport --db mongo_fdw_regress --collection countries --jsonArray --drop --maintainInsertionOrder --host='127.0.0.1' --port=27017 --quiet < data/mongo_fixture.json -CREATE USER MAPPING FOR postgres SERVER mongo_server; - -CREATE FOREIGN TABLE department(_id NAME, department_id int, department_name text) SERVER mongo_server OPTIONS(database 'testdb', collection 'department'); -CREATE FOREIGN TABLE employee(_id NAME, emp_id int, emp_name text, emp_dept_id int) SERVER mongo_server OPTIONS(database 'testdb', collection 'employee'); - -INSERT INTO department SELECT 0, i, 'dept - ' || i FROM generate_series(1,10) i; -INSERT INTO employee SELECT 0, i, 'emp - ' || i, (i - 1)%10 + 1 FROM generate_series(1,100) i; - -SELECT count(*) FROM department; -SELECT count(*) FROM employee; - -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id ORDER by emp_id; - -EXPLAIN (COSTS FALSE) SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department) ORDER by emp_id; - -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id = e.emp_dept_id AND e.emp_dept_id > 5 ORDER by emp_id, department_id; -SELECT emp_id , emp_name , emp_dept_id, department_id , department_name FROM department d, employee e WHERE d.department_id IN (SELECT department_id FROM department WHERE department_id < 3) ORDER by emp_id, department_id; - -DELETE FROM employee WHERE emp_id = 10; - -UPDATE employee SET emp_name = 'Updated emp' WHERE emp_id = 20; -SELECT emp_id, emp_name FROM employee WHERE emp_name like 'Updated emp' ORDER BY emp_id; - -SELECT emp_id , emp_name , emp_dept_id FROM employee ORDER by emp_id LIMIT 10; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (1,3,4,5) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id IN (10000,1000) ORDER by emp_id; - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1) ORDER by emp_id LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (1,3,4,5) ORDER by emp_id LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (10000,1000) ORDER by emp_id LIMIT 5; - -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_id NOT IN (SELECT emp_id FROM employee WHERE emp_id IN (1,10)) ORDER by emp_id; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 1', 'emp - 2') ORDER by emp_id LIMIT 5; -SELECT emp_id , emp_name , emp_dept_id FROM employee WHERE emp_name NOT IN ('emp - 10') ORDER by emp_id LIMIT 5; - -DELETE FROM employee; -DELETE FROM department; - -CREATE FOREIGN TABLE countries ( -_id NAME, -name VARCHAR, -population INTEGER, -capital VARCHAR, -hdi FLOAT -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM countries ORDER BY _id; --- --- Subfields and dates -CREATE FOREIGN TABLE country_elections ( -_id NAME, -"lastElections.type" VARCHAR, -"lastElections.date" TIMESTAMP -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM country_elections ORDER BY _id; --- --- Arrays -CREATE FOREIGN TABLE main_exports ( -_id NAME, -"mainExports" TEXT[] -) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'countries'); -SELECT * FROM main_exports ORDER BY _id; - --- __doc tests - --- the collection warehouse must contain the following data --- use testdb; --- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b90545859"),"warehouse_id" : NumberInt(1),"warehouse_name" : "UPS","warehouse_created" : ISODate("2014-12-12T07:12:10Z")}); --- db.warehouse.insert ({"_id" : ObjectId("58a1ebbaf543ec0b9054585a"),"warehouse_id" : NumberInt(2),"warehouse_name" : "Laptop","warehouse_created" : ISODate("2015-11-11T08:13:10Z")}); - - -CREATE FOREIGN TABLE test_json(__doc json) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_jsonb(__doc jsonb) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_text(__doc text) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); -CREATE FOREIGN TABLE test_varchar(__doc varchar) SERVER mongo_server OPTIONS (database 'testdb', collection 'warehouse'); - -SELECT * FROM test_json ORDER BY __doc::text COLLATE "C"; -SELECT * FROM test_jsonb ORDER BY __doc::text COLLATE "C"; -SELECT * FROM test_text ORDER BY __doc::text COLLATE "C"; -SELECT * FROM test_varchar ORDER BY __doc::text COLLATE "C"; - --- where clause push down test -CREATE FOREIGN TABLE test_numbers(_id NAME, a int, b text) SERVER mongo_server OPTIONS (database 'testdb', collection 'test_numbers'); -insert into test_numbers values('1', 1, 'One'); -insert into test_numbers values('2', 2, 'Two'); -insert into test_numbers values('3', 3, 'Three'); -insert into test_numbers values('4', 4, 'Four'); -insert into test_numbers values('5', 5, 'Five'); -insert into test_numbers values('6', 6, 'Six'); -insert into test_numbers values('7', 7, 'Seven'); -insert into test_numbers values('8', 8, 'Eight'); -insert into test_numbers values('9', 9, 'Nine'); -insert into test_numbers values('10', 10, 'Ten'); - -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 test_numbers where a=x; - raise notice 'Found Item %', n; - end loop; - return; -END -$$ LANGUAGE plpgsql; - -SELECT test_param_where(); - -PREPARE test_where_pd(int) as SELECT b FROM test_numbers WHERE a =$1; -explain (verbose, costs false) execute test_where_pd(1); -explain (verbose, costs false) execute test_where_pd(2); -explain (verbose, costs false) execute test_where_pd(3); -explain (verbose, costs false) execute test_where_pd(4); -explain (verbose, costs false) execute test_where_pd(5); -explain (verbose, costs false) execute test_where_pd(6); -explain (verbose, costs false) execute test_where_pd(7); -explain (verbose, costs false) execute test_where_pd(8); -explain (verbose, costs false) execute test_where_pd(9); - -execute test_where_pd(1); -execute test_where_pd(2); -execute test_where_pd(3); -execute test_where_pd(4); -execute test_where_pd(5); -execute test_where_pd(6); -execute test_where_pd(7); -execute test_where_pd(8); -execute test_where_pd(9); - --- --- 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.5'); --- Should fail with an error -INSERT INTO test_numbers VALUES ('11', 11, 'Eleven'); -UPDATE test_numbers SET a = 11 WHERE a = 10; -DELETE FROM test_numbers WHERE a = 10; -SELECT * FROM test_numbers; --- Set correct address for mongo_server -ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.1'); --- Should able to insert the data -INSERT INTO test_numbers VALUES ('12', 12, 'Twelve'); - --- Change the user mapping options --- Set wrong username, password for postgres user -DROP USER MAPPING FOR postgres SERVER mongo_server; -CREATE USER MAPPING FOR postgres SERVER mongo_server OPTIONS (username 'wrong', password 'wrong'); --- Should fail with an error -INSERT INTO test_numbers VALUES ('13', 13, 'Thirteen'); -UPDATE test_numbers SET a = 11 WHERE a = 10; -DELETE FROM test_numbers WHERE a = 10; -SELECT * FROM test_numbers; --- Set default username, password for postgres user -DROP USER MAPPING FOR postgres SERVER mongo_server; -CREATE USER MAPPING FOR postgres SERVER mongo_server; --- Should able to insert the data -INSERT INTO test_numbers VALUES ('14', 14, 'Fourteen'); - --- FDW-158: Fix server crash when analyzing a foreign table. -SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; -ANALYZE test_numbers; --- Should give correct number of rows now. -SELECT reltuples FROM pg_class WHERE relname = 'test_numbers'; --- Check with this... -SELECT count(*) FROM test_numbers; - -DELETE FROM test_numbers; -DROP FOREIGN TABLE test_numbers; - -DROP FOREIGN TABLE test_json; -DROP FOREIGN TABLE test_jsonb; -DROP FOREIGN TABLE test_text; -DROP FOREIGN TABLE test_varchar; - -DROP FOREIGN TABLE department; -DROP FOREIGN TABLE employee; -DROP FOREIGN TABLE countries; -DROP FOREIGN TABLE country_elections; -DROP FOREIGN TABLE main_exports; -DROP USER MAPPING FOR postgres SERVER mongo_server; -DROP EXTENSION mongo_fdw CASCADE; diff --git a/sql/pushdown.sql b/sql/pushdown.sql new file mode 100644 index 0000000..b20137c --- /dev/null +++ b/sql/pushdown.sql @@ -0,0 +1,123 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' + +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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'); + +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; +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; +SELECT c1, c2, c6 FROM f_test_tbl1 e + WHERE c6 > 3000 + ORDER BY c1; + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 = 1500 + ORDER BY c1; +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 = 1500 + ORDER BY c1; + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 BETWEEN 1000 AND 4000 + ORDER BY c1; +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 BETWEEN 1000 AND 4000 + ORDER BY c1; + +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; +SELECT c1, c2, c5 FROM f_test_tbl1 e + WHERE c5 <= '1980-12-17' + ORDER BY c1; + +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; + +-- Pushdown in prepared statement. +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'); + +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); + +-- Cleanup +DELETE FROM f_mongo_test WHERE a != 0; +DROP FOREIGN TABLE f_mongo_test; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/sql/select.sql b/sql/select.sql new file mode 100644 index 0000000..8351016 --- /dev/null +++ b/sql/select.sql @@ -0,0 +1,243 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' + +-- Before running this file User must create database mongo_fdw_regress & +-- mongo_fdw_regress1 databases on MongoDB with all permission for +-- 'edb' user with 'edb' 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 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'); + +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(); + +-- Cleanup +DELETE FROM f_mongo_test WHERE a != 0; +DROP TABLE l_test_tbl1; +DROP TABLE l_test_tbl2; +DROP VIEW smpl_vw; +DROP VIEW comp_vw; +DROP VIEW temp_vw; +DROP VIEW mul_tbl_view; +DROP FUNCTION 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/sql/server_options.sql b/sql/server_options.sql new file mode 100644 index 0000000..a53db4f --- /dev/null +++ b/sql/server_options.sql @@ -0,0 +1,57 @@ +\set MONGO_HOST '\'localhost\'' +\set MONGO_PORT '\'27017\'' +\set MONGO_USER_NAME '\'edb\'' +\set MONGO_PASS '\'edb\'' + +-- Before running this file User must create database mongo_fdw_regress and +-- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user +-- with 'edb' 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; + +-- Validate extension, server and mapping details +SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" + 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; + +-- 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; +DROP FOREIGN TABLE f_mongo_test; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; + +-- Create server with authentication_database option +-- authentication_database options is not supported with legacy driver +-- so below queries will fail when compiled with legacy driver. +CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw + OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); +CREATE USER MAPPING FOR public SERVER mongo_server + OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); +CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); +-- 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; + +-- Cleanup +DROP FOREIGN TABLE f_mongo_test; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; From 63db08cd14bed93b82a8d549f35a47bf282023a3 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 29 Jul 2020 15:39:38 +0530 Subject: [PATCH 133/239] Update README and CONTRIBUTING files. It removes a few obsolete link references and rephrases some contents. FDW-140, Vaibhav Dalvi, reviewed by Suraj Kharage, further adjustments by me. --- CONTRIBUTING.md | 69 ++++++------ README.md | 287 ++++++++++++++++++++++++++++-------------------- 2 files changed, 205 insertions(+), 151 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e490f03..64aba67 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,17 @@ 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. +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]. If you think you require personal -assistance, please **do not** open an issue: email `mongo_fdw` `@` `enterprisedb.com` -instead. +`mongo_fdw`'s maintainers prefer that bug reports, feature requests, and +pull requests are submitted as [GitHub Issues][1]. Bug Reports @@ -21,20 +20,22 @@ Bug Reports Before opening a bug report: 1. Search for a duplicate issue using GitHub's issue search - 2. Check whethe the bug remains in the lasest `master` or `develop` commit - 3. Create a reduced test case: remove code and data not relevant to the bug + 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: +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: +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 @@ -44,42 +45,44 @@ bug itself. You should explain: 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. +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`. +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: +Some examples of things likely to increase the likelihood a pull request +is rejected: * Large structural changes, including: - * Refactoring for its own sake + * Re-factoring for its own sake * Adding languages to the project - * Unnecesary whitespace changes + * 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. +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. +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 diff --git a/README.md b/README.md index 82badd1..4c3984a 100644 --- a/README.md +++ b/README.md @@ -1,128 +1,178 @@ # MongoDB Foreign Data Wrapper for PostgreSQL -This [MongoDB][1] extension implements the PostgreSQL's Foreign Data Wrapper. +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 9.5, 9.6, 10, 11, 12, and 13. +Please note that this version of mongo_fdw works with PostgreSQL and EDB +Postgres Advanced Server 9.5, 9.6, 10, 11, 12, and 13. Installation ------------ -The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and includes it as a git submodule. If you are cloning this repository for the first time, be sure to pass the --recursive option to git clone in order to initialize the driver submodule to a useable state. +The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and +includes it as a git submodule. If you are cloning this repository for +the first time, be sure to pass the --recursive option to git clone in +order to initialize the driver submodule to a usable state. -If checked out this project before and for some reason your submodule is not up-to-date, run git submodule update --init. +If checked out this project before and for some reason your submodule +is not up-to-date, run git submodule update --init. -When you type `make`, the C driver's source code also gets automaticallycompiled and linked. +When you type `make`, the C driver's source code also gets automatically +compiled and linked. -Note: Make sure you have permission to "/usr/local" (default installation location) folder. +Note: Make sure you have permission to "/usr/local" +(default installation location) folder. -Note that we have verified the `mongo_fdw` extension only on MacOS X, -Fedora and Ubuntu systems. If you run into issues on other systems, please [let us know][3] +If you run into any issues, please [let us know][2]. Enhancements ----------- -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 mango 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. - -New MongoDB C Driver Support ----------------------------- -The third enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But [MongoDB][1] is provided completely new library for driver called MongoDB's Meta Driver. So I have added support of that driver. Now compile time option is available to use legacy and Meta driver. I am sure there are many other benefits of the new Mongo-C-driver that we are not leveraging but we will adopt those as we learn more about the new C driver. +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 mango 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. + +### New MongoDB C Driver Support +The third enhancement is to add a new [MongoDB][1]' C driver. The +current implementation is based on the legacy driver of MongoDB. But +[MongoDB][1] is provided completely new library for driver called +MongoDB's meta driver. Added support for the same. Now compile time +option is available to use legacy and meta driver. In order to use MongoDB driver 1.0.0+, take the following steps: - * clone `libbson` version 1.0.0+ (https://github.com/mongodb/libbson). Follow install directions on that project's README. - * clone `libmongoc` version 1.0.0+ (https://github.com/mongodb/mongo-c-driver). Follow the install directions, except make sure to also run `./configure --with-libbson=system` after running automake but before running make. This should be the default behavior, but to be certain include this step. + * clone `libbson` version 1.0.0+ (https://github.com/mongodb/libbson). + Follow install directions on that project's README. + * clone `libmongoc` version 1.0.0+ + (https://github.com/mongodb/mongo-c-driver). Follow the + install directions, except make sure to also run `./configure + --with-libbson=system` after running automake but before running make. + This should be the default behavior, but to be certain include this + step. * ensure pkg-config / pkgconf is installed on your system. * run `make -f Makefile.meta && make -f Makefile.meta install` - * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, then try running `ldconfig` + * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, + then try running `ldconfig` Compilation script ----------------- -Number of manual steps needs to be performed to compile and install different type of MongoDB drivers and supported 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. +Number of manual steps needs to be performed to compile and install +different type of MongoDB drivers and supported 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 : Build with [MongoDB][1]'s legacy branch driver * autogen.sh --with-legacy -Build [MongoDB][1]'s master branch driver +Build [MongoDB][1]'s master branch driver * autogen.sh --with-master -The script will do all the necessary steps to build with legacy and metadriver accordingly. +The script will do all the necessary steps to build with legacy and +meta driver accordingly. Usage ----- The following parameters can be set on a MongoDB foreign server object: - * **`address`**: the address or hostname of the MongoDB server Defaults to `127.0.0.1` - * **`port`**: the port number of the MongoDB server. Defaults to `27017` - * **`authentication_database`**: database against which user will be authenticated against. Only valid with password based authentication. Defaults to per same database as the MongoDB collection database. - * **`replica_set`**: replica set the server is member of. If set, driver will auto-connect to correct primary in the replica set when writing. - * **`read_preference`**: primary [default], secondary, primaryPreferred, secondaryPreferred, or nearest (meta driver only). Defaults to `primary` - * **`ssl`**: false [default], true to enable ssl (meta driver only). See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to understand the options. - * **`pem_file`**: SSL option; - * **`pem_pwd`**: SSL option; - * **`ca_file`**: SSL option; - * **`ca_dir`**: SSL option; - * **`crl_file`**: SSL option; - * **`weak_cert_validation`**: SSL option; + * `address`: Address or hostname of the MongoDB server. Defaults to + `127.0.0.1` + * `port`: Port number of the MongoDB server. Defaults to `27017`. + +The following options are only supported with meta driver: + + * `authentication_database`: Database against which user will be + authenticated against. Only valid with password based authentication. + * `replica_set`: Replica set the server is member of. If set, + driver will auto-connect to correct primary in the replica set when + writing. + * `read_preference`: primary [default], secondary, primaryPreferred, + secondaryPreferred, or nearest. + * `ssl`: false [default], true to enable ssl. See + http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to + understand the options. + * `pem_file`: SSL option. + * `pem_pwd`: SSL option. + * `ca_file`: SSL option. + * `ca_dir`: SSL option. + * `crl_file`: SSL option. + * `weak_cert_validation`: SSL option. The following parameters can be set on a MongoDB foreign table object: - * **`database`**: the name of the MongoDB database to query. Defaults to `test` - * **`collection`**: the name of the MongoDB collection to query. Defaults to the foreign table name used in the relevant `CREATE` command + * `database`: Name of the MongoDB database to query. Defaults to + `test`. + * `collection`: Name of the MongoDB collection to query. Defaults to + the foreign table name used in the relevant `CREATE` command. -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. +The following parameters can be supplied while creating user mapping: -`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`. + * `username`: Username to use when connecting to MongoDB. + * `password`: Password to authenticate to the MongoDB server. -Examples with [MongoDB][1]'s equivalent statments. +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. -```sql +`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`. + +Examples +-------- + +Examples with [MongoDB][1]'s equivalent statements. +```sql -- load extension first time after install CREATE EXTENSION mongo_fdw; -- create server object CREATE SERVER mongo_server - FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address '127.0.0.1', port '27017'); + FOREIGN DATA WRAPPER mongo_fdw + OPTIONS (address '127.0.0.1', port '27017'); -- create user mapping CREATE USER MAPPING FOR postgres - SERVER mongo_server - OPTIONS (username 'mongo_user', password 'mongo_pass'); + SERVER mongo_server + OPTIONS (username 'mongo_user', password 'mongo_pass'); -- create foreign table -CREATE FOREIGN TABLE warehouse( - _id NAME, - warehouse_id int, - warehouse_name text, - warehouse_created timestamptz) -SERVER mongo_server - OPTIONS (database 'db', collection 'warehouse'); - --- 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 mongo_server + OPTIONS (database 'db', collection 'warehouse'); + +-- Note: first column of the table must be "_id" of type "name". -- select from table 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) - _id | warehouse_id | warehouse_name | warehouse_created -------------------------+----------------+--------------------------- -53720b1904864dc1f5a571a0| 1 | UPS | 12-DEC-14 12:12:10 +05:00 - - -db.warehouse.find({"warehouse_id" : 1}).pretty() +db.warehouse.find +( + { + "warehouse_id" : 1 + } +).pretty() { "_id" : ObjectId("53720b1904864dc1f5a571a0"), "warehouse_id" : 1, @@ -130,98 +180,99 @@ db.warehouse.find({"warehouse_id" : 1}).pretty() "warehouse_created" : ISODate("2014-12-12T07:12:10Z") } - -- insert row in table -INSERT INTO warehouse values (0, 1, 'UPS', '2014-12-12T07:12:10Z'); +INSERT INTO warehouse VALUES (0, 2, 'Laptop', '2015-11-11T08:13:10Z'); db.warehouse.insert ( - { - "warehouse_id" : NumberInt(1), - "warehouse_name" : "UPS", - "warehouse_created" : ISODate("2014-12-12T07:12:10Z") - } -); + { + "warehouse_id" : NumberInt(2), + "warehouse_name" : "Laptop", + "warehouse_created" : ISODate("2015-11-11T08:13:10Z") + } +) -- delete row from table -DELETE FROM warehouse where warehouse_id = 3; - -> db.warehouse.remove({"warehouse_id" : 2}) +DELETE FROM warehouse WHERE warehouse_id = 2; +db.warehouse.remove +( + { + "warehouse_id" : 2 + } +) -- update a row of table -UPDATE warehouse set warehouse_name = 'UPS_NEW' where warehouse_id = 1; +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_id" : 1 + }, + { + "warehouse_id" : 1, + "warehouse_name" : "UPS_NEW", + "warehouse_created" : ISODate("2014-12-12T07:12:10Z") + } ) -- explain a table EXPLAIN SELECT * FROM warehouse WHERE warehouse_id = 1; QUERY PLAN - ----------------------------------------------------------------- - Foreign Scan on warehouse (cost=0.00..0.00 rows=1000 width=44) +----------------------------------------------------------------- + Foreign Scan on warehouse (cost=0.00..0.00 rows=1000 width=84) Filter: (warehouse_id = 1) Foreign Namespace: db.warehouse - Planning time: 0.671 ms -(4 rows) +(3 rows) --- collect data distribution statistics` +-- collect data distribution statistics ANALYZE warehouse; -select mongo_fdw_version(); - mongo_fdw_version -------------------- - 50100 -(1 row) - - ``` 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. + * 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 reinstall. + * 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][4]. For all other types of questions or comments about the wrapper please contact us at `mongo_fdw` `@` `enterprisedb.com`. +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. +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. -If you need commercial support, please contact the EnterpriseDB sales team, or check whether your existing PostgreSQL support provider can also support mongo_fdw. License ------- Portions Copyright (c) 2004-2020, 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. +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`][5] file for full details. +See the [`LICENSE`][4] file for full details. [1]: http://www.mongodb.com -[2]: http://www.citusdata.com/blog/51-run-sql-on-mongodb -[3]: https://github.com/enterprisedb/mongo_fdw/issues/new -[4]: CONTRIBUTING.md -[5]: LICENSE -[6]: https://github.com/mongodb/mongo-c-driver-legacy -[7]: https://github.com/mongodb/mongo-c-driver +[2]: https://github.com/enterprisedb/mongo_fdw/issues/new +[3]: CONTRIBUTING.md +[4]: LICENSE From aaeb6c72b63d78f704bc7f1fe000a4c61b986ea4 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 3 Aug 2020 16:02:01 +0530 Subject: [PATCH 134/239] Stamp 5.2.7. --- expected/select.out | 7 +++++++ expected/select_1.out | 7 +++++++ mongo_fdw.c | 4 ++-- sql/select.sql | 3 +++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/expected/select.out b/expected/select.out index 9f1fbd0..19ea15b 100644 --- a/expected/select.out +++ b/expected/select.out @@ -11,6 +11,13 @@ 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 +------------------- + 50207 +(1 row) + -- 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'); diff --git a/expected/select_1.out b/expected/select_1.out index 38a231d..29be052 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -11,6 +11,13 @@ 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 +------------------- + 50207 +(1 row) + -- 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'); diff --git a/mongo_fdw.c b/mongo_fdw.c index 79e68e2..3ed4043 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -53,9 +53,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.1.0 so number will be 50100 + * our version is 5.2.7 so number will be 50207 */ -#define CODE_VERSION 50201 +#define CODE_VERSION 50207 extern PGDLLEXPORT void _PG_init(void); const char *EscapeJsonString(const char *string); diff --git a/sql/select.sql b/sql/select.sql index 8351016..37a64de 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -14,6 +14,9 @@ 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 varchar) SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); From 9b4843b77d5b8465c8b4d391464bb58912555aad Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Sun, 20 Sep 2020 18:51:31 +0530 Subject: [PATCH 135/239] Change the server port option's type from int32 to uint16. Meta driver APIs expect port value in unsigned short type which resulted in a compilation warning on some gcc version as we were passing an int to it. So change that to fix the compilation warnings. The legacy driver, however, accepts an int, so we cast that value to int while passing to those APIs. Now as we have changed the type to uint16, added a strict check making sure that the value for the port provided is within the ushort range. FDW-199, Vaibhav Dalvi, reviewed and test-case added by me. --- expected/server_options.out | 6 ++++++ mongo_fdw.h | 2 +- mongo_wrapper.c | 7 ++++--- option.c | 23 +++++++++++++++++++++-- sql/server_options.sql | 5 +++++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/expected/server_options.out b/expected/server_options.out index a8835bf..7341299 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -11,6 +11,12 @@ 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 SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" 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 diff --git a/mongo_fdw.h b/mongo_fdw.h index e6be514..09f8202 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -223,7 +223,7 @@ static const MongoValidOption ValidOptionArray[] = typedef struct MongoFdwOptions { char *svr_address; - int32 svr_port; + uint16 svr_port; char *svr_database; char *collectionName; char *svr_username; diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 3054d2b..ad4ce09 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -31,14 +31,15 @@ MongoConnect(MongoFdwOptions *opt) conn = mongo_alloc(); mongo_init(conn); - if (mongo_connect(conn, opt->svr_address, opt->svr_port) != MONGO_OK) + if (mongo_connect(conn, opt->svr_address, + (int32) opt->svr_port) != MONGO_OK) { int err = conn->err; mongo_destroy(conn); mongo_dealloc(conn); ereport(ERROR, - (errmsg("could not connect to %s:%d", opt->svr_address, + (errmsg("could not connect to %s:%hu", opt->svr_address, opt->svr_port), errhint("Mongo driver connection error: %d.", err))); } @@ -52,7 +53,7 @@ MongoConnect(MongoFdwOptions *opt) mongo_destroy(conn); mongo_dealloc(conn); ereport(ERROR, - (errmsg("could not connect to %s:%d", opt->svr_address, + (errmsg("could not connect to %s:%hu", opt->svr_address, opt->svr_port), errhint("Mongo driver connection error: %s", str))); } diff --git a/option.c b/option.c index c5878c4..324589e 100644 --- a/option.c +++ b/option.c @@ -81,7 +81,16 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) /* If port option is given, error out if its value isn't an integer */ if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0) - (void) pg_atoi(defGetString(optionDef), sizeof(int32), 0); + { + int32 port; + + port = pg_atoi(defGetString(optionDef), sizeof(int32), 0); + if (port < 0 || port > USHRT_MAX) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("port value \"%d\" is out of range for type %s", + port, "unsigned short"))); + } } PG_RETURN_VOID(); @@ -175,7 +184,17 @@ mongo_get_options(Oid foreignTableId) if (portName == NULL) options->svr_port = DEFAULT_PORT_NUMBER; else - options->svr_port = pg_atoi(portName, sizeof(int32), 0); + { + int32 port; + + port = pg_atoi(portName, sizeof(int32), 0); + if (port < 0 || port > USHRT_MAX) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("port value \"%d\" is out of range for type %s", + port, "unsigned short"))); + options->svr_port = (unsigned short) port; + } options->svr_database = mongo_get_option_value(optionList, OPTION_NAME_DATABASE); diff --git a/sql/server_options.sql b/sql/server_options.sql index a53db4f..8c8951c 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -13,6 +13,11 @@ 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 SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" 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 From 72ac0c0a621ad825e47ef279cebd8512591e779c Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 22 Sep 2020 12:10:21 +0530 Subject: [PATCH 136/239] Fix an alternate expected out file. In commit 9b4843b77d5b8465c8b4d391464bb58912555aad, I have failed to update the server_options_1.out. Fix that. Reported and patch by Vaibhav Dalvi. --- expected/server_options_1.out | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/expected/server_options_1.out b/expected/server_options_1.out index 9e6b779..464c7e5 100644 --- a/expected/server_options_1.out +++ b/expected/server_options_1.out @@ -11,6 +11,12 @@ 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 SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" 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 From 357107223b480d2a7ed0d56c4022d8dcbd5534a7 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Sat, 26 Sep 2020 22:36:24 +0530 Subject: [PATCH 137/239] Fix ReScanForeignScan API to make the parameterized query work correctly. Sub-select or correlated queries that use a parameterized plan uses ReScanForeignScan API. However, this API is not correctly rescanning the data. In the case of parameters, we need to recreate the Mongo cursor that fetches the data again. The patch does the same. In passing, refactor some code in this area so that Mongo cursor is created from MongoIterateForeignScan() that is called after Begin and ReScan APIs. However, create a Mongo cursor only if it was not created already. FDW-103, Vaibhav Dalvi, reviewed by Suraj Kharage. --- expected/select.out | 64 ++++++++++++++++++++++++++++++- expected/select_1.out | 64 ++++++++++++++++++++++++++++++- mongo_fdw.c | 89 ++++++++++++++++++++++--------------------- sql/select.sql | 19 ++++++++- 4 files changed, 190 insertions(+), 46 deletions(-) diff --git a/expected/select.out b/expected/select.out index 19ea15b..e7096f3 100644 --- a/expected/select.out +++ b/expected/select.out @@ -19,7 +19,7 @@ SELECT mongo_fdw_version(); (1 row) -- Create foreign tables -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) +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'); @@ -945,10 +945,72 @@ NOTICE: Found number Nine (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) + -- 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; diff --git a/expected/select_1.out b/expected/select_1.out index 29be052..d9d4eb9 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -19,7 +19,7 @@ SELECT mongo_fdw_version(); (1 row) -- Create foreign tables -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) +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'); @@ -909,10 +909,72 @@ NOTICE: Found number Nine (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) + -- 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 3ed4043..4427d15 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -358,7 +358,6 @@ MongoGetForeignPlan(PlannerInfo *root, ForeignScan *foreignScan; List *foreignPrivateList; List *opExpressionList; - BSON *queryDocument; List *columnList; /* @@ -371,14 +370,10 @@ MongoGetForeignPlan(PlannerInfo *root, restrictionClauses = extract_actual_clauses(restrictionClauses, false); /* - * 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. + * Find the foreign relation's clauses, which can be transformed to + * equivalent MongoDB queries. */ opExpressionList = ApplicableOpExpressionList(foreignrel); - queryDocument = QueryDocument(foreigntableid, opExpressionList, NULL); /* We don't need to serialize column list as lists are copiable */ columnList = ColumnList(foreignrel); @@ -386,9 +381,6 @@ MongoGetForeignPlan(PlannerInfo *root, /* Construct foreign plan with query document and column list */ foreignPrivateList = list_make2(columnList, opExpressionList); - /* Only clean up the query struct */ - BsonDestroy(queryDocument); - /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, restrictionClauses, scanRangeTableIndex, @@ -464,16 +456,12 @@ static void MongoBeginForeignScan(ForeignScanState *node, int eflags) { MONGO_CONN *mongoConnection; - MONGO_CURSOR *mongoCursor; Oid foreignTableId; List *columnList; HTAB *columnMappingHash; - ForeignScan *foreignScan; List *foreignPrivateList; - BSON *queryDocument; MongoFdwOptions *options; MongoFdwModifyState *fmstate; - List *opExpressionList; RangeTblEntry *rte; EState *estate = node->ss.ps.state; ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan; @@ -510,26 +498,16 @@ MongoBeginForeignScan(ForeignScanState *node, int eflags) */ mongoConnection = mongo_get_connection(server, user, options); - foreignScan = (ForeignScan *) node->ss.ps.plan; - foreignPrivateList = foreignScan->fdw_private; + foreignPrivateList = fsplan->fdw_private; Assert(list_length(foreignPrivateList) == 2); columnList = list_nth(foreignPrivateList, 0); - opExpressionList = list_nth(foreignPrivateList, 1); - - queryDocument = QueryDocument(foreignTableId, opExpressionList, node); columnMappingHash = ColumnMappingHash(foreignTableId, columnList); - /* Create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, - options->collectionName, queryDocument); - /* Create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; fmstate->mongoConnection = mongoConnection; - fmstate->mongoCursor = mongoCursor; - fmstate->queryDocument = queryDocument; fmstate->options = options; node->fdw_state = (void *) fmstate; @@ -537,9 +515,11 @@ MongoBeginForeignScan(ForeignScanState *node, int eflags) /* * MongoIterateForeignScan - * Reads the next document from MongoDB, converts it to a PostgreSQL tuple - * and stores the converted tuple into the ScanTupleSlot as a virtual - * tuple. + * 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) @@ -548,11 +528,44 @@ MongoIterateForeignScan(ForeignScanState *node) TupleTableSlot *tupleSlot = node->ss.ss_ScanTupleSlot; MONGO_CURSOR *mongoCursor = fmstate->mongoCursor; HTAB *columnMappingHash = fmstate->columnMappingHash; + ForeignScan *foreignScan = (ForeignScan *) node->ss.ps.plan; 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) + { + Oid foreignTableId; + List *foreignPrivateList; + List *opExpressionList; + BSON *queryDocument; + + foreignPrivateList = foreignScan->fdw_private; + Assert(list_length(foreignPrivateList) == 2); + + opExpressionList = list_nth(foreignPrivateList, 1); + foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); + + /* + * 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 = QueryDocument(foreignTableId, opExpressionList, node); + + mongoCursor = MongoCursorCreate(fmstate->mongoConnection, + fmstate->options->svr_database, + fmstate->options->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 @@ -614,23 +627,13 @@ static void MongoReScanForeignScan(ForeignScanState *node) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state; - MONGO_CONN *mongoConnection = fmstate->mongoConnection; - MongoFdwOptions *options; - Oid foreignTableId; /* Close down the old cursor */ - MongoCursorDestroy(fmstate->mongoCursor); - - /* Reconstruct full collection name */ - foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); - options = mongo_get_options(foreignTableId); - - /* Reconstruct cursor for collection name and set query */ - fmstate->mongoCursor = MongoCursorCreate(mongoConnection, - fmstate->options->svr_database, - fmstate->options->collectionName, - fmstate->queryDocument); - mongo_free_options(options); + if (fmstate->mongoCursor) + { + MongoCursorDestroy(fmstate->mongoCursor); + fmstate->mongoCursor = NULL; + } } static List * diff --git a/sql/select.sql b/sql/select.sql index 37a64de..539ddde 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -18,7 +18,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); -- Create foreign tables -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) +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'); @@ -222,10 +222,27 @@ $$ 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; + -- 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; From de5db6eab258753bdb290990ff9a3e833ddccee9 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 Oct 2020 11:13:11 +0530 Subject: [PATCH 138/239] Fix issue where casting target list produces 'NULL'. To check which columns are needed to be output, we create a column list from the target list. However, we are only adding the plain Var nodes appearing in the target list. Thus, if a target list has an explicit casting, then it won't appear as a plain Var node and thus not added in the column list, resulting in returning NULL for them. Fix that by correctly pulling out the Vars from the expression appearing in the target list by using pull_var_clause(). This enables us to give a correct result even if we have expressions other than an explicit cast like a function call or operators. Reported on GitHub through issues #133 by Maksim Volkau (dadhi). FDW-197, Vaibhav Dalvi, reviewed by Suraj Kharage --- expected/select.out | 68 +++++++++++++++++++++++++++++++++++++++++++ expected/select_1.out | 68 +++++++++++++++++++++++++++++++++++++++++++ mongo_query.c | 6 +++- sql/select.sql | 14 +++++++++ 4 files changed, 155 insertions(+), 1 deletion(-) diff --git a/expected/select.out b/expected/select.out index e7096f3..2ba4de5 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1006,6 +1006,74 @@ SELECT c1, c8 FROM f_test_tbl1 ft1 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, 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 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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/expected/select_1.out b/expected/select_1.out index d9d4eb9..47f712c 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -970,6 +970,74 @@ SELECT c1, c8 FROM f_test_tbl1 ft1 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, 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 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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/mongo_query.c b/mongo_query.c index a4f3972..6680642 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -730,7 +730,11 @@ ColumnList(RelOptInfo *baserel) ListCell *restrictInfoCell; /* First add the columns used in joins and projections */ - neededColumnList = list_copy(targetColumnList); + neededColumnList = pull_var_clause((Node *)targetColumnList, +#if PG_VERSION_NUM < 90600 + PVC_RECURSE_AGGREGATES, +#endif + PVC_RECURSE_PLACEHOLDERS); /* Then walk over all restriction clauses, and pull up any used columns */ foreach(restrictInfoCell, restrictInfoList) diff --git a/sql/select.sql b/sql/select.sql index 539ddde..dc934c3 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -238,6 +238,20 @@ 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, 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 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; From c4412a46d3fe096337327da81f4cb7466e8445f8 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 Oct 2020 20:02:17 +0530 Subject: [PATCH 139/239] Fix crash with COPY FROM and/or foreign partition routing operations. COPY FROM and/or foreign partition routing code path in core assumes that FDW has BeginForeignInsert() APIs present and thus later executes ExecForeignInsert(). However, mongo_fdw does not support routable foreign-table partitions and/or executing COPY FROM on foreign tables and thus do not have BeginForeignInsert() API implemented. But as it has ExecForeignInsert() API, it gets called for these operations and results in the server crash. To fix this, add the BeginForeignInsert() API that throws an error. Also, add EndForeignInsert() similar to the Begin API. The issue was originally reported on MySQL's GitHub through #208. FDW-226, Suraj Kharage, reviewed by Jeevan Chalke. --- mongo_fdw.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/mongo_fdw.c b/mongo_fdw.c index 4427d15..40d9407 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -117,6 +117,12 @@ static void MongoExplainForeignModify(ModifyTableState *mtstate, static bool MongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); +#if PG_VERSION_NUM >= 110000 +static void MongoBeginForeignInsert(ModifyTableState *mtstate, + ResultRelInfo *resultRelInfo); +static void MongoEndForeignInsert(EState *estate, + ResultRelInfo *resultRelInfo); +#endif /* * Helper functions @@ -202,6 +208,12 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) /* Support for ANALYZE */ fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; +#if PG_VERSION_NUM >= 110000 + /* Partition routing and/or COPY from */ + fdwRoutine->BeginForeignInsert = MongoBeginForeignInsert; + fdwRoutine->EndForeignInsert = MongoEndForeignInsert; +#endif + PG_RETURN_POINTER(fdwRoutine); } @@ -2186,3 +2198,36 @@ mongo_fdw_version(PG_FUNCTION_ARGS) { PG_RETURN_INT32(CODE_VERSION); } + +#if PG_VERSION_NUM >= 110000 +/* + * 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"))); +} +#endif From 9b798ecb7f53130fb466fbf2d33fd10427a99d72 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 21 Oct 2020 11:15:37 +0530 Subject: [PATCH 140/239] Update LICENSE file. --- LICENSE | 176 +++++--------------------------------------------------- 1 file changed, 15 insertions(+), 161 deletions(-) diff --git a/LICENSE b/LICENSE index 6600f1c..24fe448 100644 --- a/LICENSE +++ b/LICENSE @@ -1,165 +1,19 @@ -GNU LESSER GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +MongoDB Foreign Data Wrapper for PostgreSQL - 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. +Copyright (c) 2011-2020, EnterpriseDB Corporation. +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose, without fee, and without a written agreement is +hereby granted, provided that the above copyright notice and this paragraph and +the following two paragraphs appear in all copies. - 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. +IN NO EVENT SHALL ENTERPRISEDB CORPORATION BE LIABLE TO ANY PARTY FOR +DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST +PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF +ENTERPRISEDB CORPORATION HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - 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. +ENTERPRISEDB CORPORATION SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND +ENTERPRISEDB CORPORATION HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, +UPDATES, ENHANCEMENTS, OR MODIFICATIONS. From b4c1d76783a071a2a2733bebd0446940708e129e Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 27 Oct 2020 12:42:53 +0530 Subject: [PATCH 141/239] Stamp 5.2.8. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index 2ba4de5..380e2c7 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50207 + 50208 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 47f712c..c7bcdf7 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50207 + 50208 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 40d9407..6c1f571 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -53,9 +53,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.2.7 so number will be 50207 + * our version is 5.2.8 so number will be 50208 */ -#define CODE_VERSION 50207 +#define CODE_VERSION 50208 extern PGDLLEXPORT void _PG_init(void); const char *EscapeJsonString(const char *string); From 27ee19d2226a4d1a09fda0020dd6c40535cfa437 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 1 Dec 2020 19:59:00 +0530 Subject: [PATCH 142/239] Fix crash with LEFT JOIN LATERAL queries. In get_baserel_parampathinfo(), there is an Assert() which verifies that if rel has LATERAL refs, then every path for it should have lateral_relids. However, in MongoGetForeignPaths() while calling the create_foreignscan_path() we were passing NULL to the required_outer parameter which is passed to the get_baserel_parampathinfo() and thus we end-up getting an assertion failure. Fix that by correctly passing baserel->lateral_relids to the create_foreignscan_path(). FDW-249, Vaibhav Dalvi, reviewed by me. --- expected/select.out | 107 ++++++++++++++++++++++++++++++++++++++++++ expected/select_1.out | 107 ++++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 2 +- sql/select.sql | 19 ++++++++ 4 files changed, 234 insertions(+), 1 deletion(-) diff --git a/expected/select.out b/expected/select.out index 380e2c7..fc1a649 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1074,6 +1074,113 @@ SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6 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; + QUERY PLAN +--------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t2.a, (t1.a) + Sort Key: t1.a + -> 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 +(12 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; + 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. -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/expected/select_1.out b/expected/select_1.out index c7bcdf7..6704747 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1038,6 +1038,113 @@ SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6 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; + QUERY PLAN +--------------------------------------------------------------- + Sort + Output: t1.a, t1.b, t2.a, (t1.a) + Sort Key: t1.a + -> 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 +(12 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; + 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. -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/mongo_fdw.c b/mongo_fdw.c index 6c1f571..eb8914e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -340,7 +340,7 @@ MongoGetForeignPaths(PlannerInfo *root, startupCost, totalCost, NIL, /* no pathkeys */ - NULL, /* no outer rel either */ + baserel->lateral_relids, #if PG_VERSION_NUM >= 90500 NULL, /* no extra plan */ #endif diff --git a/sql/select.sql b/sql/select.sql index dc934c3..e1431df 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -252,6 +252,25 @@ SELECT SUM(a::float), SUM(a % 2), a % 2 AS "a % 2"FROM f_mongo_test 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; +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; +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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; From a815df820af06b65a42d1d9d012b425b4d747241 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 10 Dec 2020 12:31:41 +0530 Subject: [PATCH 143/239] Add a few more tests testing COPY FROM and target list casting. FDW-227, Kashif Zeeshan, reviewed by Rajkumar Raghuwanshi. --- expected/dml.out | 78 +++++++++++++++++++++++++++++++++++++++++++ expected/select.out | 14 ++++++++ expected/select_1.out | 14 ++++++++ sql/dml.sql | 76 +++++++++++++++++++++++++++++++++++++++++ sql/select.sql | 2 ++ 5 files changed, 184 insertions(+) diff --git a/expected/dml.out b/expected/dml.out index 81e0d91..b83707f 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -165,6 +165,84 @@ 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 -- Cleanup DROP FOREIGN TABLE f_mongo_test; DROP FOREIGN TABLE f_mongo_test1; diff --git a/expected/select.out b/expected/select.out index fc1a649..936f405 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1014,6 +1014,13 @@ SELECT a::float FROM f_mongo_test ORDER BY a LIMIT 2; 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 ---+----------------------- @@ -1029,6 +1036,13 @@ SELECT a::float, b::varchar FROM f_mongo_test ORDER BY a LIMIT 2; 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 -----+------ diff --git a/expected/select_1.out b/expected/select_1.out index 6704747..ad2265c 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -978,6 +978,13 @@ SELECT a::float FROM f_mongo_test ORDER BY a LIMIT 2; 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 ---+----------------------- @@ -993,6 +1000,13 @@ SELECT a::float, b::varchar FROM f_mongo_test ORDER BY a LIMIT 2; 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 -----+------ diff --git a/sql/dml.sql b/sql/dml.sql index 22424a0..19513b4 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -80,6 +80,82 @@ 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; + + -- Cleanup DROP FOREIGN TABLE f_mongo_test; DROP FOREIGN TABLE f_mongo_test1; diff --git a/sql/select.sql b/sql/select.sql index e1431df..f338feb 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -240,8 +240,10 @@ SELECT c1, c8 FROM f_test_tbl1 ft1 -- 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 6f409d701ee1493af1c8d436dd326e6306e34315 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 11 Dec 2020 14:46:34 +0530 Subject: [PATCH 144/239] Move to the latest version of mongo-c-driver. Update autogen.sh to fetch mongo-c-driver 1.17.3 and json-c 0.15. This allows us to support MongoDB server 4.4. Earlier, mongo-c-driver (1.9.5) was supporting MongoDB 3.6, which is quite old, so upgrade to the latest one. Installation steps in autogen.sh needs some adjustments, as these new versions now use CMake. Since with this patch, as we moved to the newest mongo-c-driver, we need to adjust some codes to get rid of the deprecated code. FDW-238, Vaibhav Dalvi, reviewed by Jeevan Chalke, tested by Rajkumar Raghuwanshi. --- README.md | 17 +++++++---------- autogen.sh | 13 ++++++------- expected/connection_validation.out | 6 +++--- mongo_fdw.c | 9 +++++++++ mongo_query.c | 3 +-- mongo_wrapper.h | 1 - 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 4c3984a..596c14e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ order to initialize the driver submodule to a usable state. If checked out this project before and for some reason your submodule is not up-to-date, run git submodule update --init. -When you type `make`, the C driver's source code also gets automatically +When you type `cmake`, the C driver's source code also gets automatically compiled and linked. Note: Make sure you have permission to "/usr/local" @@ -46,16 +46,13 @@ current implementation is based on the legacy driver of MongoDB. But MongoDB's meta driver. Added support for the same. Now compile time option is available to use legacy and meta driver. -In order to use MongoDB driver 1.0.0+, take the following steps: +In order to use MongoDB driver 1.17.0+, take the following steps: - * clone `libbson` version 1.0.0+ (https://github.com/mongodb/libbson). - Follow install directions on that project's README. - * clone `libmongoc` version 1.0.0+ - (https://github.com/mongodb/mongo-c-driver). Follow the - install directions, except make sure to also run `./configure - --with-libbson=system` after running automake but before running make. - This should be the default behavior, but to be certain include this - step. + * clone `libmongoc` version 1.17.0+ + (https://github.com/mongodb/mongo-c-driver) and follow the install + directions given there. `libbson` is now maintained in a subdirectory + of the `libmongoc`. + (https://github.com/mongodb/mongo-c-driver/tree/master/src/libbson). * ensure pkg-config / pkgconf is installed on your system. * run `make -f Makefile.meta && make -f Makefile.meta install` * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, diff --git a/autogen.sh b/autogen.sh index 9f5ef50..b675155 100755 --- a/autogen.sh +++ b/autogen.sh @@ -14,8 +14,8 @@ #------------------------------------------------------------------------- -MONGOC_VERSION=1.9.5 -JSONC_VERSION=0.13.1-20180305 +MONGOC_VERSION=1.17.3 +JSONC_VERSION=0.15-20200726 if [ "$#" -ne 1 ]; then echo "Usage: autogen.sh --[with-legacy | with-master]" @@ -61,13 +61,12 @@ function checkout_json_lib ## -# Compile and instal json-c library +# Compile and install json-c library # function install_json_lib { cd json-c - sh ./autogen.sh - ./configure CFLAGS='-fPIC' + cmake . make install cd .. } @@ -78,7 +77,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver - ./configure --with-libbson=auto --enable-ssl + cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . make install cd .. } @@ -117,7 +116,7 @@ elif [ "--with-master" == $1 ]; then install_mongoc_driver install_json_lib create_config - export PKG_CONFIG_PATH=mongo-c-driver/src/:mongo-c-driver/src/libbson/src + export PKG_CONFIG_PATH=mongo-c-driver/src/libmongoc/src:mongo-c-driver/src/libbson/src cp Makefile.meta Makefile echo "Done" else diff --git a/expected/connection_validation.out b/expected/connection_validation.out index 0134ec8..c009e9d 100644 --- a/expected/connection_validation.out +++ b/expected/connection_validation.out @@ -32,13 +32,13 @@ ERROR: could not connect to server mongo_server HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +HINT: Mongo error: "No servers yet eligible for rescan" DELETE FROM f_mongo_test WHERE a = 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +HINT: Mongo error: "No servers yet eligible for rescan" SELECT a, b FROM f_mongo_test ORDER BY 1, 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" +HINT: Mongo error: "No servers yet eligible for rescan" -- Set correct address for mongo_server ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST); -- Should able to insert the data diff --git a/mongo_fdw.c b/mongo_fdw.c index eb8914e..eaded55 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -170,6 +170,11 @@ JsonSemAction nullSemAction = void _PG_init(void) { +#ifdef META_DRIVER + /* Initialize MongoDB C driver */ + mongoc_init(); +#endif + on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL)); } @@ -225,6 +230,10 @@ static void mongo_fdw_exit(int code, Datum arg) { mongo_cleanup_connection(); +#ifdef META_DRIVER + /* Release all memory and other resources allocated by the driver */ + mongoc_cleanup(); +#endif } /* diff --git a/mongo_query.c b/mongo_query.c index 6680642..c1d8f75 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -17,7 +17,6 @@ #include #include -#include #ifdef META_DRIVER #include "mongoc.h" @@ -678,7 +677,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, outputString = OidOutputFunctionCall(outputFunctionId, value); o = JsonTokenerPrase(outputString); - if (is_error(o)) + if (o == NULL) { elog(WARNING, "cannot parse the document"); status = 0; diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 3faee07..f895250 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -25,7 +25,6 @@ #define json_object json_object_tmp #include -#include #ifdef META_DRIVER MONGO_CONN *MongoConnect(MongoFdwOptions *opt); From 93e1362eb98abe9d83fe87e96755310c3883ed4f Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 21 Jan 2021 17:05:41 +0530 Subject: [PATCH 145/239] Add JSONC_CFLAGS environment variable to cmake while building json-c. When building the mongo-c driver, autogen script also builds json-c. However, with commit 6f409d701ee1493af1c8d436dd326e6306e34315 we build the latest version of json-c which uses cmake 3+. The json-c version 0.15 includes the xlocale.h file which on some platform included through other system headers too resulting into compilation failures. As json-c includes xlocale.h if HAVE_XLOCALE_H is set, provide a way to disable it using an environment variable. So on platforms like ppc64le, where it is included through the json-c file, it can be disabled by exporting JSONC_CFLAGS="-DHAVE_XLOCALE_H=0" and only included through the system header. Reported by Varsha Mehtre, fix suggested by Sandeep Thakkar. FDW-270, Vaibhav Dalvi, reviewed by me. --- autogen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index b675155..f2190d5 100755 --- a/autogen.sh +++ b/autogen.sh @@ -66,7 +66,7 @@ function checkout_json_lib function install_json_lib { cd json-c - cmake . + cmake3 $JSONC_CFLAGS . make install cd .. } @@ -77,7 +77,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver - cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . + cmake3 -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . make install cd .. } From b6caf62120921bc18d7f6e4a5ccd90498b2afd4d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 1 Feb 2021 16:58:06 +0530 Subject: [PATCH 146/239] Restrict fetching system attributes from the remote relation. Fetching them results in a server crash. Also, they are PostgreSQL specific, so don't fetch them. Throw an error when used in a query, instead. FDW-262, Vaibhav Dalvi, reviewed by me. --- expected/select.out | 9 +++++++++ expected/select_1.out | 9 +++++++++ mongo_fdw.c | 34 ++++++++++++++++++++++++++++++++++ sql/select.sql | 6 ++++++ 4 files changed, 58 insertions(+) diff --git a/expected/select.out b/expected/select.out index 936f405..4c94028 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1195,6 +1195,15 @@ 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 -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/expected/select_1.out b/expected/select_1.out index ad2265c..9dc5af7 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1159,6 +1159,15 @@ 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 -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/mongo_fdw.c b/mongo_fdw.c index eaded55..469f2a0 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -24,6 +24,7 @@ #if PG_VERSION_NUM >= 120000 #include "access/table.h" #endif +#include "catalog/heap.h" #include "catalog/pg_type.h" #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" @@ -380,6 +381,39 @@ MongoGetForeignPlan(PlannerInfo *root, List *foreignPrivateList; List *opExpressionList; List *columnList; + List *scan_var_list; + ListCell *lc; + +#if PG_VERSION_NUM >= 90600 + scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs, + PVC_RECURSE_PLACEHOLDERS); +#else + scan_var_list = pull_var_clause((Node *) foreignrel->reltargetlist, + PVC_RECURSE_AGGREGATES, + PVC_RECURSE_PLACEHOLDERS); +#endif + + /* 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; + +#if PG_VERSION_NUM >= 120000 + attr = SystemAttributeDefinition(var->varattno); +#else + attr = SystemAttributeDefinition(var->varattno, false); +#endif + ereport(ERROR, + (errcode(ERRCODE_FDW_COLUMN_NAME_NOT_FOUND), + errmsg("system attribute \"%s\" can't be fetched from remote relation", + attr->attname.data))); + } /* * We push down applicable restriction clauses to MongoDB, but for diff --git a/sql/select.sql b/sql/select.sql index f338feb..4afee9d 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -273,6 +273,12 @@ 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; From fce56c68aaa03f6aa512f2466b25bdf92a72147d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 3 Feb 2021 11:37:21 +0530 Subject: [PATCH 147/239] Rfactor WHERE pushdown code. Earlier, we are finding the pushable OpExpr by calling the function ApplicableOpExpressionList(). Change that to the postgres_fdw way of having a mongo_is_foreign_expr() function and a foreign_expr_walker(). This is cleaner and in-line with PostgreSQL that can be extended easily. This refactoring allowed is to push down the clauses having a Var node wrapped in the RelabelType. Also, for no reason but just for simplicity, we were re-evaluating restriction clauses that are pushed down to MongoDB again on the local side. Remove that altogether which adds to some performance improvement. FDW-194, Vaibhav Dalvi, reviewed by Suraj Kharage, further improvements by Jeevan Chalke. --- data/mongo_test_data.js | 5 + expected/pushdown.out | 222 ++++++++++++++++++-- mongo_fdw.c | 68 +++++-- mongo_fdw.h | 15 +- mongo_query.c | 433 ++++++++++++++++++++++++++++++++-------- mongo_query.h | 2 + sql/pushdown.sql | 79 +++++++- 7 files changed, 708 insertions(+), 116 deletions(-) diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index 6287dce..da0f603 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -11,6 +11,7 @@ db.dropDatabase(); use mongo_fdw_regress db.test_tbl1.drop(); db.test_tbl2.drop(); +db.test_tbl3.drop(); db.mongo_test.drop(); // Below queries will create and insert values in collections db.mongo_test.insert({a : NumberInt(0), b : "mongo_test collection"}); @@ -36,3 +37,7 @@ db.test_tbl1.insertMany([ {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]}, + {name: "vdd", marks: [29, 31]} +]); diff --git a/expected/pushdown.out b/expected/pushdown.out index ba49a3f..ed01682 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -17,6 +17,12 @@ CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR( 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) + 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; @@ -73,9 +79,8 @@ SELECT c1, c2, c6 FROM f_test_tbl1 e Sort Key: e.c1 -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6 - Filter: (e.c6 > '3000'::numeric) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 rows) +(6 rows) SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 @@ -96,9 +101,8 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e Sort Key: e.c1 -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 - Filter: (e.c6 = '1500'::numeric) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 rows) +(6 rows) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 = 1500 @@ -112,16 +116,15 @@ EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 BETWEEN 1000 AND 4000 ORDER BY c1; - QUERY PLAN ---------------------------------------------------------------------------- + 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.c6 >= '1000'::numeric) AND (e.c6 <= '4000'::numeric)) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 rows) +(6 rows) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 BETWEEN 1000 AND 4000 @@ -187,9 +190,8 @@ SELECT c1, c2, c5 FROM f_test_tbl1 e Sort Key: e.c1 -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c5 - Filter: (e.c5 <= '1980-12-17'::date) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 rows) +(6 rows) SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' @@ -272,10 +274,197 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e 700 | EMP7 | 2450.34 | 10 (3 rows) +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT a FROM f_mongo_test + WHERE a%2 = 1 + ORDER BY a; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: a + Sort Key: f_mongo_test.a + -> Foreign Scan on public.f_mongo_test + Output: a + Filter: ((f_mongo_test.a % 2) = 1) + Foreign Namespace: mongo_fdw_regress.mongo_test +(7 rows) + +SELECT a FROM f_mongo_test + WHERE a%2 = 1 + ORDER BY a; + 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) + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 = 'EMP10'; + 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'; + c1 | c2 +------+------- + 1000 | EMP10 +(1 row) + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 < 'EMP10'; + 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'; + c1 | c2 +-----+------ + 100 | EMP1 +(1 row) + +-- Should not push down if two columns of same table is +-- involved in single WHERE clause operator expression. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c6 FROM f_test_tbl1 + WHERE c1 = c6 AND c1 = 1100 + ORDER BY c1; + QUERY PLAN +-------------------------------------------------------- + Foreign Scan on public.f_test_tbl1 + Output: c1, c6 + Filter: ((f_test_tbl1.c1)::numeric = f_test_tbl1.c6) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(4 rows) + +SELECT c1, c6 FROM f_test_tbl1 + WHERE c1 = c6 AND c1 = 1100 + ORDER BY c1; + c1 | c6 +------+------ + 1100 | 1100 +(1 row) + +-- Nested operator expression in WHERE clause. Shouldn't push down. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(4 rows) + +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; + 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; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(4 rows) + +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > 0::BOOLEAN; + 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. -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'); PREPARE pre_stmt_f_mongo_test(int) AS SELECT b FROM f_mongo_test WHERE a = $1 ORDER BY b; EXPLAIN (VERBOSE, COSTS FALSE) @@ -287,9 +476,8 @@ EXECUTE pre_stmt_f_mongo_test(1); Sort Key: f_mongo_test.b -> Foreign Scan on public.f_mongo_test Output: b - Filter: (f_mongo_test.a = 1) Foreign Namespace: mongo_fdw_regress.mongo_test -(7 rows) +(6 rows) EXECUTE pre_stmt_f_mongo_test(1); b @@ -306,9 +494,8 @@ EXECUTE pre_stmt_f_mongo_test(2); Sort Key: f_mongo_test.b -> Foreign Scan on public.f_mongo_test Output: b - Filter: (f_mongo_test.a = 2) Foreign Namespace: mongo_fdw_regress.mongo_test -(7 rows) +(6 rows) EXECUTE pre_stmt_f_mongo_test(2); b @@ -321,6 +508,7 @@ DELETE FROM f_mongo_test WHERE a != 0; 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/mongo_fdw.c b/mongo_fdw.c index 469f2a0..5cc7e94 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -247,6 +247,29 @@ MongoGetForeignRelSize(PlannerInfo *root, Oid foreigntableid) { double documentCount = ForeignTableDocumentCount(foreigntableid); + MongoFdwRelationInfo *fpinfo; + ListCell *lc; + + /* + * 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. + */ + foreach(lc, baserel->baserestrictinfo) + { + RestrictInfo *ri = (RestrictInfo *) lfirst(lc); + + if (mongo_is_foreign_expr(root, baserel, ri->clause)) + fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri); + else + fpinfo->local_conds = lappend(fpinfo->local_conds, ri); + } if (documentCount > 0.0) { @@ -297,6 +320,7 @@ MongoGetForeignPaths(PlannerInfo *root, Cost startupCost = 0.0; Cost totalCost = 0.0; Path *foreignPath; + MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) baserel->fdw_private; documentCount = ForeignTableDocumentCount(foreigntableid); @@ -306,7 +330,7 @@ MongoGetForeignPaths(PlannerInfo *root, * We estimate the number of rows returned after restriction * qualifiers are applied by MongoDB. */ - opExpressionList = ApplicableOpExpressionList(baserel); + opExpressionList = fpinfo->remote_conds; documentSelectivity = clauselist_selectivity(root, opExpressionList, 0, JOIN_INNER, NULL); inputRowCount = clamp_row_est(documentCount * documentSelectivity); @@ -376,13 +400,15 @@ MongoGetForeignPlan(PlannerInfo *root, List *restrictionClauses, Plan *outer_plan) { + MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; Index scanRangeTableIndex = foreignrel->relid; ForeignScan *foreignScan; List *foreignPrivateList; - List *opExpressionList; List *columnList; List *scan_var_list; ListCell *lc; + List *local_exprs = NIL; + List *remote_exprs = NIL; #if PG_VERSION_NUM >= 90600 scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs, @@ -416,28 +442,40 @@ MongoGetForeignPlan(PlannerInfo *root, } /* - * We push down applicable restriction clauses to MongoDB, but for - * simplicity we currently put all the restrictionClauses into the plan - * node's qual list for the executor to re-check. So all we have to do - * here is strip RestrictInfo nodes from the clauses and ignore - * pseudoconstants (which will be handled elsewhere). + * 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. */ - restrictionClauses = extract_actual_clauses(restrictionClauses, false); + foreach(lc, restrictionClauses) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); - /* - * Find the foreign relation's clauses, which can be transformed to - * equivalent MongoDB queries. - */ - opExpressionList = ApplicableOpExpressionList(foreignrel); + 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 (mongo_is_foreign_expr(root, foreignrel, rinfo->clause)) + remote_exprs = lappend(remote_exprs, rinfo->clause); + else + local_exprs = lappend(local_exprs, rinfo->clause); + } /* We don't need to serialize column list as lists are copiable */ columnList = ColumnList(foreignrel); /* Construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(columnList, opExpressionList); + foreignPrivateList = list_make2(columnList, remote_exprs); /* Create the foreign scan node */ - foreignScan = make_foreignscan(targetList, restrictionClauses, + foreignScan = make_foreignscan(targetList, local_exprs, scanRangeTableIndex, NIL, /* No expressions to evaluate */ foreignPrivateList diff --git a/mongo_fdw.h b/mongo_fdw.h index 09f8202..f88babd 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -285,6 +285,18 @@ typedef struct ColumnMapping Oid columnArrayTypeId; } 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 +{ + /* baserestrictinfo clauses, broken down into safe and unsafe subsets. */ + List *local_conds; + List *remote_conds; +} MongoFdwRelationInfo; + /* options.c */ extern MongoFdwOptions *mongo_get_options(Oid foreignTableId); extern void mongo_free_options(MongoFdwOptions *options); @@ -299,11 +311,12 @@ extern void mongo_cleanup_connection(void); extern void mongo_release_connection(MONGO_CONN *conn); /* Function declarations related to creating the mongo query */ -extern List *ApplicableOpExpressionList(RelOptInfo *baserel); extern BSON *QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode); extern List *ColumnList(RelOptInfo *baserel); +extern bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, + Expr *expression); /* Function declarations for foreign data wrapper */ extern Datum mongo_fdw_handler(PG_FUNCTION_ARGS); diff --git a/mongo_query.c b/mongo_query.c index c1d8f75..d743ea4 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -18,6 +18,7 @@ #include #include +#include "catalog/pg_collation.h" #ifdef META_DRIVER #include "mongoc.h" #else @@ -32,9 +33,36 @@ #include "optimizer/optimizer.h" #endif +/* + * 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 */ + unsigned short varcount; /* Var count */ + unsigned short opexprcount; +} 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 Expr *FindArgumentOfType(List *argumentList, NodeTag argumentType); -static char *MongoOperatorName(const char *operatorName); static List *EqualityOperatorList(List *operatorList); static List *UniqueColumnList(List *operatorList); static List *ColumnOperatorList(Var *column, List *operatorList); @@ -43,83 +71,9 @@ static void AppendConstantValue(BSON *queryDocument, const char *keyName, static void AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, ForeignScanState *scanStateNode); - -/* - * ApplicableOpExpressionList - * Walks over all filter clauses that relate to this foreign table, and - * chooses applicable clauses that we know we can translate into Mongo - * queries. - * - * Currently, these clauses include comparison expressions - * that have a column and a constant as arguments. For example, - * "o_orderdate >= date '1994-01-01' + interval '1' year" is an applicable - * expression. - */ -List * -ApplicableOpExpressionList(RelOptInfo *baserel) -{ - List *opExpressionList = NIL; - List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell; - - foreach(restrictInfoCell, restrictInfoList) - { - RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); - Expr *expression = restrictInfo->clause; - OpExpr *opExpression; - char *operatorName; - List *argumentList; - Var *column; - Const *constant; - bool equalsOperator = false; - bool constantIsArray = false; - Param *paramNode; - - /* We only support operator expressions */ - if (!IsA(expression, OpExpr)) - continue; - - opExpression = (OpExpr *) expression; - operatorName = get_opname(opExpression->opno); - - /* We only support =, <, >, <=, >=, and <> operators */ - if (strncmp(operatorName, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) - equalsOperator = true; - - if (!equalsOperator && MongoOperatorName(operatorName) == NULL) - continue; - - /* - * We only support simple binary operators that compare a column - * against a constant. If the expression is a tree, we don't recurse - * into it. - */ - argumentList = opExpression->args; - column = (Var *) FindArgumentOfType(argumentList, T_Var); - constant = (Const *) FindArgumentOfType(argumentList, T_Const); - paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); - - /* - * We don't push down operators where the constant is an array, since - * conditional operators for arrays in MongoDB aren't properly - * defined. For example, {similar_products : [ "B0009S4IJW", - * "6301964144" ]} finds results that are equal to the array, but - * {similar_products: {$gte: [ "B0009S4IJW", "6301964144" ]}} returns - * an empty set. - */ - if (constant != NULL) - if (OidIsValid(get_element_type(constant->consttype))) - constantIsArray = true; - - if (column != NULL && constant != NULL && !constantIsArray) - opExpressionList = lappend(opExpressionList, opExpression); - - if (column != NULL && paramNode != NULL) - opExpressionList = lappend(opExpressionList, opExpression); - } - - return opExpressionList; -} +static bool foreign_expr_walker(Node *node, + foreign_glob_cxt *glob_cxt, + foreign_loc_cxt *outer_cxt); /* * FindArgumentOfType @@ -136,6 +90,10 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) { Expr *argument = (Expr *) lfirst(argumentCell); + /* For RelabelType type, examine the inner node */ + if (IsA(argument, RelabelType)) + argument = ((RelabelType *) argument)->arg; + if (nodeTag(argument) == argumentType) { foundArgument = argument; @@ -280,7 +238,7 @@ QueryDocument(Oid relationId, List *opExpressionList, * Takes in the given PostgreSQL comparison operator name, and returns its * equivalent in MongoDB. */ -static char * +char * MongoOperatorName(const char *operatorName) { const char *mongoOperatorName = NULL; @@ -776,3 +734,320 @@ ColumnList(RelOptInfo *baserel) 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. + * + * We only support simple binary operators that compare a column against a + * constant. If the expression is a tree, we don't recurse into it. + */ +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; + + /* Increment the Var count */ + glob_cxt->varcount++; + + /* + * If the Var is from the foreign table, we consider its + * collation (if any) safe to use. If it is from another + * table, we treat its collation the same way as we would a + * Param's collation, i.e. it's not safe for it to have a + * non-default collation. + */ + if (var->varno == glob_cxt->foreignrel->relid && + 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 */ + collation = var->varcollid; + if (var->varcollid != InvalidOid && + var->varcollid != DEFAULT_COLLATION_OID) + return false; + + if (collation == InvalidOid || + collation == DEFAULT_COLLATION_OID) + { + /* + * It's noncollatable, or it's safe to combine with a + * collatable foreign Var, so set state to NONE. + */ + state = FDW_COLLATE_NONE; + } + else + { + /* + * Do not fail right away, since the Var might appear + * in a collation-insensitive context. + */ + state = FDW_COLLATE_UNSAFE; + } + } + } + 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); + + /* Increment the operator expression count */ + glob_cxt->opexprcount++; + + /* We only support =, <, >, <=, >=, and <> operators */ + if (!(strncmp(oname, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) && + (MongoOperatorName(oname) == NULL)) + return false; + + /* + * Recurse to input subexpressions. + */ + if (glob_cxt->opexprcount > 1 || + !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 (same logic as for a real function). + */ + 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. + * + * If comparison is between two columns of same table then we + * don't push down because currently building corresponding + * MongoDB query not possible with the help of MongoC driver. + */ + foreach(lc, l) + { + if ((!foreign_expr_walker((Node *) lfirst(lc), + glob_cxt, &inner_cxt)) || + glob_cxt->varcount > 1) + return false; + } + + /* + * When processing a list, collation state just bubbles up + * from the list elements. + */ + collation = inner_cxt.collation; + state = inner_cxt.state; + } + 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) +{ + foreign_glob_cxt glob_cxt; + foreign_loc_cxt loc_cxt; + + /* + * Check that the expression consists of nodes that are safe to execute + * remotely. + */ + glob_cxt.root = root; + glob_cxt.foreignrel = baserel; + glob_cxt.varcount = 0; + glob_cxt.opexprcount = 0; + loc_cxt.collation = InvalidOid; + loc_cxt.state = FDW_COLLATE_NONE; + if (!foreign_expr_walker((Node *) expression, &glob_cxt, &loc_cxt)) + return false; + + /* Expressions examined here should be boolean, ie noncollatable */ + Assert(loc_cxt.collation == InvalidOid); + Assert(loc_cxt.state == FDW_COLLATE_NONE); + + /* OK to evaluate on the remote server */ + return true; +} diff --git a/mongo_query.h b/mongo_query.h index 6ed4a31..7a471ea 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -23,4 +23,6 @@ bool AppendMongoValue(BSON *queryDocument, bool isnull, Oid id); +char *MongoOperatorName(const char *operatorName); + #endif /* MONGO_QUERY_H */ diff --git a/sql/pushdown.sql b/sql/pushdown.sql index b20137c..9c0d107 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -20,6 +20,13 @@ CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR( 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) + 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; @@ -99,11 +106,74 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c3 LIKE 'MANA%' ORDER BY c1; --- Pushdown in prepared statement. -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'); +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT a FROM f_mongo_test + WHERE a%2 = 1 + ORDER BY a; +SELECT a FROM f_mongo_test + WHERE a%2 = 1 + ORDER BY a; + +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; + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 = 'EMP10'; +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 = 'EMP10'; + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 < 'EMP10'; +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 < 'EMP10'; + +-- Should not push down if two columns of same table is +-- involved in single WHERE clause operator expression. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c6 FROM f_test_tbl1 + WHERE c1 = c6 AND c1 = 1100 + ORDER BY c1; +SELECT c1, c6 FROM f_test_tbl1 + WHERE c1 = c6 AND c1 = 1100 + ORDER BY c1; + +-- Nested operator expression in WHERE clause. Shouldn't push down. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > 0::BOOLEAN; +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > 0::BOOLEAN; + +-- 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) @@ -118,6 +188,7 @@ DELETE FROM f_mongo_test WHERE a != 0; 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; From 654780f440ddc6ad46a6aa7b9f87f5dce644b9d8 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 9 Feb 2021 19:24:55 +0530 Subject: [PATCH 148/239] Update EDB copyrights for 2021. --- LICENSE | 2 +- Makefile | 2 +- Makefile.legacy | 2 +- Makefile.meta | 2 +- README.md | 2 +- autogen.sh | 2 +- connection.c | 2 +- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 2 +- mongo_fdw.control | 2 +- mongo_fdw.h | 2 +- mongo_query.c | 2 +- mongo_query.h | 2 +- mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 2 +- option.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/LICENSE b/LICENSE index 24fe448..9d793e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MongoDB Foreign Data Wrapper for PostgreSQL -Copyright (c) 2011-2020, EnterpriseDB Corporation. +Copyright (c) 2011-2021, EnterpriseDB Corporation. Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is diff --git a/Makefile b/Makefile index f2771eb..3d263cc 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.legacy b/Makefile.legacy index f2771eb..3d263cc 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.meta b/Makefile.meta index 3e1bf00..9996b82 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -1,6 +1,6 @@ # mongo_fdw/Makefile.meta # -# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index 596c14e..1810c0c 100644 --- a/README.md +++ b/README.md @@ -259,7 +259,7 @@ also support mongo_fdw. License ------- -Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. This program is free software: you can redistribute it and/or modify it diff --git a/autogen.sh b/autogen.sh index f2190d5..bc6a452 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index 5e5d976..7de33c7 100644 --- a/connection.c +++ b/connection.c @@ -4,7 +4,7 @@ * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index 1fb4df2..6837a98 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index 59a1543..e4603c9 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw.c b/mongo_fdw.c index 5cc7e94..5e9dcd8 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index 5c7b90b..fd56d8a 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index f88babd..1e365d9 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index d743ea4..76f4180 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index 7a471ea..969b7d9 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index ad4ce09..abfa577 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index f895250..fc1d22f 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index b8c1366..f64b31f 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index 324589e..d59d85b 100644 --- a/option.c +++ b/option.c @@ -4,7 +4,7 @@ * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2020, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From 1abfac7d82c2e8899da6bd2ac2e3d64e6ac42403 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 23 Feb 2021 18:14:10 +0530 Subject: [PATCH 149/239] Push only OpExpr clauses to the remote server. Earlier we were doing the same, but commit fce56c68aaa03f6aa512f2466b25bdf92a72147d broke that. Fix it. FDW-297, Vaibhav Dalvi, reviewed by Jeevan Chalke. --- data/mongo_test_data.js | 4 ++-- expected/pushdown.out | 26 +++++++++++++++++++++++++- mongo_fdw.c | 12 ++++++++---- sql/pushdown.sql | 11 ++++++++++- 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index da0f603..7c7e0a1 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -38,6 +38,6 @@ db.test_tbl1.insertMany([ {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]}, - {name: "vdd", marks: [29, 31]} + {name: "dvd", marks: [23, 24], pass: false}, + {name: "vdd", marks: [29, 31], pass: true} ]); diff --git a/expected/pushdown.out b/expected/pushdown.out index ed01682..d4c805e 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -17,7 +17,7 @@ CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR( 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) +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'); @@ -503,6 +503,30 @@ EXECUTE pre_stmt_f_mongo_test(2); 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; + 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.pass + Foreign Namespace: mongo_fdw_regress.test_tbl3 +(7 rows) + +SELECT name, marks FROM f_test_tbl3 + WHERE pass = true + ORDER BY name; + name | marks +------+--------- + vdd | {29,31} +(1 row) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP FOREIGN TABLE f_mongo_test; diff --git a/mongo_fdw.c b/mongo_fdw.c index 5e9dcd8..974eb8b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -259,13 +259,15 @@ MongoGetForeignRelSize(PlannerInfo *root, /* * Identify which baserestrictinfo clauses can be sent to the remote - * server and which can't. + * 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)) + if (IsA(ri->clause, OpExpr) && + mongo_is_foreign_expr(root, baserel, ri->clause)) fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri); else fpinfo->local_conds = lappend(fpinfo->local_conds, ri); @@ -446,7 +448,8 @@ MongoGetForeignPlan(PlannerInfo *root, * 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. + * 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) { @@ -462,7 +465,8 @@ MongoGetForeignPlan(PlannerInfo *root, 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 (mongo_is_foreign_expr(root, foreignrel, rinfo->clause)) + else if (IsA(rinfo->clause, OpExpr) && + mongo_is_foreign_expr(root, foreignrel, rinfo->clause)) remote_exprs = lappend(remote_exprs, rinfo->clause); else local_exprs = lappend(local_exprs, rinfo->clause); diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 9c0d107..9ee6f08 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -20,7 +20,7 @@ CREATE FOREIGN TABLE f_test_tbl1 (_id name, c1 INTEGER, c2 VARCHAR(10), c3 CHAR( 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) +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. @@ -183,6 +183,15 @@ 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; +SELECT name, marks FROM f_test_tbl3 + WHERE pass = true + ORDER BY name; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP FOREIGN TABLE f_mongo_test; From b0e5c3f50ff44d23a9c0758d3d403ef5a489f287 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 26 Feb 2021 15:31:31 +0530 Subject: [PATCH 150/239] Remove support for v9.5. Standard Support for both EDB Postgres Advanced Server 9.5 and PostgreSQL 9.5 ended in February 2021. Adjust Makefile so that we restrict compilation of mongo_fdw code against v9.5. Update the README accordingly. Vaibhav Dalvi --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- README.md | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 3d263cc..2e52082 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) - $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) + $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 3d263cc..2e52082 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) - $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) + $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 9996b82..56d2539 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.5 9.6 10 11 12 13)) - $(error PostgreSQL 9.5, 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) + $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) endif diff --git a/README.md b/README.md index 1810c0c..a5cc8b2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 9.5, 9.6, 10, 11, 12, and 13. +Postgres Advanced Server 9.6, 10, 11, 12, and 13. Installation ------------ From 91a5cc3f78461a0adfc1fc79948f3b9569df55bc Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 11 Mar 2021 19:33:16 +0530 Subject: [PATCH 151/239] Revert "Update LICENSE file." This reverts commit 9b798ecb7f53130fb466fbf2d33fd10427a99d72. --- LICENSE | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 15 deletions(-) diff --git a/LICENSE b/LICENSE index 9d793e6..6600f1c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,165 @@ -MongoDB Foreign Data Wrapper for PostgreSQL +GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2011-2021, EnterpriseDB Corporation. + 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. -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose, without fee, and without a written agreement is -hereby granted, provided that the above copyright notice and this paragraph and -the following two paragraphs appear in all copies. -IN NO EVENT SHALL ENTERPRISEDB CORPORATION BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST -PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF -ENTERPRISEDB CORPORATION HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 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. -ENTERPRISEDB CORPORATION SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND -ENTERPRISEDB CORPORATION HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, -UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + 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. From 6e75904934d53f8cc105f4773d95d82996a7cc06 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 14 Jun 2021 11:25:00 +0530 Subject: [PATCH 152/239] Correct the exit status in autogen.sh. Earlier, if any of the statements fails, autogen.sh script was simply continuing with the next statement, and in the end, returns 0 as if nothing failed. Fix it so that script does fail upon any error and returns a non-zero status code. Reported on GitHub through issues #150 by Andreas Scherbaum (andreasscherbaum). FDW-379, Vaibhav Dalvi, reviewed by Jeevan Ladhe, tested by Rajkumar Raghuwanshi. --- autogen.sh | 82 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/autogen.sh b/autogen.sh index bc6a452..10d8a20 100755 --- a/autogen.sh +++ b/autogen.sh @@ -27,10 +27,10 @@ fi # 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 && + 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 } @@ -39,10 +39,10 @@ function checkout_mongo_driver # function checkout_legacy_branch { - rm -rf mongo-c-driver - wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz - tar -zxf v0.8.tar.gz - mv mongo-c-driver-0.8 mongo-c-driver + rm -rf mongo-c-driver && + wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz && + tar -zxf v0.8.tar.gz && + mv mongo-c-driver-0.8 mongo-c-driver && rm -rf v0.8.tar.gz } ## @@ -50,12 +50,12 @@ function checkout_legacy_branch # 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 && + 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 } @@ -65,9 +65,9 @@ function checkout_json_lib # function install_json_lib { - cd json-c - cmake3 $JSONC_CFLAGS . - make install + cd json-c && + cmake3 $JSONC_CFLAGS . && + make install && cd .. } @@ -76,9 +76,9 @@ function install_json_lib # function install_mongoc_driver { - cd mongo-c-driver - cmake3 -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . - make install + cd mongo-c-driver && + cmake3 -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . && + make install && cd .. } @@ -97,28 +97,44 @@ function cleanup # function create_config { - echo "#ifdef __CONFIG__" >> config.h - echo "#define META_DRIVER" >> config.h + echo "#ifdef __CONFIG__" >> config.h && + echo "#define META_DRIVER" >> config.h && echo "#endif" >> config.h } cleanup if [ "--with-legacy" = $1 ]; then - checkout_json_lib - checkout_legacy_branch - install_json_lib + checkout_json_lib && + checkout_legacy_branch && + install_json_lib && cp Makefile.legacy Makefile - echo "Done" + + ret=$? + if [ "$ret" -ne 0 ]; then + echo "Failed" + exit $ret + else + echo "Done" + exit 0 + fi elif [ "--with-master" == $1 ]; then - checkout_mongo_driver - checkout_json_lib - install_mongoc_driver - install_json_lib - create_config - export PKG_CONFIG_PATH=mongo-c-driver/src/libmongoc/src:mongo-c-driver/src/libbson/src + checkout_mongo_driver && + checkout_json_lib && + install_mongoc_driver && + install_json_lib && + create_config && + export PKG_CONFIG_PATH=mongo-c-driver/src/libmongoc/src:mongo-c-driver/src/libbson/src && cp Makefile.meta Makefile - echo "Done" + + ret=$? + if [ "$ret" -ne 0 ]; then + echo "Failed" + exit $ret + else + echo "Done" + exit 0 + fi else echo "Usage: autogen.sh --[with-legacy | with-master]" fi From 8e62592f10b75db195faaf67896a32daecb18b67 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 14 Jun 2021 11:25:18 +0530 Subject: [PATCH 153/239] Fall back to cmake if cmake3 is not present. The autogen.sh is using cmake3 to compile the jsonc library and mongoc driver. However, it is observed that some of the platforms do not have that, and thus script fails. So, if cmake3 is not present on the system, then fall back to cmake and use that. Note that if the CMake version is below the required one (i.e. 3.1), then the script will fail. Reported on GitHub through issues #149 by Andreas Scherbaum (andreasscherbaum). FDW-379, Vaibhav Dalvi, reviewed by Jeevan Ladhe, tested by Rajkumar Raghuwanshi. --- autogen.sh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index 10d8a20..6ba463e 100755 --- a/autogen.sh +++ b/autogen.sh @@ -22,6 +22,11 @@ if [ "$#" -ne 1 ]; then exit fi +CMAKE_COMMAND='cmake3' +if ! [ -x "$(command -v cmake3)" ]; then + CMAKE_COMMAND='cmake' +fi + ### # Pull the latest version of Monggo C Driver's master branch # @@ -66,7 +71,7 @@ function checkout_json_lib function install_json_lib { cd json-c && - cmake3 $JSONC_CFLAGS . && + $CMAKE_COMMAND $JSONC_CFLAGS . && make install && cd .. } @@ -77,7 +82,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver && - cmake3 -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . && + $CMAKE_COMMAND -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . && make install && cd .. } From ceb03dc0218594e128f31e6d9395110710ef504b Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 15 Jun 2021 11:22:46 +0530 Subject: [PATCH 154/239] Stamp 5.2.9. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index 4c94028..bf33b29 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50208 + 50209 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 9dc5af7..8a371c6 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50208 + 50209 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 974eb8b..8dc55f4 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -54,9 +54,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.2.8 so number will be 50208 + * our version is 5.2.9 so number will be 50209 */ -#define CODE_VERSION 50208 +#define CODE_VERSION 50209 extern PGDLLEXPORT void _PG_init(void); const char *EscapeJsonString(const char *string); From 1fbc1904ca77958aba2bf22d3adf62e684b4fd48 Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Thu, 1 Jul 2021 09:53:10 +0530 Subject: [PATCH 155/239] Add support for PostgreSQL 14 and EDB Postgres Advanced Server 14. This fixes the errors, warnings and regression test failures appearing when compiled with PostgreSQL 14 and EDB Postgres Advanced Server 14. FDW-366, Vaibhav Dalvi, reviewed by Suraj Kharage. --- Makefile | 4 ++-- Makefile.legacy | 4 ++-- Makefile.meta | 4 ++-- README.md | 2 +- expected/dml.out | 6 ------ mongo_fdw.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++-- mongo_fdw.h | 4 +--- sql/dml.sql | 1 - 8 files changed, 60 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 2e52082..450b87e 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) - $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) + $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 2e52082..450b87e 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) - $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) + $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 56d2539..ccdd458 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13)) - $(error PostgreSQL 9.6, 10, 11, 12, or 13 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) + $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/README.md b/README.md index a5cc8b2..81e1489 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 9.6, 10, 11, 12, and 13. +Postgres Advanced Server 9.6, 10, 11, 12, 13, and 14. Installation ------------ diff --git a/expected/dml.out b/expected/dml.out index b83707f..969131a 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -133,12 +133,6 @@ SELECT a,b FROM f_mongo_test3 ORDER BY 1, 2; (0 rows) -- FDW-158: Fix server crash when analyzing a foreign table. -SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; - reltuples ------------ - 0 -(1 row) - ANALYZE f_mongo_test; -- Should give correct number of rows now. SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; diff --git a/mongo_fdw.c b/mongo_fdw.c index 8dc55f4..2db051b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -33,6 +33,9 @@ #include "miscadmin.h" #include "mongo_fdw.h" #include "mongo_query.h" +#if PG_VERSION_NUM >= 140000 +#include "optimizer/appendinfo.h" +#endif #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif @@ -94,9 +97,16 @@ static TupleTableSlot *MongoExecForeignDelete(EState *estate, 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, @@ -872,6 +882,26 @@ MongoBeginForeignModify(ModifyTableState *mtstate, 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) { @@ -984,14 +1014,24 @@ MongoExecForeignInsert(EState *estate, * 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 @@ -1004,16 +1044,25 @@ MongoAddForeignUpdateTargets(Query *parsetree, #endif /* 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); - /* Wrap it in a TLE with the right name ... */ + /* 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), @@ -1021,6 +1070,7 @@ MongoAddForeignUpdateTargets(Query *parsetree, /* ... And add it to the query's targetlist */ parsetree->targetList = lappend(parsetree->targetList, tle); +#endif } static TupleTableSlot * @@ -1043,7 +1093,7 @@ MongoExecForeignUpdate(EState *estate, foreignTableId = RelationGetRelid(resultRelInfo->ri_RelationDesc); /* Get the id that was passed up as a resjunk column */ - datum = ExecGetJunkAttribute(planSlot, 1, &isNull); + datum = ExecGetJunkAttribute(planSlot, fmstate->rowidAttno, &isNull); #if PG_VERSION_NUM < 110000 columnName = get_relid_attribute_name(foreignTableId, 1); diff --git a/mongo_fdw.h b/mongo_fdw.h index 1e365d9..de71e4c 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -264,9 +264,7 @@ typedef struct MongoFdwModifyState BSON *queryDocument; /* Bson Document */ MongoFdwOptions *options; - - /* Working memory context */ - MemoryContext temp_cxt; /* context for per-tuple temporary data */ + AttrNumber rowidAttno; /* attnum of resjunk rowid column */ } MongoFdwModifyState; /* diff --git a/sql/dml.sql b/sql/dml.sql index 19513b4..cbff482 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -65,7 +65,6 @@ 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. -SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; ANALYZE f_mongo_test; -- Should give correct number of rows now. SELECT reltuples FROM pg_class WHERE relname = 'f_mongo_test'; From b559c3e28dbc8a13cefcff6cacee8b27e5f43c8c Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 2 Aug 2021 10:22:06 +0530 Subject: [PATCH 156/239] Use environment variables in tests for connection parameters. The regression tests currently have connection parameters like host, port, user, password are hard-coded in each of the test sql file. If someone has to run the regressions against a MongoDB server other than localhost, and different credentials, one has to change all the sql files manually. Change these variables to read from respective environment variables in the sql files. These environment variables are already set to default values in mongodb_init.sh script. Update the README accordingly. Also, a test retrieving user mapping and server options expects these hard coded values in the output and fails when the regression runs against different MongoDB server with different credentials. Fix them too by writing a plpgsql block. FDW-323, Vaibhav Dalvi, reviewed by Suraj Kharage. --- README.md | 31 ++++++++++++++++ expected/connection_validation.out | 13 +++---- expected/connection_validation_1.out | 13 +++---- expected/dml.out | 13 +++---- expected/pushdown.out | 13 +++---- expected/select.out | 14 ++++---- expected/select_1.out | 14 ++++---- expected/server_options.out | 54 +++++++++++++++++++++------- expected/server_options_1.out | 54 +++++++++++++++++++++------- mongodb_init.sh | 8 ++--- sql/connection_validation.sql | 13 +++---- sql/dml.sql | 13 +++---- sql/pushdown.sql | 13 +++---- sql/select.sql | 14 ++++---- sql/server_options.sql | 46 ++++++++++++++++++------ 15 files changed, 223 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 81e1489..48686b1 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,37 @@ compiled and linked. Note: Make sure you have permission to "/usr/local" (default installation location) folder. +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: + + ``` + $ export PATH=/usr/local/pgsql/bin/:$PATH + ``` + +2. Compile the code using make. + + ``` + $ make USE_PGXS=1 + ``` + +3. Finally install the foreign data wrapper. + + ``` + $ make USE_PGXS=1 install + ``` + +4. Running regression test. + + ``` + $ 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]. Enhancements diff --git a/expected/connection_validation.out b/expected/connection_validation.out index c009e9d..33a538a 100644 --- a/expected/connection_validation.out +++ b/expected/connection_validation.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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 diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out index b788466..0aa5205 100644 --- a/expected/connection_validation_1.out +++ b/expected/connection_validation_1.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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 diff --git a/expected/dml.out b/expected/dml.out index 969131a..2631f88 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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 diff --git a/expected/pushdown.out b/expected/pushdown.out index d4c805e..c2bbfe0 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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 diff --git a/expected/select.out b/expected/select.out index bf33b29..cc3f19d 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1,11 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' --- Before running this file User must create database mongo_fdw_regress & +\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 and -- mongo_fdw_regress1 databases on MongoDB with all permission for --- 'edb' user with 'edb' password and ran mongodb_init.sh --- file to load collections. +-- 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 diff --git a/expected/select_1.out b/expected/select_1.out index 8a371c6..43c36da 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1,11 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' --- Before running this file User must create database mongo_fdw_regress & +\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 and -- mongo_fdw_regress1 databases on MongoDB with all permission for --- 'edb' user with 'edb' password and ran mongodb_init.sh --- file to load collections. +-- 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 diff --git a/expected/server_options.out b/expected/server_options.out index 7341299..aff26ff 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; NOTICE: extension "mongo_fdw" already exists, skipping @@ -18,13 +19,40 @@ 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 -SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" - 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; - Extension | Server | Server_Options | User_Mapping_Options ------------+--------------+--------------------------------+---------------------- - mongo_fdw | mongo_server | {address=localhost,port=27017} | +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 diff --git a/expected/server_options_1.out b/expected/server_options_1.out index 464c7e5..fa593c9 100644 --- a/expected/server_options_1.out +++ b/expected/server_options_1.out @@ -1,10 +1,11 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; NOTICE: extension "mongo_fdw" already exists, skipping @@ -18,13 +19,40 @@ 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 -SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" - 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; - Extension | Server | Server_Options | User_Mapping_Options ------------+--------------+--------------------------------+---------------------- - mongo_fdw | mongo_server | {address=localhost,port=27017} | +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 diff --git a/mongodb_init.sh b/mongodb_init.sh index 46d6b07..8a140d6 100755 --- a/mongodb_init.sh +++ b/mongodb_init.sh @@ -1,8 +1,8 @@ #!/bin/sh -MONGO_HOST="127.0.0.1" -MONGO_PORT="27017" -MONGO_USER_NAME="edb" -MONGO_PWD="edb" +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. diff --git a/sql/connection_validation.sql b/sql/connection_validation.sql index 821d8b0..0030ef4 100644 --- a/sql/connection_validation.sql +++ b/sql/connection_validation.sql @@ -1,11 +1,12 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; diff --git a/sql/dml.sql b/sql/dml.sql index cbff482..0a06a6a 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -1,11 +1,12 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 9ee6f08..47f841e 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -1,11 +1,12 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; diff --git a/sql/select.sql b/sql/select.sql index 4afee9d..e6fac3e 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -1,12 +1,12 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 & +-- Before running this file User must create database mongo_fdw_regress and -- mongo_fdw_regress1 databases on MongoDB with all permission for --- 'edb' user with 'edb' password and ran mongodb_init.sh --- file to load collections. +-- 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; diff --git a/sql/server_options.sql b/sql/server_options.sql index 8c8951c..cac2f8e 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -1,11 +1,12 @@ -\set MONGO_HOST '\'localhost\'' -\set MONGO_PORT '\'27017\'' -\set MONGO_USER_NAME '\'edb\'' -\set MONGO_PASS '\'edb\'' +\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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for 'edb' user --- with 'edb' password and ran mongodb_init.sh file to load collections. +-- mongo_fdw_regress1 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; @@ -19,10 +20,35 @@ CREATE SERVER mongo_server1 FOREIGN DATA WRAPPER mongo_fdw ALTER SERVER mongo_server OPTIONS (SET port '65537'); -- Validate extension, server and mapping details -SELECT e.fdwname AS "Extension", srvname AS "Server", s.srvoptions AS "Server_Options", u.umoptions AS "User_Mapping_Options" - 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; +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) From 2f20c4a4ff03b0550b6ca818a0af9860b376c173 Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Sat, 14 Aug 2021 20:48:51 +0530 Subject: [PATCH 157/239] Fix expected output differences due to hint messages. The HINT messages in connection_validation.out vary for different MongoDB versions as well as platforms. Avoid these hint messages by setting the verbosity to terse, so that we can get consistent output across platforms and irrespective of the MongoDB version being used. FDW-403, Vaibhav Dalvi, reviwed by Jeevan Ladhe. --- expected/connection_validation.out | 9 +-------- expected/connection_validation_1.out | 9 +-------- sql/connection_validation.sql | 1 + 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/expected/connection_validation.out b/expected/connection_validation.out index 33a538a..bbb5412 100644 --- a/expected/connection_validation.out +++ b/expected/connection_validation.out @@ -1,3 +1,4 @@ +\set VERBOSITY terse \set MONGO_HOST `echo \'"$MONGO_HOST"\'` \set MONGO_PORT `echo \'"$MONGO_PORT"\'` \set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'` @@ -30,16 +31,12 @@ ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); -- Should fail with an error INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED'); ERROR: could not connect to server mongo_server -HINT: Mongo error: "No suitable servers found (`serverSelectionTryOnce` set): [connection refused calling ismaster on '127.0.0.5:27017']" UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No servers yet eligible for rescan" DELETE FROM f_mongo_test WHERE a = 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No servers yet eligible for rescan" SELECT a, b FROM f_mongo_test ORDER BY 1, 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "No servers yet eligible for rescan" -- Set correct address for mongo_server ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST); -- Should able to insert the data @@ -53,16 +50,12 @@ CREATE USER MAPPING FOR public SERVER mongo_server -- Should fail with an error INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED'); ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3; ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." DELETE FROM f_mongo_test WHERE a = 3; ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." SELECT a, b FROM f_mongo_test ORDER BY 1, 2; ERROR: could not connect to server mongo_server -HINT: Mongo error: "Authentication failed." -- Drop user mapping and create without username and password for public -- user mapping DROP USER MAPPING FOR public SERVER mongo_server; diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out index 0aa5205..09d70ff 100644 --- a/expected/connection_validation_1.out +++ b/expected/connection_validation_1.out @@ -1,3 +1,4 @@ +\set VERBOSITY terse \set MONGO_HOST `echo \'"$MONGO_HOST"\'` \set MONGO_PORT `echo \'"$MONGO_PORT"\'` \set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'` @@ -30,16 +31,12 @@ ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); -- Should fail with an error INSERT INTO f_mongo_test VALUES ('0', 2, 'RECORD INSERTED'); ERROR: could not connect to 127.0.0.5:27017 -HINT: Mongo driver connection error: 2. UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; ERROR: could not connect to 127.0.0.5:27017 -HINT: Mongo driver connection error: 2. DELETE FROM f_mongo_test WHERE a = 2; ERROR: could not connect to 127.0.0.5:27017 -HINT: Mongo driver connection error: 2. SELECT a, b FROM f_mongo_test ORDER BY 1, 2; ERROR: could not connect to 127.0.0.5:27017 -HINT: Mongo driver connection error: 2. -- Set correct address for mongo_server ALTER SERVER mongo_server OPTIONS (SET address :MONGO_HOST); -- Should able to insert the data @@ -53,16 +50,12 @@ CREATE USER MAPPING FOR public SERVER mongo_server -- Should fail with an error INSERT INTO f_mongo_test VALUES ('0', 3, 'RECORD INSERTED'); ERROR: could not connect to localhost:27017 -HINT: Mongo driver connection error: UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3; ERROR: could not connect to localhost:27017 -HINT: Mongo driver connection error: DELETE FROM f_mongo_test WHERE a = 3; ERROR: could not connect to localhost:27017 -HINT: Mongo driver connection error: SELECT a, b FROM f_mongo_test ORDER BY 1, 2; ERROR: could not connect to localhost:27017 -HINT: Mongo driver connection error: -- Drop user mapping and create without username and password for public -- user mapping DROP USER MAPPING FOR public SERVER mongo_server; diff --git a/sql/connection_validation.sql b/sql/connection_validation.sql index 0030ef4..f0cee56 100644 --- a/sql/connection_validation.sql +++ b/sql/connection_validation.sql @@ -1,3 +1,4 @@ +\set VERBOSITY terse \set MONGO_HOST `echo \'"$MONGO_HOST"\'` \set MONGO_PORT `echo \'"$MONGO_PORT"\'` \set MONGO_USER_NAME `echo \'"$MONGO_USER_NAME"\'` From 85994ba68d754f80b1061f9e8f315bb4cd0246ff Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Sat, 14 Aug 2021 21:37:30 +0530 Subject: [PATCH 158/239] Fix the implementation for retrieving and validating the options. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function mongo_get_option_value() iterates over all the options and returns the value of the option requested as a c-string. For the boolean options ssl and weak_cert_validation also mongo_get_options() calls the same function mongo_get_option_value() and they end up being true always, as any string assigned to bool results in true. To fix this correctly, in mongo_get_options() retrieve the value using defGetBoolean() so that we get only boolean value. But, with the current implementation to retrieve every single option one needs to iterate over the entire list of options separately, leading to a quadratic complexity. Instead, fix this by iterating over the options list just once and remembering all the options on the go by retrieving their respective values using respective defGet*() APIs, rather than using the custom function mongo_get_option_value() which always returns a c-string. FDW-316, Vaibhav Dalvi, reviewed by Suraj Kharage, reported by Kashif Zeeshan. --- expected/server_options.out | 29 +++++++ expected/server_options_1.out | 38 +++++++++ option.c | 150 +++++++++++++++++----------------- sql/server_options.sql | 18 ++++ 4 files changed, 160 insertions(+), 75 deletions(-) diff --git a/expected/server_options.out b/expected/server_options.out index aff26ff..7157f61 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -87,6 +87,35 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; 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) + DROP FOREIGN TABLE f_mongo_test; DROP USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; diff --git a/expected/server_options_1.out b/expected/server_options_1.out index fa593c9..be67b0e 100644 --- a/expected/server_options_1.out +++ b/expected/server_options_1.out @@ -87,6 +87,44 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; 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: invalid option "ssl" +HINT: Valid options in this context are: address, port. +ALTER SERVER mongo_server OPTIONS (ssl 'x'); +ERROR: invalid option "ssl" +HINT: Valid options in this context are: address, port. +-- 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'); +ERROR: invalid option "ssl" +HINT: Valid options in this context are: address, port. +-- 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; + a | b +---+----------------------- + 0 | mongo_test collection +(1 row) + +\set VERBOSITY default +-- Switch back to 'false'. +ALTER SERVER mongo_server OPTIONS (SET ssl 'false'); +ERROR: option "ssl" not found +-- Should now be successful. +SELECT a, b FROM f_mongo_test ORDER BY 1, 2; + a | b +---+----------------------- + 0 | mongo_test collection +(1 row) + DROP FOREIGN TABLE f_mongo_test; DROP USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; diff --git a/option.c b/option.c index d59d85b..d32fddf 100644 --- a/option.c +++ b/option.c @@ -17,8 +17,6 @@ #include "miscadmin.h" #include "mongo_wrapper.h" -static char *mongo_get_option_value(List *optionList, const char *optionName); - /* * Validate the generic options given to a FOREIGN DATA WRAPPER, SERVER, * USER MAPPING or FOREIGN TABLE that uses postgres_fdw. @@ -91,6 +89,14 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) errmsg("port value \"%d\" is out of range for type %s", port, "unsigned short"))); } +#ifdef META_DRIVER + else if (strcmp(optionName, OPTION_NAME_SSL) == 0 || + strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0) + { + /* These accept only boolean values */ + (void) defGetBoolean(optionDef); + } +#endif } PG_RETURN_VOID(); @@ -142,9 +148,9 @@ mongo_get_options(Oid foreignTableId) ForeignTable *foreignTable; ForeignServer *foreignServer; UserMapping *mapping; - char *portName; List *optionList = NIL; MongoFdwOptions *options; + ListCell *lc; foreignTable = GetForeignTable(foreignTableId); foreignServer = GetForeignServer(foreignTable->serverid); @@ -157,59 +163,80 @@ mongo_get_options(Oid foreignTableId) options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); #ifdef META_DRIVER - options->readPreference = mongo_get_option_value(optionList, - OPTION_NAME_READ_PREFERENCE); - options->authenticationDatabase = mongo_get_option_value(optionList, - OPTION_NAME_AUTHENTICATION_DATABASE); - options->replicaSet = mongo_get_option_value(optionList, - OPTION_NAME_REPLICA_SET); - options->ssl = mongo_get_option_value(optionList, OPTION_NAME_SSL); - options->pem_file = mongo_get_option_value(optionList, - OPTION_NAME_PEM_FILE); - options->pem_pwd = mongo_get_option_value(optionList, OPTION_NAME_PEM_PWD); - options->ca_file = mongo_get_option_value(optionList, OPTION_NAME_CA_FILE); - options->ca_dir = mongo_get_option_value(optionList, - OPTION_NAME_CA_DIR); - options->crl_file = mongo_get_option_value(optionList, - OPTION_NAME_CRL_FILE); - options->weak_cert_validation = mongo_get_option_value(optionList, - OPTION_NAME_WEAK_CERT); + options->ssl = false; + options->weak_cert_validation = false; #endif - options->svr_address = mongo_get_option_value(optionList, - OPTION_NAME_ADDRESS); - if (options->svr_address == NULL) - options->svr_address = pstrdup(DEFAULT_IP_ADDRESS); - portName = mongo_get_option_value(optionList, OPTION_NAME_PORT); - if (portName == NULL) - options->svr_port = DEFAULT_PORT_NUMBER; - else + /* Loop through the options */ + foreach(lc, optionList) { - int32 port; + DefElem *def = (DefElem *) lfirst(lc); - port = pg_atoi(portName, sizeof(int32), 0); - if (port < 0 || port > USHRT_MAX) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("port value \"%d\" is out of range for type %s", - port, "unsigned short"))); - options->svr_port = (unsigned short) port; +#ifdef META_DRIVER + 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 /* This is for continuation */ +#endif + + if (strcmp(def->defname, OPTION_NAME_ADDRESS) == 0) + options->svr_address = 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 = defGetString(def); + + else if (strcmp(def->defname, OPTION_NAME_COLLECTION) == 0) + options->collectionName = 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); } - options->svr_database = mongo_get_option_value(optionList, - OPTION_NAME_DATABASE); - if (options->svr_database == NULL) - options->svr_database = pstrdup(DEFAULT_DATABASE_NAME); + /* 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; - options->collectionName = mongo_get_option_value(optionList, - OPTION_NAME_COLLECTION); - if (options->collectionName == NULL) - options->collectionName = get_rel_name(foreignTableId); + if (!options->svr_database) + options->svr_database = pstrdup(DEFAULT_DATABASE_NAME); - options->svr_username = mongo_get_option_value(optionList, - OPTION_NAME_USERNAME); - options->svr_password = mongo_get_option_value(optionList, - OPTION_NAME_PASSWORD); + if (!options->collectionName) + options->collectionName= get_rel_name(foreignTableId); return options; } @@ -224,30 +251,3 @@ mongo_free_options(MongoFdwOptions *options) pfree(options); } } - -/* - * mongo_get_option_value - * Walks over foreign table and foreign server options, and looks for the - * option with the given name. If found, the function returns the - * option's value. - */ -static char * -mongo_get_option_value(List *optionList, const char *optionName) -{ - ListCell *optionCell; - char *optionValue = NULL; - - foreach(optionCell, optionList) - { - DefElem *optionDef = (DefElem *) lfirst(optionCell); - char *optionDefName = optionDef->defname; - - if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) - { - optionValue = defGetString(optionDef); - break; - } - } - - return optionValue; -} diff --git a/sql/server_options.sql b/sql/server_options.sql index cac2f8e..f8082d5 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -60,6 +60,24 @@ 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; + DROP FOREIGN TABLE f_mongo_test; DROP USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; From 3d43081303b5913dce93d875c5da4031a0bcec5b Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Thu, 9 Sep 2021 17:24:17 +0530 Subject: [PATCH 159/239] Update default value for option weak_cert_validation in README. FDW-331, Vaibhav Dalvi, reviewed by Kashif Zeeshan. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48686b1..9e58ca6 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ The following options are only supported with meta driver: * `ca_file`: SSL option. * `ca_dir`: SSL option. * `crl_file`: SSL option. - * `weak_cert_validation`: SSL option. + * `weak_cert_validation`: SSL option, false [default]. The following parameters can be set on a MongoDB foreign table object: From 434b47a73faa56d15ab902423cb15efc56c79c07 Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Fri, 10 Sep 2021 12:44:42 +0530 Subject: [PATCH 160/239] Allow mongo-c and json-c drivers installation at custom locations. autogen.sh helps in avoiding manual installation and automates the download and installation of the appropriate drivers and libraries including mongo-c and json-c driver. But, these drivers get installed at the default locations e.g. /usr/lib/, and user might not have permission to write in this directory. This results in autogen.sh failure. Use environment variables MONGOC_INSTALL_DIR and JSONC_INSTALL_DIR in autogen.sh to set CMAKE_INSTALL_PREFIX to allow custom directory installation for these drivers where the user might have permission. Reported on GitHub through issue #152 by user pgloader. FDW-406, Vaibhav Dalvi, reviewed by Jeevan Ladhe, tested by Rajkumar Raghuwanshi. --- README.md | 29 ++++++++++++++++++++++++++++- autogen.sh | 6 ++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9e58ca6..050a612 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,34 @@ different type of MongoDB drivers and supported 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 : +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 permission to install the directory whether it is custom or default. + +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 +``` + +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. Build with [MongoDB][1]'s legacy branch driver * autogen.sh --with-legacy diff --git a/autogen.sh b/autogen.sh index 6ba463e..0124b06 100755 --- a/autogen.sh +++ b/autogen.sh @@ -16,6 +16,8 @@ MONGOC_VERSION=1.17.3 JSONC_VERSION=0.15-20200726 +MONGOC_INSTALL="${MONGOC_INSTALL_DIR}" +JSONC_INSTALL="${JSONC_INSTALL_DIR}" if [ "$#" -ne 1 ]; then echo "Usage: autogen.sh --[with-legacy | with-master]" @@ -71,7 +73,7 @@ function checkout_json_lib function install_json_lib { cd json-c && - $CMAKE_COMMAND $JSONC_CFLAGS . && + $CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$JSONC_INSTALL $JSONC_CFLAGS . && make install && cd .. } @@ -82,7 +84,7 @@ function install_json_lib function install_mongoc_driver { cd mongo-c-driver && - $CMAKE_COMMAND -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . && + $CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$MONGOC_INSTALL -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_SSL=AUTO . && make install && cd .. } From 3205c5bd9eff788f067993bd4a464775bd1c29a0 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 15 Sep 2021 12:41:31 +0530 Subject: [PATCH 161/239] Stamp 5.2.10. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index cc3f19d..139431c 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50209 + 50210 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 43c36da..8797f5b 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50209 + 50210 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 2db051b..51846bd 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -57,9 +57,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.2.9 so number will be 50209 + * our version is 5.2.10 so number will be 50210 */ -#define CODE_VERSION 50209 +#define CODE_VERSION 50210 extern PGDLLEXPORT void _PG_init(void); const char *EscapeJsonString(const char *string); From 67ae36c13ac3566d7e8869089b9290283c87bcbe Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Tue, 26 Oct 2021 19:10:27 +0530 Subject: [PATCH 162/239] Optimize and rename ColumnList() to mongo_get_column_list(). MongoGetForeignPlan() builds the list of columns used for projection and restriction clauses in scan_var_list. Pass this list to ColumnList() to avoid duplication of work, and use it to retrieve a list of foreign table columns. Also, rename ColumnList() to mongo_get_column_list(). FDW-391, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Ladhe. --- mongo_fdw.c | 10 ++++++-- mongo_fdw.h | 3 ++- mongo_query.c | 71 +++++++++++---------------------------------------- 3 files changed, 25 insertions(+), 59 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 51846bd..e66dd8f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -482,8 +482,14 @@ MongoGetForeignPlan(PlannerInfo *root, local_exprs = lappend(local_exprs, rinfo->clause); } - /* We don't need to serialize column list as lists are copiable */ - columnList = ColumnList(foreignrel); + /* Add local expression Var nodes to scan_var_list. */ + scan_var_list = list_concat_unique(NIL, scan_var_list); + scan_var_list = list_concat_unique(scan_var_list, + pull_var_clause((Node *) local_exprs, + PVC_RECURSE_PLACEHOLDERS)); + + /* Form column list required for query execution from scan_var_list. */ + columnList = mongo_get_column_list(foreignrel, scan_var_list); /* Construct foreign plan with query document and column list */ foreignPrivateList = list_make2(columnList, remote_exprs); diff --git a/mongo_fdw.h b/mongo_fdw.h index de71e4c..af7d37e 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -312,7 +312,8 @@ extern void mongo_release_connection(MONGO_CONN *conn); extern BSON *QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode); -extern List *ColumnList(RelOptInfo *baserel); +extern List *mongo_get_column_list(RelOptInfo *foreignrel, + List *scan_var_list); extern bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression); diff --git a/mongo_query.c b/mongo_query.c index 76f4180..3097133 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -664,72 +664,31 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, } /* - * ColumnList - * Takes in the planner's information about this foreign table. The - * function then finds all columns needed for query execution, including - * those used in projections, joins, and filter clauses, de-duplicates - * these columns, and returns them in a new list. + * mongo_get_column_list + * Process scan_var_list to find all columns needed for query execution + * and return them. */ List * -ColumnList(RelOptInfo *baserel) +mongo_get_column_list(RelOptInfo *foreignrel, List *scan_var_list) { List *columnList = NIL; - List *neededColumnList; - AttrNumber columnIndex; - AttrNumber columnCount = baserel->max_attr; + ListCell *lc; -#if PG_VERSION_NUM >= 90600 - List *targetColumnList = baserel->reltarget->exprs; -#else - List *targetColumnList = baserel->reltargetlist; -#endif - List *restrictInfoList = baserel->baserestrictinfo; - ListCell *restrictInfoCell; - - /* First add the columns used in joins and projections */ - neededColumnList = pull_var_clause((Node *)targetColumnList, -#if PG_VERSION_NUM < 90600 - PVC_RECURSE_AGGREGATES, -#endif - PVC_RECURSE_PLACEHOLDERS); - - /* Then walk over all restriction clauses, and pull up any used columns */ - foreach(restrictInfoCell, restrictInfoList) + foreach(lc, scan_var_list) { - RestrictInfo *restrictInfo = (RestrictInfo *) lfirst(restrictInfoCell); - Node *restrictClause = (Node *) restrictInfo->clause; - List *clauseColumnList = NIL; - - /* Recursively pull up any columns used in the restriction clause */ - clauseColumnList = pull_var_clause(restrictClause, -#if PG_VERSION_NUM < 90600 - PVC_RECURSE_AGGREGATES, -#endif - PVC_RECURSE_PLACEHOLDERS); - - neededColumnList = list_union(neededColumnList, clauseColumnList); - } + Var *var = (Var *) lfirst(lc); - /* Walk over all column definitions, and de-duplicate column list */ - for (columnIndex = 1; columnIndex <= columnCount; columnIndex++) - { - ListCell *neededColumnCell; - Var *column = NULL; + Assert(IsA(var, Var)); - /* Look for this column in the needed column list */ - foreach(neededColumnCell, neededColumnList) - { - Var *neededColumn = (Var *) lfirst(neededColumnCell); + /* Var belongs to foreign table? */ + if (!bms_is_member(var->varno, foreignrel->relids)) + continue; - if (neededColumn->varattno == columnIndex) - { - column = neededColumn; - break; - } - } + /* Do not support whole-row reference */ + if (var->varattno == 0) + continue; - if (column != NULL) - columnList = lappend(columnList, column); + columnList = lappend(columnList, var); } return columnList; From d768fc71e5fe1ae777da5a880206995f856a344c Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Tue, 26 Oct 2021 19:43:34 +0530 Subject: [PATCH 163/239] Add support for whole-row reference. PostgreSQL allows user to refer the complete row as a single column by means of whole row reference. This internally is referred by a reserved varattno zero, currently this is ignored and hence no data is returned when a foreign table is referred with whole row reference. Add functionality to build the list of all the foreign table columns when whole row reference is requested to query the remote server. FDW-391, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Ladhe, tested by Rajkumar Raghuwanshi. --- expected/select.out | 39 ++++++++++++++++ expected/select_1.out | 35 +++++++++++++++ mongo_fdw.c | 2 +- mongo_fdw.h | 3 +- mongo_query.c | 102 ++++++++++++++++++++++++++++++++++++++++-- sql/select.sql | 8 ++++ 6 files changed, 183 insertions(+), 6 deletions(-) diff --git a/expected/select.out b/expected/select.out index 139431c..6448992 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1204,6 +1204,45 @@ 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) + +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 + -> Merge Left Join + Output: d.*, d.c2, e.c1, e.* + Merge Cond: (d.c1 = e.c8) + -> Sort + Output: d.*, d.c2, d.c1 + Sort Key: d.c1 + -> Foreign Scan on public.f_test_tbl2 d + Output: d.*, d.c2, d.c1 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Output: e.c1, e.*, e.c8 + Sort Key: e.c8 + -> Foreign Scan on public.f_test_tbl1 e + Output: e.c1, e.*, e.c8 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(18 rows) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/expected/select_1.out b/expected/select_1.out index 8797f5b..129beac 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1168,6 +1168,41 @@ 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) + +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 Right Join + Output: d.*, d.c2, e.c1, e.* + Hash Cond: (e.c8 = d.c1) + -> Foreign Scan on public.f_test_tbl1 e + Output: e.c1, e.*, e.c8 + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Hash + Output: d.*, d.c2, d.c1 + -> Foreign Scan on public.f_test_tbl2 d + Output: d.*, d.c2, d.c1 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(14 rows) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; diff --git a/mongo_fdw.c b/mongo_fdw.c index e66dd8f..5cb9f31 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -489,7 +489,7 @@ MongoGetForeignPlan(PlannerInfo *root, PVC_RECURSE_PLACEHOLDERS)); /* Form column list required for query execution from scan_var_list. */ - columnList = mongo_get_column_list(foreignrel, scan_var_list); + columnList = mongo_get_column_list(root, foreignrel, scan_var_list); /* Construct foreign plan with query document and column list */ foreignPrivateList = list_make2(columnList, remote_exprs); diff --git a/mongo_fdw.h b/mongo_fdw.h index af7d37e..8d47cff 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -312,7 +312,8 @@ extern void mongo_release_connection(MONGO_CONN *conn); extern BSON *QueryDocument(Oid relationId, List *opExpressionList, ForeignScanState *scanStateNode); -extern List *mongo_get_column_list(RelOptInfo *foreignrel, +extern List *mongo_get_column_list(PlannerInfo *root, + RelOptInfo *foreignrel, List *scan_var_list); extern bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression); diff --git a/mongo_query.c b/mongo_query.c index 3097133..837e3b2 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -18,6 +18,13 @@ #include #include +#if PG_VERSION_NUM < 120000 +#include "access/sysattr.h" +#endif +#if PG_VERSION_NUM >= 120000 +#include "access/table.h" +#endif +#include "catalog/heap.h" #include "catalog/pg_collation.h" #ifdef META_DRIVER #include "mongoc.h" @@ -32,6 +39,8 @@ #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif +#include "parser/parsetree.h" +#include "utils/rel.h" /* * Global context for foreign_expr_walker's search of an expression tree. @@ -74,6 +83,8 @@ static void AppendParamValue(BSON *queryDocument, const char *keyName, 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); /* * FindArgumentOfType @@ -669,7 +680,8 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, * and return them. */ List * -mongo_get_column_list(RelOptInfo *foreignrel, List *scan_var_list) +mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, + List *scan_var_list) { List *columnList = NIL; ListCell *lc; @@ -684,11 +696,29 @@ mongo_get_column_list(RelOptInfo *foreignrel, List *scan_var_list) if (!bms_is_member(var->varno, foreignrel->relids)) continue; - /* Do not support whole-row reference */ + /* Is whole-row reference requested? */ if (var->varattno == 0) - continue; + { + List *wr_var_list; + RangeTblEntry *rte = rt_fetch(var->varno, root->parse->rtable); + Bitmapset *attrs_used; + + Assert(OidIsValid(rte->relid)); + + /* + * Get list of Var nodes for all undropped attributes of the base + * relation. + */ + attrs_used = bms_make_singleton(0 - + FirstLowInvalidHeapAttributeNumber); - columnList = lappend(columnList, var); + wr_var_list = prepare_var_list_for_baserel(rte->relid, var->varno, + attrs_used); + columnList = list_concat_unique(columnList, wr_var_list); + bms_free(attrs_used); + } + else + columnList = list_append_unique(columnList, var); } return columnList; @@ -1010,3 +1040,67 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression) /* 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 */ +#if PG_VERSION_NUM < 130000 + relation = heap_open(relid, NoLock); +#else + relation = table_open(relid, NoLock); +#endif + + 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); + + } + } + +#if PG_VERSION_NUM < 130000 + heap_close(relation, NoLock); +#else + table_close(relation, NoLock); +#endif + + return tlist; +} diff --git a/sql/select.sql b/sql/select.sql index e6fac3e..259af13 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -279,6 +279,14 @@ 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; +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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; From 2f6c2bc8c8650011e8d82dc3f774a66d5415036d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 15 Nov 2021 08:28:03 +0530 Subject: [PATCH 164/239] Refactor code a bit. QueryDocument() adds the filters to the MongoDB query. However, for MongoAcquireSampleRows(), there are no quals and thus NO need to call QueryDocument(). Fix that. Also, the list_concat() function has changed in v13, so add a mongo_list_concat() wrapper which calls the correct form depending on the version. Along the way, improve some comments and indentation. FDW-138, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, with additional improvements by me. --- mongo_fdw.c | 34 ++++++++++++++++++++++------------ mongo_fdw.h | 15 +++++++++++---- mongo_query.c | 7 ++++--- mongo_query.h | 10 +++------- option.c | 6 +++--- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 5cb9f31..263407e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -413,9 +413,9 @@ MongoGetForeignPlan(PlannerInfo *root, Plan *outer_plan) { MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; - Index scanRangeTableIndex = foreignrel->relid; + Index scan_relid = foreignrel->relid; ForeignScan *foreignScan; - List *foreignPrivateList; + List *fdw_private; List *columnList; List *scan_var_list; ListCell *lc; @@ -492,13 +492,13 @@ MongoGetForeignPlan(PlannerInfo *root, columnList = mongo_get_column_list(root, foreignrel, scan_var_list); /* Construct foreign plan with query document and column list */ - foreignPrivateList = list_make2(columnList, remote_exprs); + fdw_private = list_make2(columnList, remote_exprs); /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, local_exprs, - scanRangeTableIndex, + scan_relid, NIL, /* No expressions to evaluate */ - foreignPrivateList + fdw_private #if PG_VERSION_NUM >= 90500 ,NIL ,NIL @@ -680,7 +680,7 @@ MongoIterateForeignScan(ForeignScanState *node) } /* - * We execute the protocol to load a virtual tuple into a slot. We first + * 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. @@ -1293,8 +1293,6 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) ListCell *columnCell; const long hashTableSize = 2048; HTAB *columnMappingHash; - - /* Create hash table */ HASHCTL hashInfo; memset(&hashInfo, 0, sizeof(hashInfo)); @@ -1313,7 +1311,7 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) Var *column = (Var *) lfirst(columnCell); AttrNumber columnId = column->varattno; ColumnMapping *columnMapping; - char *columnName = NULL; + char *columnName; bool handleFound = false; void *hashKey; @@ -1581,7 +1579,7 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) break; default: /* - * We currently error out on other data types. Some types such as + * 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 @@ -2152,7 +2150,7 @@ MongoAcquireSampleRows(Relation relation, AttrNumber columnId; HTAB *columnMappingHash; MONGO_CURSOR *mongoCursor; - BSON *queryDocument; + BSON *queryDocument = BsonCreate(); List *columnList = NIL; char *relationName; MemoryContext oldContext = CurrentMemoryContext; @@ -2197,7 +2195,19 @@ MongoAcquireSampleRows(Relation relation, */ mongoConnection = mongo_get_connection(server, user, options); - queryDocument = QueryDocument(foreignTableId, NIL, NULL); + if (!BsonFinish(queryDocument)) + { +#ifdef META_DRIVER + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); +#else + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON error: %d", queryDocument->err))); +#endif + } + /* Create cursor for collection name and set query */ mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); diff --git a/mongo_fdw.h b/mongo_fdw.h index 8d47cff..4141aaf 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -170,6 +170,13 @@ #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. */ +#if PG_VERSION_NUM < 130000 + #define mongo_list_concat(l1, l2) list_concat(l1, list_copy(l2)) +#else + #define mongo_list_concat(l1, l2) list_concat((l1), (l2)) +#endif + /* * MongoValidOption keeps an option name and a context. When an option is * passed into mongo_fdw objects (server and foreign table), we compare this @@ -268,10 +275,10 @@ typedef struct MongoFdwModifyState } MongoFdwModifyState; /* - * ColumnMapping reprents 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 + * 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 diff --git a/mongo_query.c b/mongo_query.c index 837e3b2..e050f86 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -80,6 +80,7 @@ static void AppendConstantValue(BSON *queryDocument, const char *keyName, static void AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, ForeignScanState *scanStateNode); +static char *MongoOperatorName(const char *operatorName); static bool foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, foreign_loc_cxt *outer_cxt); @@ -249,7 +250,7 @@ QueryDocument(Oid relationId, List *opExpressionList, * Takes in the given PostgreSQL comparison operator name, and returns its * equivalent in MongoDB. */ -char * +static char * MongoOperatorName(const char *operatorName) { const char *mongoOperatorName = NULL; @@ -710,7 +711,7 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, * relation. */ attrs_used = bms_make_singleton(0 - - FirstLowInvalidHeapAttributeNumber); + FirstLowInvalidHeapAttributeNumber); wr_var_list = prepare_var_list_for_baserel(rte->relid, var->varno, attrs_used); @@ -1033,7 +1034,7 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression) if (!foreign_expr_walker((Node *) expression, &glob_cxt, &loc_cxt)) return false; - /* Expressions examined here should be boolean, ie noncollatable */ + /* Expressions examined here should be boolean, i.e. noncollatable */ Assert(loc_cxt.collation == InvalidOid); Assert(loc_cxt.state == FDW_COLLATE_NONE); diff --git a/mongo_query.h b/mongo_query.h index 969b7d9..81c184d 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -17,12 +17,8 @@ #define NUMERICARRAY_OID 1231 -bool AppendMongoValue(BSON *queryDocument, - const char *keyName, - Datum value, - bool isnull, - Oid id); - -char *MongoOperatorName(const char *operatorName); +/* Function to be used in mongo_fdw.c */ +extern bool AppendMongoValue(BSON *queryDocument, const char *keyName, + Datum value, bool isnull, Oid id); #endif /* MONGO_QUERY_H */ diff --git a/option.c b/option.c index d32fddf..04087d8 100644 --- a/option.c +++ b/option.c @@ -156,9 +156,9 @@ mongo_get_options(Oid foreignTableId) foreignServer = GetForeignServer(foreignTable->serverid); mapping = GetUserMapping(GetUserId(), foreignTable->serverid); - optionList = list_concat(optionList, foreignTable->options); - optionList = list_concat(optionList, foreignServer->options); - optionList = list_concat(optionList, mapping->options); + optionList = mongo_list_concat(optionList, foreignTable->options); + optionList = mongo_list_concat(optionList, foreignServer->options); + optionList = mongo_list_concat(optionList, mapping->options); options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); From 198753b73d6c7ec2a15b284eb57d28ff9779268c Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 15 Nov 2021 08:28:03 +0530 Subject: [PATCH 165/239] Push down joins to the remote MongoDB servers. If we have a join between two foreign tables from the same remote server, push that join down to the remote server instead of fetching all the rows for both the tables and performing a join locally. This will have a very significant win in many practical cases. Since it is very unclear that which of the operators are supported on the MongoDB servers, currently join involving only relational and arithmetic operators in join-clauses are pushed down to avoid any potential join failure. Also, only INNER and LEFT/RIGHT OUTER joins are supported, and not FULL, SEMI, and ANTI joins. Moreover, only joins between two tables are pushed down and not when either inner or outer relation is the join itself. We use an aggregate pipeline with the lookup operator. Also, the MongoDB server returns join results as an array, and thus to get in a tuple form we use the unwind operator. This patch also supports the partition-wise join push-down when the table is partitioned. FDW-138, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, with additional improvements by me. Tested by Rajkumar Raghuwanshi. --- Makefile.meta | 2 +- deparse.c | 535 +++++++++++++++++++++++++++++ mongo_fdw.c | 788 +++++++++++++++++++++++++++++++++++++++---- mongo_fdw.h | 109 +++++- mongo_query.c | 733 ++++++++++++++++++++++++++++++++++------ mongo_query.h | 94 ++++++ mongo_wrapper_meta.c | 2 +- 7 files changed, 2084 insertions(+), 179 deletions(-) create mode 100644 deparse.c diff --git a/Makefile.meta b/Makefile.meta index ccdd458..5385782 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -20,7 +20,7 @@ MONGO_INCLUDE = $(shell pkg-config --cflags libmongoc-1.0) PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) -DMETA_DRIVER SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) -OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o $(LIBJSON_OBJS) +OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o deparse.o $(LIBJSON_OBJS) EXTENSION = mongo_fdw diff --git a/deparse.c b/deparse.c new file mode 100644 index 0000000..5b82f63 --- /dev/null +++ b/deparse.c @@ -0,0 +1,535 @@ +/*------------------------------------------------------------------------- + * + * deparse.c + * Query deparser for mongo_fdw + * + * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group + * Portions Copyright (c) 2004-2021, 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" +#if PG_VERSION_NUM >= 130000 +#include "common/hashfn.h" +#endif +#ifdef META_DRIVER +#include "mongoc.h" +#else +#include "mongo.h" +#endif +#include "mongo_query.h" +#if PG_VERSION_NUM < 120000 +#include "nodes/relation.h" +#include "optimizer/var.h" +#endif +#if PG_VERSION_NUM >= 120000 +#include "optimizer/optimizer.h" +#endif +#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, MongoJoinQualInfo *jqinfo); +static void mongo_check_var(Var *column, MongoJoinQualInfo *jqinfo); + +/* 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, MongoJoinQualInfo *jqinfo) +{ + if (node == NULL) + return; + + switch (nodeTag(node)) + { + case T_Var: + mongo_check_var((Var *) node, jqinfo); + break; + case T_OpExpr: + mongo_check_op_expr((OpExpr *) node, jqinfo); + break; + case T_List: + { + ListCell *lc; + + foreach(lc, (List *) node) + mongo_check_qual((Expr *) lfirst(lc), jqinfo); + } + break; + case T_RelabelType: + mongo_check_qual(((RelabelType *) node)->arg, jqinfo); + break; + case T_BoolExpr: + mongo_check_qual((Expr *)((BoolExpr *) node)->args, jqinfo); + 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, MongoJoinQualInfo *jqinfo) +{ + 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), jqinfo); + } + + /* Deparse right operand. */ + if (oprkind == 'l' || oprkind == 'b') + { + arg = list_tail(node->args); + mongo_check_qual(lfirst(arg), jqinfo); + } + + 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. + * + * Save required information in the form of a list in MongoJoinQualInfo + * 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, MongoJoinQualInfo *jqinfo) +{ + RangeTblEntry *rte; + char *colname; + char *tabname; + ListCell *lc; + ForeignTable *table; + ColumnHashKey key; + bool found; + bool is_outerrel = false; + + if (!(bms_is_member(column->varno, jqinfo->foreignRel->relids) && + column->varlevelsup == 0)) + return; /* Var does not belong to foreign table */ + + Assert(!IS_SPECIAL_VARNO(column->varno)); + + if (!jqinfo->joinExprColHash) + { + HASHCTL hashInfo; + + memset(&hashInfo, 0, sizeof(hashInfo)); + hashInfo.keysize = sizeof(ColumnHashKey); + hashInfo.entrysize = sizeof(ColumnHashKey); + hashInfo.hcxt = CurrentMemoryContext; + + jqinfo->joinExprColHash = hash_create("Join Expression Column Hash", + MaxHashTableSize, + &hashInfo, + (HASH_ELEM | HASH_BLOBS | HASH_CONTEXT)); + } + + key.varno = column->varno; + key.varattno = column->varattno; + + hash_search(jqinfo->joinExprColHash, (void *)&key, HASH_ENTER, &found); + if (found) + return; + + /* Get RangeTblEntry from array in PlannerInfo. */ + rte = planner_rt_fetch(column->varno, jqinfo->root); + +#if PG_VERSION_NUM >= 110000 + colname = get_attname(rte->relid, column->varattno, false); +#else + colname = get_relid_attribute_name(rte->relid, column->varattno); +#endif + + table = GetForeignTable(rte->relid); + foreach(lc, table->options) + { + DefElem *def = (DefElem *) lfirst(lc); + + if (strcmp(def->defname, "collection") == 0) + tabname = defGetString(def); + } + + if (tabname == NULL) + tabname = get_rel_name(rte->relid); + + /* Is relation inner or outer? */ + if (bms_is_member(column->varno, jqinfo->outerRelids)) + is_outerrel = true; + + /* Fill the lists with elements */ + jqinfo->colNameList = lappend(jqinfo->colNameList, makeString(colname)); + jqinfo->colNumList = lappend_int(jqinfo->colNumList, column->varattno); + jqinfo->rtiList = lappend_int(jqinfo->rtiList, column->varno); + jqinfo->isOuterList = lappend_int(jqinfo->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: + AppendConstantValue(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; + 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"; + mongo_append_expr(linitial(node->args), child_doc, context); + return; + } + + 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-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; + + /* 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 = MongoOperatorName(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); + } + + 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-clauses. + */ + foreach(arg, node->args) + { + if (!IsA(lfirst(arg), Var)) + continue; + + if (context->isBoolExpr) + BsonAppendStartObject(&and_op, psprintf("%d", and_index++), &expr); + else + BsonAppendStartObject(child_doc, + psprintf("%d", context->arrayIndex++), + &expr); + + mongo_add_null_check(lfirst(arg), &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) + field = psprintf("$$v_%s", 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 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) + field = psprintf("$$v_%s", 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); +} diff --git a/mongo_fdw.c b/mongo_fdw.c index 263407e..8257b23 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -25,6 +25,7 @@ #include "access/table.h" #endif #include "catalog/heap.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" @@ -39,6 +40,7 @@ #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif +#include "optimizer/tlist.h" #if PG_VERSION_NUM < 120000 #include "optimizer/var.h" #endif @@ -51,6 +53,7 @@ #include "utils/jsonfuncs.h" #endif #include "utils/rel.h" +#include "utils/syscache.h" /* Declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -134,17 +137,25 @@ static void MongoBeginForeignInsert(ModifyTableState *mtstate, static void MongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo); #endif +static void MongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, + RelOptInfo *outerrel, + RelOptInfo *innerrel, + JoinType jointype, + JoinPathExtraData *extra); /* * Helper functions */ static double ForeignTableDocumentCount(Oid foreignTableId); -static HTAB *ColumnMappingHash(Oid foreignTableId, List *columnList); +static HTAB *ColumnMappingHash(Oid foreignTableId, List *columnList, + List *colNameList, List *colIsInnerList, + bool isJoin); static void FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls); + bool *columnNulls, + bool isJoin); static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); static Datum ColumnValue(BSON_ITERATOR *bsonIterator, @@ -158,6 +169,11 @@ static int MongoAcquireSampleRows(Relation relation, 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, MongoJoinQualInfo *jqinfo); /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -230,6 +246,9 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->EndForeignInsert = MongoEndForeignInsert; #endif + /* Support function for join push-down */ + fdwRoutine->GetForeignJoinPaths = MongoGetForeignJoinPaths; + PG_RETURN_POINTER(fdwRoutine); } @@ -257,8 +276,13 @@ MongoGetForeignRelSize(PlannerInfo *root, Oid foreigntableid) { double documentCount = ForeignTableDocumentCount(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 @@ -277,12 +301,15 @@ MongoGetForeignRelSize(PlannerInfo *root, RestrictInfo *ri = (RestrictInfo *) lfirst(lc); if (IsA(ri->clause, OpExpr) && - mongo_is_foreign_expr(root, baserel, ri->clause)) + 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; + if (documentCount > 0.0) { double rowSelectivity; @@ -301,6 +328,26 @@ MongoGetForeignRelSize(PlannerInfo *root, ereport(DEBUG1, (errmsg("could not retrieve document count for collection"), errhint("Falling back to default estimates in planning."))); + + options = mongo_get_options(foreigntableid); + 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)); } /* @@ -421,6 +468,40 @@ MongoGetForeignPlan(PlannerInfo *root, 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; + MongoJoinQualInfo *jqinfo; + + /* Set scan relation id */ + if (foreignrel->reloptkind == RELOPT_BASEREL || + foreignrel->reloptkind == RELOPT_OTHER_MEMBER_REL) + scan_relid = foreignrel->relid; + else + { + /* Join 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 PG_VERSION_NUM >= 90600 scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs, @@ -476,7 +557,7 @@ MongoGetForeignPlan(PlannerInfo *root, 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)) + mongo_is_foreign_expr(root, foreignrel, rinfo->clause, false)) remote_exprs = lappend(remote_exprs, rinfo->clause); else local_exprs = lappend(local_exprs, rinfo->clause); @@ -488,21 +569,163 @@ MongoGetForeignPlan(PlannerInfo *root, pull_var_clause((Node *) local_exprs, PVC_RECURSE_PLACEHOLDERS)); +#if PG_VERSION_NUM >= 100000 + if (IS_JOIN_REL(foreignrel)) +#else + if (foreignrel->reloptkind == RELOPT_JOINREL) +#endif + { + /* + * 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) + { + ListCell *lc; + + /* + * 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); + } + } + /* Form column list required for query execution from scan_var_list. */ - columnList = mongo_get_column_list(root, foreignrel, scan_var_list); + columnList = mongo_get_column_list(root, foreignrel, scan_var_list, + &column_name_list, + &is_inner_column_list); - /* Construct foreign plan with query document and column list */ + /* + * Prepare separate lists of column names, varno, varattno, and whether it + * is part of the outer relation or not. This information would be useful + * at the time of execution to prepare the MongoDB query. + */ +#if PG_VERSION_NUM >= 100000 + if (IS_JOIN_REL(foreignrel)) +#else + if (foreignrel->reloptkind == RELOPT_JOINREL) +#endif + { + /* + * We use MongoJoinQualInfo to pass various information related to + * joining quals to fdw_private which is used to form equivalent + * MongoDB query during the execution phase. + */ + jqinfo = (MongoJoinQualInfo *) palloc(sizeof(MongoJoinQualInfo)); + + jqinfo->root = root; + jqinfo->foreignRel = foreignrel; + jqinfo->outerRelids = fpinfo->outerrel->relids; + jqinfo->joinExprColHash = NULL; + /* Initialize all lists */ + jqinfo->colNameList = NIL; + jqinfo->colNumList = NIL; + jqinfo->rtiList = NIL; + jqinfo->isOuterList = NIL; + + /* + * Extract required data of columns involved in join clauses and append + * it into the various lists required to pass it to the executor. + * Also, save join type in the list. + */ + if (fpinfo->joinclauses) + mongo_prepare_qual_info(fpinfo->joinclauses, jqinfo); + + /* + * 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 (fpinfo->remote_conds) + mongo_prepare_qual_info(fpinfo->remote_conds, jqinfo); + + /* Destroy hash table used to get unique column info */ + hash_destroy(jqinfo->joinExprColHash); + } + + /* + * 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, remote_exprs); + /* + * 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. + */ +#if PG_VERSION_NUM >= 100000 + if (IS_JOIN_REL(foreignrel)) +#else + if (foreignrel->reloptkind == RELOPT_JOINREL) +#endif + { + 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); + fdw_private = lappend(fdw_private, fpinfo->joinclauses); + fdw_private = lappend(fdw_private, jqinfo->colNameList); + fdw_private = lappend(fdw_private, jqinfo->colNumList); + fdw_private = lappend(fdw_private, jqinfo->rtiList); + fdw_private = lappend(fdw_private, jqinfo->isOuterList); + fdw_private = lappend(fdw_private, + list_make2(makeString(fpinfo->inner_relname), + makeString(fpinfo->outer_relname))); + fdw_private = lappend(fdw_private, makeInteger(fpinfo->jointype)); + } + /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, local_exprs, scan_relid, NIL, /* No expressions to evaluate */ fdw_private #if PG_VERSION_NUM >= 90500 + ,fdw_scan_tlist ,NIL - ,NIL - ,NULL + ,outer_plan #endif ); @@ -516,21 +739,39 @@ MongoGetForeignPlan(PlannerInfo *root, static void MongoExplainForeignScan(ForeignScanState *node, ExplainState *es) { - MongoFdwOptions *options; - StringInfo namespaceName; - Oid foreignTableId; + ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan; + RangeTblEntry *rte; + EState *estate = node->ss.ps.state; + List *fdw_private = fsplan->fdw_private; + int rtindex; - foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); - options = mongo_get_options(foreignTableId); + if (fsplan->scan.scanrelid > 0) + rtindex = fsplan->scan.scanrelid; + else + rtindex = bms_next_member(fsplan->fs_relids, -1); + rte = rt_fetch(rtindex, estate->es_range_table); - /* Construct fully qualified collection name */ - namespaceName = makeStringInfo(); - appendStringInfo(namespaceName, "%s.%s", options->svr_database, - options->collectionName); + if (list_length(fdw_private) > mongoFdwPrivateRelations) + { + char *relations = strVal(list_nth(fdw_private, + mongoFdwPrivateRelations)); - mongo_free_options(options); + ExplainPropertyText("Foreign Namespace", relations, es); + } + else + { + StringInfo namespaceName; + MongoFdwOptions *options; - ExplainPropertyText("Foreign Namespace", namespaceName->data, es); + 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 @@ -569,54 +810,70 @@ static void MongoBeginForeignScan(ForeignScanState *node, int eflags) { MONGO_CONN *mongoConnection; - Oid foreignTableId; List *columnList; HTAB *columnMappingHash; - List *foreignPrivateList; 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; - foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); - options = mongo_get_options(foreignTableId); - fmstate = (MongoFdwModifyState *) palloc0(sizeof(MongoFdwModifyState)); /* * Identify which user to do the remote access as. This should match what - * ExecCheckRTEPerms() does. + * 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. */ - rte = rt_fetch(fsplan->scan.scanrelid, estate->es_range_table); + if (fsplan->scan.scanrelid > 0) + { + rtindex = fsplan->scan.scanrelid; + fmstate->isJoinRel = false; + } + else + { + rtindex = bms_next_member(fsplan->fs_relids, -1); + fmstate->isJoinRel = true; + } + rte = rt_fetch(rtindex, estate->es_range_table); userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); /* Get info about foreign table. */ fmstate->rel = node->ss.ss_currentRelation; - table = GetForeignTable(RelationGetRelid(fmstate->rel)); + 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); - foreignPrivateList = fsplan->fdw_private; - Assert(list_length(foreignPrivateList) == 2); + columnList = list_nth(fdw_private, mongoFdwPrivateColumnList); - columnList = list_nth(foreignPrivateList, 0); + if (fmstate->isJoinRel == true) + { + colNameList = list_nth(fdw_private, mongoFdwPrivateColNameList); + colIsInnerList = list_nth(fdw_private, mongoFdwPrivateColIsInnerList); + } - columnMappingHash = ColumnMappingHash(foreignTableId, columnList); + columnMappingHash = ColumnMappingHash(rte->relid, columnList, colNameList, + colIsInnerList, fmstate->isJoinRel); /* Create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; @@ -641,7 +898,6 @@ MongoIterateForeignScan(ForeignScanState *node) TupleTableSlot *tupleSlot = node->ss.ss_ScanTupleSlot; MONGO_CURSOR *mongoCursor = fmstate->mongoCursor; HTAB *columnMappingHash = fmstate->columnMappingHash; - ForeignScan *foreignScan = (ForeignScan *) node->ss.ps.plan; TupleDesc tupleDescriptor = tupleSlot->tts_tupleDescriptor; Datum *columnValues = tupleSlot->tts_values; bool *columnNulls = tupleSlot->tts_isnull; @@ -650,16 +906,8 @@ MongoIterateForeignScan(ForeignScanState *node) /* Create cursor for collection name and set query */ if (mongoCursor == NULL) { - Oid foreignTableId; - List *foreignPrivateList; - List *opExpressionList; BSON *queryDocument; - - foreignPrivateList = foreignScan->fdw_private; - Assert(list_length(foreignPrivateList) == 2); - - opExpressionList = list_nth(foreignPrivateList, 1); - foreignTableId = RelationGetRelid(node->ss.ss_currentRelation); + char *collectionName; /* * We construct the query document to have MongoDB filter its rows. We @@ -668,11 +916,20 @@ MongoIterateForeignScan(ForeignScanState *node) * performance on the MongoDB server-side, so we instead filter out * columns on our side. */ - queryDocument = QueryDocument(foreignTableId, opExpressionList, node); + queryDocument = QueryDocument(node); + + /* + * Decide input collection to the aggregation. In case of join, outer + * relation should be given as input collection to the aggregation. + */ + if (fmstate->isJoinRel) + collectionName = fmstate->outerRelName; + else + collectionName = fmstate->options->collectionName; mongoCursor = MongoCursorCreate(fmstate->mongoConnection, fmstate->options->svr_database, - fmstate->options->collectionName, + collectionName, queryDocument); /* Save mongoCursor */ @@ -697,7 +954,7 @@ MongoIterateForeignScan(ForeignScanState *node) const char *bsonDocumentKey = NULL; /* Top level document */ FillTupleSlot(bsonDocument, bsonDocumentKey, columnMappingHash, - columnValues, columnNulls); + columnValues, columnNulls, fmstate->isJoinRel); ExecStoreVirtualTuple(tupleSlot); } @@ -1288,12 +1545,14 @@ ForeignTableDocumentCount(Oid foreignTableId) * corresponding PostgreSQL columns. */ static HTAB * -ColumnMappingHash(Oid foreignTableId, List *columnList) +ColumnMappingHash(Oid foreignTableId, List *columnList, List *colNameList, + List *colIsInnerList, bool isJoin) { ListCell *columnCell; - const long hashTableSize = 2048; HTAB *columnMappingHash; HASHCTL hashInfo; + uint32 attnum = 0; + Index listIndex = 0; memset(&hashInfo, 0, sizeof(hashInfo)); hashInfo.keysize = NAMEDATALEN; @@ -1301,7 +1560,7 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) hashInfo.hash = string_hash; hashInfo.hcxt = CurrentMemoryContext; - columnMappingHash = hash_create("Column Mapping Hash", hashTableSize, + columnMappingHash = hash_create("Column Mapping Hash", MaxHashTableSize, &hashInfo, (HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT)); Assert(columnMappingHash != NULL); @@ -1309,18 +1568,50 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) foreach(columnCell, columnList) { Var *column = (Var *) lfirst(columnCell); - AttrNumber columnId = column->varattno; ColumnMapping *columnMapping; char *columnName; bool handleFound = false; void *hashKey; + if (isJoin) + { + 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; + } + else + { #if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(foreignTableId, columnId); + columnName = get_relid_attribute_name(foreignTableId, + column->varattno); #else - columnName = get_attname(foreignTableId, columnId, false); + columnName = get_attname(foreignTableId, column->varattno, false); #endif - hashKey = (void *) columnName; + hashKey = (void *) columnName; + } columnMapping = (ColumnMapping *) hash_search(columnMappingHash, hashKey, @@ -1328,7 +1619,20 @@ ColumnMappingHash(Oid foreignTableId, List *columnList) &handleFound); Assert(columnMapping != NULL); - columnMapping->columnIndex = columnId - 1; + /* + * Save attribute number of the current column in the resulting tuple. + * For join relation, it is continuously increasing integers starting + * from 0, and for simple relation, it's varattno. + */ + if(isJoin) + { + columnMapping->columnIndex = attnum; + attnum++; + } + else + columnMapping->columnIndex = column->varattno - 1; + + /* Save other information */ columnMapping->columnTypeId = column->vartype; columnMapping->columnTypeMod = column->vartypmod; columnMapping->columnArrayTypeId = get_element_type(column->vartype); @@ -1352,7 +1656,8 @@ FillTupleSlot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls) + bool *columnNulls, + bool isJoin) { ColumnMapping *columnMapping; bool handleFound = false; @@ -1436,7 +1741,7 @@ FillTupleSlot(const BSON *bsonDocument, bool handleFound = false; const char *bsonFullKey; void *hashKey; - int32 columnIndex; + int32 attnum; columnMapping = NULL; if (bsonDocumentKey != NULL) @@ -1475,7 +1780,7 @@ FillTupleSlot(const BSON *bsonDocument, BsonIterSubObject(&bsonIterator, &subObject); FillTupleSlot(&subObject, bsonFullKey, columnMappingHash, - columnValues, columnNulls); + columnValues, columnNulls, isJoin); continue; } } @@ -1494,16 +1799,16 @@ FillTupleSlot(const BSON *bsonDocument, if (!compatibleTypes) continue; - columnIndex = columnMapping->columnIndex; + attnum = columnMapping->columnIndex; + /* Fill in corresponding column value and null flag */ if (OidIsValid(columnArrayTypeId)) - columnValues[columnIndex] = ColumnValueArray(&bsonIterator, - columnArrayTypeId); + columnValues[attnum] = ColumnValueArray(&bsonIterator, + columnArrayTypeId); else - columnValues[columnIndex] = ColumnValue(&bsonIterator, - columnTypeId, - columnMapping->columnTypeMod); - columnNulls[columnIndex] = false; + columnValues[attnum] = ColumnValue(&bsonIterator, columnTypeId, + columnMapping->columnTypeMod); + columnNulls[attnum] = false; } } @@ -2211,7 +2516,8 @@ MongoAcquireSampleRows(Relation relation, /* Create cursor for collection name and set query */ mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); - columnMappingHash = ColumnMappingHash(foreignTableId, columnList); + columnMappingHash = ColumnMappingHash(foreignTableId, columnList, NIL, NIL, + false); /* * Use per-tuple memory context to prevent leak of memory used to read @@ -2254,7 +2560,7 @@ MongoAcquireSampleRows(Relation relation, MemoryContextSwitchTo(tupleContext); FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls); + columnMappingHash, columnValues, columnNulls, false); MemoryContextSwitchTo(oldContext); } @@ -2382,3 +2688,357 @@ MongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) errmsg("COPY and foreign partition routing not supported in mongo_fdw"))); } #endif + +/* + * 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. */ + + /* + * Skip if this join combination has been considered already. + */ + if (joinrel->fdw_private) + 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 >= 120000 + joinpath = create_foreign_join_path(root, + joinrel, + NULL, + joinrel->rows, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + joinrel->lateral_relids, + epq_path, + NULL); /* no fdw_private */ +#else + joinpath = create_foreignscan_path(root, + joinrel, + NULL, /* default pathtarget */ + 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); + + /* XXX Consider pathkeys for the join relation */ + + /* 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; + + /* Recursive joins can't be pushed down */ +#if PG_VERSION_NUM >= 100000 + if (IS_JOIN_REL(outerrel) || IS_JOIN_REL(innerrel)) +#else + if (outerrel->reloptkind == RELOPT_JOINREL || + innerrel->reloptkind == RELOPT_JOINREL) +#endif + return false; + + /* + * If either of the joining relations is marked as unsafe to pushdown, the + * join can not be pushed down. + */ + fpinfo = (MongoFdwRelationInfo *) joinrel->fdw_private; + fpinfo_o = (MongoFdwRelationInfo *) outerrel->fdw_private; + fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private; + 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; + +#if PG_VERSION_NUM >= 90600 + scan_var_list = pull_var_clause((Node *) joinrel->reltarget->exprs, + PVC_RECURSE_PLACEHOLDERS); +#else + scan_var_list = pull_var_clause((Node *) joinrel->reltargetlist, + PVC_RECURSE_AGGREGATES, + PVC_RECURSE_PLACEHOLDERS); +#endif + + /* + * 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); +#if PG_VERSION_NUM >= 110000 + colname = get_attname(rte->relid, var->varattno, false); +#else + colname = get_relid_attribute_name(rte->relid, var->varattno); +#endif + /* 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, + true); + + 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 + 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. */ +#if PG_VERSION_NUM >= 100000 + relids = IS_OTHER_REL(joinrel) ? + joinrel->top_parent_relids : joinrel->relids; +#else + relids = joinrel->relids; +#endif /* PG_VERSION_NUM >= 100000 */ + + 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; + + /* + * 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 join quals by extracting + * clause from each qual and process it further using mongo_check_qual(). + */ +static void +mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo) +{ + 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, jqinfo); + } +} diff --git a/mongo_fdw.h b/mongo_fdw.h index 4141aaf..486b0e5 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -177,6 +177,13 @@ #define mongo_list_concat(l1, l2) list_concat((l1), (l2)) #endif +/* + * 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 @@ -272,6 +279,10 @@ typedef struct MongoFdwModifyState MongoFdwOptions *options; AttrNumber rowidAttno; /* attnum of resjunk rowid column */ + + /* Join information */ + bool isJoinRel; /* Is this JOIN operation? */ + char *outerRelName; /* Outer relation name */ } MongoFdwModifyState; /* @@ -288,6 +299,8 @@ typedef struct ColumnMapping Oid columnTypeId; int32 columnTypeMod; Oid columnArrayTypeId; + /* Column serial number in target list (set only for join rel) */ + uint32 columnSerialNo; } ColumnMapping; /* @@ -297,11 +310,91 @@ typedef struct ColumnMapping */ 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; + + /* Join information */ + RelOptInfo *outerrel; + RelOptInfo *innerrel; + JoinType jointype; + List *joinclauses; + char *inner_relname; + char *outer_relname; } MongoFdwRelationInfo; +/* + * MongoJoinQualInfo contains column name, varno, varattno, and its relation + * name of columns involved in the join quals which is passed to execution + * state through fdw_private. + * + * 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. + * + * 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 MongoJoinQualInfo: + * + * 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. + */ +typedef struct MongoJoinQualInfo +{ + 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 *joinExprColHash; +} MongoJoinQualInfo; + +typedef struct ColumnHashKey +{ + int varno; + int varattno; +} ColumnHashKey; + /* options.c */ extern MongoFdwOptions *mongo_get_options(Oid foreignTableId); extern void mongo_free_options(MongoFdwOptions *options); @@ -316,17 +409,19 @@ extern void mongo_cleanup_connection(void); extern void mongo_release_connection(MONGO_CONN *conn); /* Function declarations related to creating the mongo query */ -extern BSON *QueryDocument(Oid relationId, - List *opExpressionList, - ForeignScanState *scanStateNode); -extern List *mongo_get_column_list(PlannerInfo *root, - RelOptInfo *foreignrel, - List *scan_var_list); +extern BSON *QueryDocument(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); + Expr *expression, bool is_join_cond); /* 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, MongoJoinQualInfo *jqinfo); +extern const char *mongo_get_jointype_name(JoinType jointype); + #endif /* MONGO_FDW_H */ diff --git a/mongo_query.c b/mongo_query.c index e050f86..2eacee2 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -21,11 +21,16 @@ #if PG_VERSION_NUM < 120000 #include "access/sysattr.h" #endif +#include "access/htup_details.h" #if PG_VERSION_NUM >= 120000 #include "access/table.h" #endif #include "catalog/heap.h" #include "catalog/pg_collation.h" +#include "catalog/pg_operator.h" +#if PG_VERSION_NUM >= 130000 +#include "common/hashfn.h" +#endif #ifdef META_DRIVER #include "mongoc.h" #else @@ -41,6 +46,7 @@ #endif #include "parser/parsetree.h" #include "utils/rel.h" +#include "utils/syscache.h" /* * Global context for foreign_expr_walker's search of an expression tree. @@ -51,6 +57,9 @@ typedef struct foreign_glob_cxt RelOptInfo *foreignrel; /* the foreign relation we are planning for */ unsigned short varcount; /* Var count */ unsigned short opexprcount; + Relids relids; /* relids of base relations in the underlying + * scan */ + bool is_join_cond; /* "true" for join relations */ } foreign_glob_cxt; /* @@ -75,17 +84,24 @@ static Expr *FindArgumentOfType(List *argumentList, NodeTag argumentType); static List *EqualityOperatorList(List *operatorList); static List *UniqueColumnList(List *operatorList); static List *ColumnOperatorList(Var *column, List *operatorList); -static void AppendConstantValue(BSON *queryDocument, const char *keyName, - Const *constant); static void AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, ForeignScanState *scanStateNode); -static char *MongoOperatorName(const char *operatorName); 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 *ColumnInfoHash(List *colname_list, List *colnum_list, + List *rti_list, List *isouter_list); +static void mongo_prepare_inner_pipeline(List *joinclause, + BSON *inner_pipeline, + List *colname_list, + List *isouter_list, + pipeline_cxt *context); +static void mongo_append_joinclauses_to_inner_pipeline(List *joinclause, + BSON *child_doc, + pipeline_cxt *context); /* * FindArgumentOfType @@ -118,118 +134,285 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) /* * QueryDocument - * Takes in the applicable operator expressions for a relation and - * converts these expressions into equivalent queries in MongoDB. + * Takes in the applicable operator expressions for relation and also the + * join clauses for join relation and converts these expressions and join + * clauses 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 name = 'xyz'; + * + * Equivalent MongoDB query: * - * For now, this function can only transform simple comparison expressions, and - * returns these transformed expressions in a BSON document. For example, - * simple expressions: - * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" become + * 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": { "name" : "xyz" } }, + * { + * "$unwind": + * { + * "path": "$Join_Result", + * "preserveNullAndEmptyArrays": true + * } + * } + * ]) + * + * 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 the conditions in WHERE i.e. remote_exprs, this function can only + * transform simple comparison expressions and returns these transformed + * expressions in a BSON document. For example, simple expressions: + * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" becomes * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". */ BSON * -QueryDocument(Oid relationId, List *opExpressionList, - ForeignScanState *scanStateNode) +QueryDocument(ForeignScanState *scanStateNode) { - List *equalityOperatorList; - List *comparisonOperatorList; - List *columnList; - ListCell *equalityOperatorCell; - ListCell *columnCell; + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; + ForeignScan *fsplan = (ForeignScan *) scanStateNode->ss.ps.plan; BSON *queryDocument = BsonCreate(); + BSON *filter = BsonCreate(); + List *PrivateList = fsplan->fdw_private; + List *opExpressionList = list_nth(PrivateList, + mongoFdwPrivateRemoteExprList); + BSON root_pipeline; + int root_index = 0; + List *joinclauses; + List *colname_list = NIL; + List *isouter_list = NIL; + char *inner_relname; + char *outer_relname; + HTAB *columnInfoHash; + int jointype; + + /* Prepare array of stages */ + BsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); + + if (fmstate->isJoinRel) + { + List *colnum_list; + List *rti_list; + List *innerouter_relname; + int natts; + + 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)); + colname_list = list_nth(PrivateList, + mongoFdwPrivateJoinClauseColNameList); + colnum_list = list_nth(PrivateList, + mongoFdwPrivareJoinClauseColNumList); + rti_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseRtiList); + isouter_list = list_nth(PrivateList, + mongoFdwPrivateJoinClauseIsOuterList); + + /* Length should be same for all lists of column information */ + natts = list_length(colname_list); + Assert(natts == list_length(colnum_list) && + natts == list_length(rti_list) && + natts == list_length(isouter_list)); + + columnInfoHash = ColumnInfoHash(colname_list, colnum_list, rti_list, + isouter_list); + } /* - * We distinguish between equality expressions and others since we need to - * insert the latter (<, >, <=, >=, <>) as separate sub-documents into the - * BSON query object. + * 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. */ - equalityOperatorList = EqualityOperatorList(opExpressionList); - comparisonOperatorList = list_difference(opExpressionList, - equalityOperatorList); - - /* Append equality expressions to the query */ - foreach(equalityOperatorCell, equalityOperatorList) + if (opExpressionList) { - OpExpr *equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); - Oid columnId = InvalidOid; - char *columnName; - Const *constant; - Param *paramNode; - List *argumentList = equalityOperator->args; - Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); - - constant = (Const *) FindArgumentOfType(argumentList, T_Const); - paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); - - columnId = column->varattno; + Oid relationId; + List *equalityOperatorList; + List *comparisonOperatorList; + List *columnList; + ListCell *equalityOperatorCell; + ListCell *columnCell; + + if (fsplan->scan.scanrelid > 0) + relationId = RelationGetRelid(scanStateNode->ss.ss_currentRelation); + else + relationId = 0; + + /* + * We distinguish between equality expressions and others since we need + * to insert the latter (<, >, <=, >=, <>) as separate sub-documents + * into the BSON query object. + */ + equalityOperatorList = EqualityOperatorList(opExpressionList); + comparisonOperatorList = list_difference(opExpressionList, + equalityOperatorList); + + /* Append equality expressions to the query */ + foreach(equalityOperatorCell, equalityOperatorList) + { + OpExpr *equalityOperator; + Oid columnId = InvalidOid; + char *columnName; + Const *constant; + Param *paramNode; + List *argumentList; + Var *column; + + equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); + argumentList = equalityOperator->args; + column = (Var *) FindArgumentOfType(argumentList, T_Var); + constant = (Const *) FindArgumentOfType(argumentList, T_Const); + paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); + + if (relationId != 0) + { + columnId = column->varattno; #if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(relationId, columnId); + columnName = get_relid_attribute_name(relationId, columnId); #else - columnName = get_attname(relationId, columnId, false); + columnName = get_attname(relationId, columnId, false); #endif + } + /* For join rel, use columnInfoHash to get column name */ + else + { + 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) + columnName = columnInfo->colName; + } - if (constant != NULL) - AppendConstantValue(queryDocument, columnName, constant); - else - AppendParamValue(queryDocument, columnName, paramNode, - scanStateNode); - } - - /* - * For comparison expressions, we need to group them by their columns and - * append all expressions that correspond to a column as one sub-document. - * - * Otherwise, even when we have two expressions to define the upper- and - * lower-bound of a range, Mongo uses only one of these expressions during - * an index search. - */ - columnList = UniqueColumnList(comparisonOperatorList); + if (constant != NULL) + AppendConstantValue(filter, columnName, constant); + else + AppendParamValue(filter, columnName, paramNode, + scanStateNode); + } - /* Append comparison expressions, grouped by columns, to the query */ - foreach(columnCell, columnList) - { - Var *column = (Var *) lfirst(columnCell); - Oid columnId = InvalidOid; - char *columnName; - List *columnOperatorList; - ListCell *columnOperatorCell; - BSON childDocument; - - columnId = column->varattno; + /* + * For comparison expressions, we need to group them by their columns + * and then append all expressions that correspond to a column as one + * sub-document. Otherwise, even when we have two expressions to + * define the upper and lower bound of a range, Mongo uses only one of + * these expressions during an index search. + */ + columnList = UniqueColumnList(comparisonOperatorList); + + /* Append comparison expressions, grouped by columns, to the query */ + foreach(columnCell, columnList) + { + Var *column = (Var *) lfirst(columnCell); + Oid columnId = InvalidOid; + char *columnName; + List *columnOperatorList; + ListCell *columnOperatorCell; + BSON childDocument; + + if (relationId != 0) + { + columnId = column->varattno; #if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(relationId, columnId); + columnName = get_relid_attribute_name(relationId, columnId); #else - columnName = get_attname(relationId, columnId, false); + columnName = get_attname(relationId, columnId, false); #endif + } + /* For join rel, use columnInfoHash to get column name */ + else + { + 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) + columnName = columnInfo->colName; + } - /* Find all expressions that correspond to the column */ - columnOperatorList = ColumnOperatorList(column, - comparisonOperatorList); + /* Find all expressions that correspond to the column */ + columnOperatorList = ColumnOperatorList(column, + comparisonOperatorList); - /* For comparison expressions, start a sub-document */ - BsonAppendStartObject(queryDocument, columnName, &childDocument); + /* For comparison expressions, start a sub-document */ + BsonAppendStartObject(filter, columnName, &childDocument); - foreach(columnOperatorCell, columnOperatorList) - { - OpExpr *columnOperator = (OpExpr *) lfirst(columnOperatorCell); - char *operatorName; - char *mongoOperatorName; - List *argumentList = columnOperator->args; - Const *constant = (Const *) FindArgumentOfType(argumentList, - T_Const); - - operatorName = get_opname(columnOperator->opno); - mongoOperatorName = MongoOperatorName(operatorName); + foreach(columnOperatorCell, columnOperatorList) + { + OpExpr *columnOperator; + char *operatorName; + char *mongoOperatorName; + List *argumentList; + Const *constant; + + columnOperator = (OpExpr *) lfirst(columnOperatorCell); + argumentList = columnOperator->args; + constant = (Const *) FindArgumentOfType(argumentList, T_Const); + operatorName = get_opname(columnOperator->opno); + mongoOperatorName = MongoOperatorName(operatorName); #ifdef META_DRIVER - AppendConstantValue(&childDocument, mongoOperatorName, constant); + AppendConstantValue(&childDocument, mongoOperatorName, + constant); #else - AppendConstantValue(queryDocument, mongoOperatorName, constant); + AppendConstantValue(filter, mongoOperatorName, constant); #endif + } + BsonAppendFinishObject(filter, &childDocument); } - BsonAppendFinishObject(queryDocument, &childDocument); } - - if (!BsonFinish(queryDocument)) + if (!BsonFinish(filter)) { #ifdef META_DRIVER ereport(ERROR, @@ -242,6 +425,123 @@ QueryDocument(Oid relationId, List *opExpressionList, #endif } + if (fmstate->isJoinRel) + { + BSON inner_pipeline; + BSON lookup_object; + BSON lookup; + BSON let_exprs; + BSON outer_match_stage; + 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); + + if (is_outer) + { + /* + * 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. + */ + char *varname = psprintf("v_%s", 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; + + /* Form equivalent join qual clauses in MongoDB */ + mongo_prepare_inner_pipeline(joinclauses, &inner_pipeline, + colname_list, isouter_list, &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); + + /* $match stage. This is to add a filter */ + BsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + &outer_match_stage); + BsonAppendBson(&outer_match_stage, "$match", filter); + BsonAppendFinishObject(&root_pipeline, &outer_match_stage); + + /* + * $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); + + if (!BsonFinish(queryDocument)) + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); + + fmstate->outerRelName = outer_relname; + } + else + { + BSON match_stage; + + /* $match stage. This is to add a filter for the WHERE clause */ + BsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + &match_stage); + BsonAppendBson(&match_stage, "$match", filter); + BsonAppendFinishObject(&root_pipeline, &match_stage); + } + + BsonAppendFinishArray(queryDocument, &root_pipeline); + + if (!BsonFinish(queryDocument)) + { + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); + } + return queryDocument; } @@ -250,16 +550,25 @@ QueryDocument(Oid relationId, List *opExpressionList, * Takes in the given PostgreSQL comparison operator name, and returns its * equivalent in MongoDB. */ -static char * +char * MongoOperatorName(const char *operatorName) { const char *mongoOperatorName = NULL; - const int32 nameCount = 5; + const int32 nameCount = 14; static const char *nameMappings[][2] = {{"<", "$lt"}, {">", "$gt"}, {"<=", "$lte"}, {">=", "$gte"}, - {"<>", "$ne"}}; + {"<>", "$ne"}, + {"=", "$eq"}, + {"+", "$add"}, + {"-", "$subtract"}, + {"*", "$multiply"}, + {"/", "$divide"}, + {"%", "$mod"}, + {"^", "$pow"}, + {"|/", "$sqrt"}, + {"@", "$abs"}}; int32 nameIndex; for (nameIndex = 0; nameIndex < nameCount; nameIndex++) @@ -386,7 +695,7 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, * The function translates the constant value from its PostgreSQL type * to its MongoDB equivalent. */ -static void +void AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) { if (constant->constisnull) @@ -679,10 +988,15 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, * 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 *scan_var_list, List **column_name_list, + List **is_inner_column_list) { List *columnList = NIL; ListCell *lc; @@ -690,6 +1004,8 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, foreach(lc, scan_var_list) { Var *var = (Var *) lfirst(lc); + RangeTblEntry *rte = planner_rt_fetch(var->varno, root); + int is_innerrel = false; Assert(IsA(var, Var)); @@ -720,6 +1036,29 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, } else columnList = list_append_unique(columnList, var); + +#if PG_VERSION_NUM >= 100000 + if (IS_JOIN_REL(foreignrel)) +#else + if (foreignrel->reloptkind == RELOPT_JOINREL) +#endif + { + MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; + char *columnName; + +#if PG_VERSION_NUM < 110000 + columnName = get_relid_attribute_name(rte->relid, var->varattno); +#else + columnName = get_attname(rte->relid, var->varattno, false); +#endif + *column_name_list = lappend(*column_name_list, + makeString(columnName)); + if (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; @@ -735,8 +1074,15 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, * as being built-in), and that all collations used in the expression derive * from Vars of the foreign table. * - * We only support simple binary operators that compare a column against a - * constant. If the expression is a tree, we don't recurse into it. + * For WHERE clauses, we only support simple binary operators that compare a + * column against a constant. If the expression is a tree, we don't recurse + * into it. + * + * For JOIN clauses, in addition to the above support, 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, @@ -770,7 +1116,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, * Param's collation, i.e. it's not safe for it to have a * non-default collation. */ - if (var->varno == glob_cxt->foreignrel->relid && + if (bms_is_member(var->varno, glob_cxt->relids) && var->varlevelsup == 0) { /* Var belongs to foreign table */ @@ -862,17 +1208,23 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* Increment the operator expression count */ glob_cxt->opexprcount++; - /* We only support =, <, >, <=, >=, and <> operators */ + /* + * We support =, <, >, <=, >=, <>, +, -, *, /, %, ^, |/, and @ + * operators for joinclause of join relation. + */ if (!(strncmp(oname, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) && (MongoOperatorName(oname) == NULL)) return false; /* * Recurse to input subexpressions. + * + * We support only =, <, >, <=, >= and <> operators for WHERE + * conditions of simple as well as join relation. */ - if (glob_cxt->opexprcount > 1 || - !foreign_expr_walker((Node *) oe->args, - glob_cxt, &inner_cxt)) + if (!foreign_expr_walker((Node *) oe->args, glob_cxt, + &inner_cxt) || + (!glob_cxt->is_join_cond && glob_cxt->opexprcount > 1)) return false; /* @@ -933,15 +1285,16 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* * Recurse to component subexpressions. * - * If comparison is between two columns of same table then we - * don't push down because currently building corresponding - * MongoDB query not possible with the help of MongoC driver. + * 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)) || - glob_cxt->varcount > 1) + (!(glob_cxt->is_join_cond) && glob_cxt->varcount > 1)) return false; } @@ -953,6 +1306,22 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, 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; default: /* @@ -1016,7 +1385,8 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, * Returns true if given expr is safe to evaluate on the foreign server. */ bool -mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression) +mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, + bool is_join_cond) { foreign_glob_cxt glob_cxt; foreign_loc_cxt loc_cxt; @@ -1027,8 +1397,10 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression) */ glob_cxt.root = root; glob_cxt.foreignrel = baserel; + glob_cxt.relids = baserel->relids; glob_cxt.varcount = 0; glob_cxt.opexprcount = 0; + glob_cxt.is_join_cond = is_join_cond; loc_cxt.collation = InvalidOid; loc_cxt.state = FDW_COLLATE_NONE; if (!foreign_expr_walker((Node *) expression, &glob_cxt, &loc_cxt)) @@ -1105,3 +1477,152 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) return tlist; } + +/* + * ColumnInfoHash + * 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 * +ColumnInfoHash(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; + +#if PG_VERSION_NUM >= 130000 + l4 = lnext(isouter_list, l4); +#else + l4 = lnext(l4); +#endif + } + + return columnInfoHash; +} + +/* + * mongo_prepare_inner_pipeline + * Form inner query pipeline syntax equivalent to postgresql join clauses. + * + * From the example given on QueryDocument, 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_inner_pipeline(List *joinclause, BSON *inner_pipeline, + List *colname_list, List *isouter_list, + pipeline_cxt *context) +{ + BSON *and_query_doc = BsonCreate(); + BSON match_object; + BSON match_stage; + BSON expr; + BSON and_op; + int inner_pipeline_index = 0; + + BsonAppendStartObject(inner_pipeline, + psprintf("%d", inner_pipeline_index++), + &match_object); + BsonAppendStartObject(&match_object, "$match", &match_stage); + BsonAppendStartObject(&match_stage, "$expr", &expr); + + BsonAppendStartArray(and_query_doc, "$and", &and_op); + + context->arrayIndex = 0; + + /* Append join clause expression */ + mongo_append_joinclauses_to_inner_pipeline(joinclause, &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); + BsonAppendFinishObject(&match_object, &match_stage); + BsonAppendFinishObject(inner_pipeline, &match_object); +} + +/* + * mongo_append_joinclauses_to_inner_pipeline + * Append all join expressions to mongoDB's $and array. + */ +static void +mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, + pipeline_cxt *context) +{ + ListCell *lc; + + /* loop through all join-clauses */ + foreach(lc, joinclause) + { + 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++; + } +} diff --git a/mongo_query.h b/mongo_query.h index 81c184d..ff50d9b 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -17,8 +17,102 @@ #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? */ +} 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, + + /* Join information */ + + /* + * 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, + + /* List of join clauses to form a pipeline */ + mongoFdwPrivateJoinClauseList, + + /* + * 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, + + /* Inner and Outer relation names */ + mongoFdwPrivateJoinInnerOuterRelName, + + /* Join-type */ + mongoFdwPrivateJoinType +}; + /* Function to be used in mongo_fdw.c */ extern bool AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, bool isnull, Oid id); +/* Functions to be used in deparse.c */ +extern char *MongoOperatorName(const char *operatorName); +extern void AppendConstantValue(BSON *queryDocument, const char *keyName, + Const *constant); +extern void mongo_append_expr(Expr *node, BSON *child_doc, + pipeline_cxt *context); + #endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index f64b31f..ff3c21e 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -249,7 +249,7 @@ MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) bson_error_t error; c = mongoc_client_get_collection(conn, database, collection); - cur = mongoc_collection_find_with_opts(c, q, NULL, NULL); + cur = mongoc_collection_aggregate(c, MONGOC_QUERY_NONE, q, NULL, NULL); mongoc_cursor_error(cur, &error); if (!cur) ereport(ERROR, From 9fff635e48c00e0c3ea69035ac6c08afdd05962d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 15 Nov 2021 08:28:03 +0530 Subject: [PATCH 166/239] Don't support join pushdown in legacy mode. The required MongoDB operators are not present (or complex to implement), so don't support join pushdown there. Also, the MongoDB community stopped supporting this version(0.8) of the driver. FDW-138, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. Tested by Rajkumar Raghuwanshi. --- mongo_fdw.c | 14 ++++++++++++++ mongo_query.c | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 8257b23..b17b23d 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -137,11 +137,13 @@ static void MongoBeginForeignInsert(ModifyTableState *mtstate, static void MongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo); #endif +#ifdef META_DRIVER static void MongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); +#endif /* * Helper functions @@ -169,11 +171,13 @@ static int MongoAcquireSampleRows(Relation relation, double *totalRowCount, double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); +#ifdef META_DRIVER 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, MongoJoinQualInfo *jqinfo); +#endif /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -246,8 +250,10 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->EndForeignInsert = MongoEndForeignInsert; #endif +#ifdef META_DRIVER /* Support function for join push-down */ fdwRoutine->GetForeignJoinPaths = MongoGetForeignJoinPaths; +#endif PG_RETURN_POINTER(fdwRoutine); } @@ -471,7 +477,9 @@ MongoGetForeignPlan(PlannerInfo *root, List *fdw_scan_tlist = NIL; List *column_name_list = NIL; List *is_inner_column_list = NIL; +#ifdef META_DRIVER MongoJoinQualInfo *jqinfo; +#endif /* Set scan relation id */ if (foreignrel->reloptkind == RELOPT_BASEREL || @@ -635,6 +643,7 @@ MongoGetForeignPlan(PlannerInfo *root, &column_name_list, &is_inner_column_list); +#ifdef META_DRIVER /* * Prepare separate lists of column names, varno, varattno, and whether it * is part of the outer relation or not. This information would be useful @@ -682,6 +691,7 @@ MongoGetForeignPlan(PlannerInfo *root, /* Destroy hash table used to get unique column info */ hash_destroy(jqinfo->joinExprColHash); } +#endif /* * Build the fdw_private list that will be available to the executor. @@ -689,6 +699,7 @@ MongoGetForeignPlan(PlannerInfo *root, */ fdw_private = list_make2(columnList, remote_exprs); +#ifdef META_DRIVER /* * Unlike postgres_fdw, remote query formation is done in the execution * state. There is NO way to get the correct information required to form @@ -716,6 +727,7 @@ MongoGetForeignPlan(PlannerInfo *root, makeString(fpinfo->outer_relname))); fdw_private = lappend(fdw_private, makeInteger(fpinfo->jointype)); } +#endif /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, local_exprs, @@ -2689,6 +2701,7 @@ MongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) } #endif +#ifdef META_DRIVER /* * MongoGetForeignJoinPaths * Add possible ForeignPath to joinrel, if the join is safe to push down. @@ -3042,3 +3055,4 @@ mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo) mongo_check_qual(expr, jqinfo); } } +#endif /* End of META_DRIVER */ diff --git a/mongo_query.c b/mongo_query.c index 2eacee2..ddf5900 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -92,6 +92,7 @@ static bool foreign_expr_walker(Node *node, foreign_loc_cxt *outer_cxt); static List *prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used); +#ifdef META_DRIVER static HTAB *ColumnInfoHash(List *colname_list, List *colnum_list, List *rti_list, List *isouter_list); static void mongo_prepare_inner_pipeline(List *joinclause, @@ -102,6 +103,7 @@ static void mongo_prepare_inner_pipeline(List *joinclause, static void mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, pipeline_cxt *context); +#endif /* * FindArgumentOfType @@ -204,13 +206,14 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) BSON * QueryDocument(ForeignScanState *scanStateNode) { - MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; ForeignScan *fsplan = (ForeignScan *) scanStateNode->ss.ps.plan; BSON *queryDocument = BsonCreate(); BSON *filter = BsonCreate(); List *PrivateList = fsplan->fdw_private; List *opExpressionList = list_nth(PrivateList, mongoFdwPrivateRemoteExprList); +#ifdef META_DRIVER + MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; BSON root_pipeline; int root_index = 0; List *joinclauses; @@ -256,6 +259,7 @@ QueryDocument(ForeignScanState *scanStateNode) columnInfoHash = ColumnInfoHash(colname_list, colnum_list, rti_list, isouter_list); } +#endif /* * Add filter into query pipeline if available. These are remote_exprs @@ -311,6 +315,7 @@ QueryDocument(ForeignScanState *scanStateNode) columnName = get_attname(relationId, columnId, false); #endif } +#ifdef META_DRIVER /* For join rel, use columnInfoHash to get column name */ else { @@ -328,6 +333,7 @@ QueryDocument(ForeignScanState *scanStateNode) if (found) columnName = columnInfo->colName; } +#endif if (constant != NULL) AppendConstantValue(filter, columnName, constant); @@ -364,6 +370,7 @@ QueryDocument(ForeignScanState *scanStateNode) columnName = get_attname(relationId, columnId, false); #endif } +#ifdef META_DRIVER /* For join rel, use columnInfoHash to get column name */ else { @@ -381,6 +388,7 @@ QueryDocument(ForeignScanState *scanStateNode) if (found) columnName = columnInfo->colName; } +#endif /* Find all expressions that correspond to the column */ columnOperatorList = ColumnOperatorList(column, @@ -425,6 +433,7 @@ QueryDocument(ForeignScanState *scanStateNode) #endif } +#ifdef META_DRIVER if (fmstate->isJoinRel) { BSON inner_pipeline; @@ -543,6 +552,9 @@ QueryDocument(ForeignScanState *scanStateNode) } return queryDocument; +#endif + + return filter; } /* @@ -1478,6 +1490,7 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) return tlist; } +#ifdef META_DRIVER /* * ColumnInfoHash * Creates a hash table that maps varno and varattno to the column names, @@ -1626,3 +1639,4 @@ mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, context->arrayIndex++; } } +#endif From 68cff4fca72975bb4eba49d73ee37415a38c814c Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 15 Nov 2021 08:28:03 +0530 Subject: [PATCH 167/239] Add test coverage for the join push-down feature. As various version has slight output change, need to add multiple alternate files for the newly added test case. FDW-138, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. Tested by Rajkumar Raghuwanshi. --- Makefile.meta | 2 +- data/mongo_test_data.js | 32 + expected/join_pushdown.out | 1596 +++++++++++++++++++++++++++++++++ expected/join_pushdown_1.out | 1590 +++++++++++++++++++++++++++++++++ expected/join_pushdown_2.out | 1593 +++++++++++++++++++++++++++++++++ expected/join_pushdown_3.out | 1598 ++++++++++++++++++++++++++++++++++ expected/join_pushdown_4.out | 1475 +++++++++++++++++++++++++++++++ sql/join_pushdown.sql | 420 +++++++++ 8 files changed, 8305 insertions(+), 1 deletion(-) create mode 100644 expected/join_pushdown.out create mode 100644 expected/join_pushdown_1.out create mode 100644 expected/join_pushdown_2.out create mode 100644 expected/join_pushdown_3.out create mode 100644 expected/join_pushdown_4.out create mode 100644 sql/join_pushdown.sql diff --git a/Makefile.meta b/Makefile.meta index 5385782..ccbfbe1 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o depa 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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index 7c7e0a1..859d06c 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -12,6 +12,10 @@ use mongo_fdw_regress db.test_tbl1.drop(); db.test_tbl2.drop(); db.test_tbl3.drop(); +db.test1.drop(); +db.test2.drop(); +db.test3.drop(); +db.test4.drop(); db.mongo_test.drop(); // Below queries will create and insert values in collections db.mongo_test.insert({a : NumberInt(0), b : "mongo_test collection"}); @@ -41,3 +45,31 @@ 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"}, +]); diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out new file mode 100644 index 0000000..e21cfc5 --- /dev/null +++ b/expected/join_pushdown.out @@ -0,0 +1,1596 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------- + Incremental Sort + Sort Key: d.c1, e.c1 + Presorted Key: d.c1 + -> Merge Left Join + Merge Cond: (d.c1 = e.c8) + -> Sort + Sort Key: d.c1 + -> Foreign Scan on f_test_tbl2 d + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: e.c8 + -> Seq Scan on l_test_tbl1 e +(12 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Merge Join + Merge Cond: (e.c1 = d.c8) + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(13 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. +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 + -> Merge Left Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> 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 +(19 rows) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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 + -> Merge Full Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(13 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) + +-- Test partition-wise join +SET enable_partitionwise_join TO on; +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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'); +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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 +------------------------------------------------------------------- + Merge Append + Sort Key: t1.c1, t1.c2 + -> Merge Join + Output: t1_1.c1, t1_1.c2 + Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) + -> Sort + Output: t1_1.c1, t1_1.c2 + Sort Key: t1_1.c1, t1_1.c2 + -> Foreign Scan on public.ftprt1_p1 t1_1 + Output: t1_1.c1, t1_1.c2 + Filter: ((t1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: t2_1.c2, t2_1.c1 + Sort Key: t2_1.c2, t2_1.c1 + -> Foreign Scan on public.ftprt2_p1 t2_1 + Output: t2_1.c2, t2_1.c1 + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Join + Output: t1_2.c1, t1_2.c2 + Merge Cond: ((t1_2.c1 = t2_2.c2) AND (t1_2.c2 = t2_2.c1)) + -> Sort + Output: t1_2.c1, t1_2.c2 + Sort Key: t1_2.c1, t1_2.c2 + -> Foreign Scan on public.ftprt1_p2 t1_2 + Output: t1_2.c1, t1_2.c2 + Filter: ((t1_2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: t2_2.c2, t2_2.c1 + Sort Key: t2_2.c2, t2_2.c1 + -> Foreign Scan on public.ftprt2_p2 t2_2 + Output: t2_2.c2, t2_2.c1 + Foreign Namespace: mongo_fdw_regress.test4 +(34 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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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 + Filter: ((fprt1_1.c1 % 2) = 0) + 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 + Filter: ((fprt2_1.c2 % 2) = 0) + 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 + Filter: ((fprt1_2.c1 % 2) = 0) + 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 + Filter: ((fprt2_2.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test4 +(40 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; +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +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 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; diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out new file mode 100644 index 0000000..f0d52ca --- /dev/null +++ b/expected/join_pushdown_1.out @@ -0,0 +1,1590 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: d.c1, e.c1 + -> 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Merge Join + Merge Cond: (e.c1 = d.c8) + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(13 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. +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 + -> Merge Left Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> 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 +(19 rows) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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 + -> Merge Full Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(13 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) + +-- Test partition-wise join +SET enable_partitionwise_join TO on; +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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'); +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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.c1, t2.c2 + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Foreign Scan + Output: t1_1.c1, t2_1.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.c1, t2.c2, t3.c2 + Hash Cond: (t1.c1 = t3.c1) + -> Foreign Scan + Output: t1.c1, t2.c2 + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Hash + Output: t3.c2, t3.c1 + -> Foreign Scan on public.ftprt1_p1 t3 + Output: t3.c2, t3.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> 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.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2) + -> Hash + Output: t3_1.c2, t3_1.c1 + -> Foreign Scan on public.ftprt1_p2 t3_1 + Output: t3_1.c2, t3_1.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 +------------------------------------------------------------------- + Merge Append + Sort Key: t1.c1, t1.c2 + -> Merge Join + Output: t1.c1, t1.c2 + Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) + -> Sort + Output: t1.c1, t1.c2 + Sort Key: t1.c1, t1.c2 + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1, t1.c2 + Filter: ((t1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: t2.c2, t2.c1 + Sort Key: t2.c2, t2.c1 + -> Foreign Scan on public.ftprt2_p1 t2 + Output: t2.c2, t2.c1 + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Join + Output: t1_1.c1, t1_1.c2 + Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) + -> Sort + Output: t1_1.c1, t1_1.c2 + Sort Key: t1_1.c1, t1_1.c2 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1, t1_1.c2 + Filter: ((t1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: t2_1.c2, t2_1.c1 + Sort Key: t2_1.c2, t2_1.c1 + -> Foreign Scan on public.ftprt2_p2 t2_1 + Output: t2_1.c2, t2_1.c1 + Foreign Namespace: mongo_fdw_regress.test4 +(34 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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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 +------------------------------------------------------------------------------------ + Sort + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 + -> Append + -> Merge Left Join + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) + -> Sort + Output: ftprt1_p1.c1 + Sort Key: ftprt1_p1.c1 + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1 + Filter: ((ftprt1_p1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt2_p1.c2 + -> Foreign Scan on public.ftprt2_p1 + Output: ftprt2_p1.c2, 't2_phv'::text + Filter: ((ftprt2_p1.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Left Join + Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) + Merge Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) + -> Sort + Output: ftprt1_p2.c1 + Sort Key: ftprt1_p2.c1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1 + Filter: ((ftprt1_p2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: ftprt2_p2.c2, ('t2_phv'::text) + Sort Key: ftprt2_p2.c2 + -> Foreign Scan on public.ftprt2_p2 + Output: ftprt2_p2.c2, 't2_phv'::text + Filter: ((ftprt2_p2.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test4 +(38 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; +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +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 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out new file mode 100644 index 0000000..efad1fa --- /dev/null +++ b/expected/join_pushdown_2.out @@ -0,0 +1,1593 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: d.c1, e.c1 + -> 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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: t.c1_1, t.c2_1, t.c1_3 + Sort Key: t.c1_3, t.c1_1 + CTE t + -> Foreign Scan + Output: d.c1, d.c3, e.c1 + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) + -> CTE Scan on t + Output: t.c1_1, t.c2_1, t.c1_3 +(9 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Merge Join + Merge Cond: (e.c1 = d.c8) + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(13 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. +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 + -> Merge Left Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> 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 +(19 rows) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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 + -> Merge Full Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(13 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) + +-- Test partition-wise join +SET enable_partitionwise_join TO on; +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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'); +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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.c1, t2.c2 + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Foreign Scan + Output: t1_1.c1, t2_1.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.c1, t2.c2, t3.c2 + Hash Cond: (t1.c1 = t3.c1) + -> Foreign Scan + Output: t1.c1, t2.c2 + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Hash + Output: t3.c2, t3.c1 + -> Foreign Scan on public.ftprt1_p1 t3 + Output: t3.c2, t3.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> 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.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2) + -> Hash + Output: t3_1.c2, t3_1.c1 + -> Foreign Scan on public.ftprt1_p2 t3_1 + Output: t3_1.c2, t3_1.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 +------------------------------------------------------------------- + Merge Append + Sort Key: t1.c1, t1.c2 + -> Merge Join + Output: t1.c1, t1.c2 + Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) + -> Sort + Output: t1.c1, t1.c2 + Sort Key: t1.c1, t1.c2 + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1, t1.c2 + Filter: ((t1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: t2.c2, t2.c1 + Sort Key: t2.c2, t2.c1 + -> Foreign Scan on public.ftprt2_p1 t2 + Output: t2.c2, t2.c1 + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Join + Output: t1_1.c1, t1_1.c2 + Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) + -> Sort + Output: t1_1.c1, t1_1.c2 + Sort Key: t1_1.c1, t1_1.c2 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1, t1_1.c2 + Filter: ((t1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: t2_1.c2, t2_1.c1 + Sort Key: t2_1.c2, t2_1.c1 + -> Foreign Scan on public.ftprt2_p2 t2_1 + Output: t2_1.c2, t2_1.c1 + Foreign Namespace: mongo_fdw_regress.test4 +(34 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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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 +------------------------------------------------------------------------------------ + Sort + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 + -> Append + -> Merge Left Join + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) + -> Sort + Output: ftprt1_p1.c1 + Sort Key: ftprt1_p1.c1 + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1 + Filter: ((ftprt1_p1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt2_p1.c2 + -> Foreign Scan on public.ftprt2_p1 + Output: ftprt2_p1.c2, 't2_phv'::text + Filter: ((ftprt2_p1.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Left Join + Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) + Merge Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) + -> Sort + Output: ftprt1_p2.c1 + Sort Key: ftprt1_p2.c1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1 + Filter: ((ftprt1_p2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: ftprt2_p2.c2, ('t2_phv'::text) + Sort Key: ftprt2_p2.c2 + -> Foreign Scan on public.ftprt2_p2 + Output: ftprt2_p2.c2, 't2_phv'::text + Filter: ((ftprt2_p2.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test4 +(38 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; +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +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 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out new file mode 100644 index 0000000..ae06c4b --- /dev/null +++ b/expected/join_pushdown_3.out @@ -0,0 +1,1598 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: d.c1, e.c1 + -> 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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: t.c1_1, t.c2_1, t.c1_3 + Sort Key: t.c1_3, t.c1_1 + CTE t + -> Foreign Scan + Output: d.c1, d.c3, e.c1 + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) + -> CTE Scan on t + Output: t.c1_1, t.c2_1, t.c1_3 +(9 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Merge Join + Merge Cond: (e.c1 = d.c8) + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(13 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. +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 + -> Merge Left Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> 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 +(19 rows) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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 + -> Merge Full Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(13 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) + +-- Test partition-wise join +SET enable_partitionwise_join TO on; +ERROR: unrecognized configuration parameter "enable_partitionwise_join" +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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'); +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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 + -> Hash Join + Output: t1.c1, t2.c2 + Hash Cond: (t1.c1 = t2.c2) + -> Append + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1 + Foreign Namespace: mongo_fdw_regress.test2 + -> Hash + Output: t2.c2 + -> Append + -> Foreign Scan on public.ftprt2_p1 t2 + Output: t2.c2 + Foreign Namespace: mongo_fdw_regress.test3 + -> Foreign Scan on public.ftprt2_p2 t2_1 + Output: t2_1.c2 + Foreign Namespace: mongo_fdw_regress.test4 +(22 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 + -> Hash Join + Output: t1.c1, t2.c2, t3.c2 + Hash Cond: (t1.c1 = t3.c1) + -> Hash Join + Output: t1.c1, t2.c2 + Hash Cond: (t1.c1 = t2.c2) + -> Append + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1 + Foreign Namespace: mongo_fdw_regress.test2 + -> Hash + Output: t2.c2 + -> Append + -> Foreign Scan on public.ftprt2_p1 t2 + Output: t2.c2 + Foreign Namespace: mongo_fdw_regress.test3 + -> Foreign Scan on public.ftprt2_p2 t2_1 + Output: t2_1.c2 + Foreign Namespace: mongo_fdw_regress.test4 + -> Hash + Output: t3.c2, t3.c1 + -> Append + -> Foreign Scan on public.ftprt1_p1 t3 + Output: t3.c2, t3.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t3_1 + Output: t3_1.c2, t3_1.c1 + Foreign Namespace: mongo_fdw_regress.test2 +(34 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 +---------------------------------------------------------------- + Merge Join + Output: t1.c1, t1.c2 + Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) + -> Sort + Output: t1.c1, t1.c2 + Sort Key: t1.c1, t1.c2 + -> Append + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1, t1.c2 + Filter: ((t1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1, t1_1.c2 + Filter: ((t1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: t2.c2, t2.c1 + Sort Key: t2.c2, t2.c1 + -> Append + -> Foreign Scan on public.ftprt2_p1 t2 + Output: t2.c2, t2.c1 + Foreign Namespace: mongo_fdw_regress.test3 + -> Foreign Scan on public.ftprt2_p2 t2_1 + Output: t2_1.c2, t2_1.c1 + Foreign Namespace: mongo_fdw_regress.test4 +(25 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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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 +------------------------------------------------------------------------------ + Sort + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 + -> Merge Left Join + Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) + Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) + -> Sort + Output: ftprt1_p1.c1 + Sort Key: ftprt1_p1.c1 + -> Append + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1 + Filter: ((ftprt1_p1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1 + Filter: ((ftprt1_p2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: ftprt2_p1.c2, ('t2_phv'::text) + Sort Key: ftprt2_p1.c2 + -> Append + -> Foreign Scan on public.ftprt2_p1 + Output: ftprt2_p1.c2, 't2_phv'::text + Filter: ((ftprt2_p1.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test3 + -> Foreign Scan on public.ftprt2_p2 + Output: ftprt2_p2.c2, 't2_phv'::text + Filter: ((ftprt2_p2.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test4 +(30 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; +ERROR: unrecognized configuration parameter "enable_partitionwise_join" +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +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 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; diff --git a/expected/join_pushdown_4.out b/expected/join_pushdown_4.out new file mode 100644 index 0000000..54cc953 --- /dev/null +++ b/expected/join_pushdown_4.out @@ -0,0 +1,1475 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: d.c1, e.c1 + -> 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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: t.c1_1, t.c2_1, t.c1_3 + Sort Key: t.c1_3, t.c1_1 + CTE t + -> Foreign Scan + Output: d.c1, d.c3, e.c1 + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) + -> CTE Scan on t + Output: t.c1_1, t.c2_1, t.c1_3 +(9 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Merge Join + Merge Cond: (e.c1 = d.c8) + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(13 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. +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 + -> Merge Left Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> 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 +(19 rows) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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 + -> Merge Full Join + Merge Cond: (d.c8 = e.c1) + -> Sort + Sort Key: d.c8 + -> Foreign Scan on f_test_tbl1 d + Foreign Namespace: mongo_fdw_regress.test_tbl1 + -> Sort + Sort Key: e.c1 + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(13 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) + +-- Test partition-wise join +SET enable_partitionwise_join TO on; +ERROR: unrecognized configuration parameter "enable_partitionwise_join" +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +NOTICE: syntax error +CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1'); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES... + ^ +CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2'); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES... + ^ +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +NOTICE: syntax error +CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3'); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES... + ^ +CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8) + SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4'); +ERROR: syntax error at or near "PARTITION" +LINE 1: CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES... + ^ +-- 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; +ERROR: relation "fprt1" does not exist +LINE 3: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER... + ^ +SELECT t1.c1, t2.c2 + FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2; +ERROR: relation "fprt1" does not exist +LINE 2: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER... + ^ +-- 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; +ERROR: relation "fprt1" does not exist +LINE 3: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER... + ^ +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; +ERROR: relation "fprt1" does not exist +LINE 2: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER... + ^ +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; +ERROR: relation "fprt1" does not exist +LINE 3: FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2 + ^ +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; +ERROR: relation "fprt1" does not exist +LINE 2: FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2 + ^ +-- With PHVs, partitionwise join selected but no join pushdown +-- Table alias in foreign scan is different for v12, v11 and v10. +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; +ERROR: relation "fprt1" does not exist +LINE 3: FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) ... + ^ +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; +ERROR: relation "fprt1" does not exist +LINE 2: FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) ... + ^ +RESET enable_partitionwise_join; +ERROR: unrecognized configuration parameter "enable_partitionwise_join" +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +DROP FOREIGN TABLE test_text; +DROP FOREIGN TABLE test_varchar; +DROP TABLE l_test_tbl1; +DROP FOREIGN TABLE ftprt1_p1; +ERROR: foreign table "ftprt1_p1" does not exist +DROP FOREIGN TABLE ftprt1_p2; +ERROR: foreign table "ftprt1_p2" does not exist +DROP FOREIGN TABLE ftprt2_p1; +ERROR: foreign table "ftprt2_p1" does not exist +DROP FOREIGN TABLE ftprt2_p2; +ERROR: foreign table "ftprt2_p2" does not exist +DROP TABLE IF EXISTS fprt1; +NOTICE: table "fprt1" does not exist, skipping +DROP TABLE IF EXISTS fprt2; +NOTICE: table "fprt2" does not exist, skipping +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; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql new file mode 100644 index 0000000..1a7a229 --- /dev/null +++ b/sql/join_pushdown.sql @@ -0,0 +1,420 @@ +\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 'edb' user with 'edb' 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'); + +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, 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 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 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, 3 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, 3 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, 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 (d.c1 = e.c8 AND e.c4 > d.c1 AND e.c2 < d.c3) ORDER BY 1, 3; +-- 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, 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 d.c1 = 20 ORDER BY 1, 3; + +-- 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, 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; +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, 3; +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, 3; +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + +-- 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, d.c1; +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, d.c1; +-- 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, d.c1; +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, d.c1; + +-- 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, 3; +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; + +-- 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, 3; +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, 3; +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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; +RESET enable_hashjoin; +RESET enable_nestloop; + +-- 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, 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 d.c1 = e.c8 WHERE d.c1 = 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 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, 3; +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, 3; + +-- 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; + +-- This won't push-down because WHERE only pushes operator expression. +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. +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; + +-- Don't pushdown when full document retrieval is involved. +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"; + +-- 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; + +-- Test partition-wise join +SET enable_partitionwise_join TO on; + +-- Create the partition table in plpgsql block as those are failing with +-- different error messages on back-branches. +-- All test cases related to partition-wise join gives an error on v96 as +-- partition syntax is not supported there. +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; +EXCEPTION WHEN others THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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'); + +DO +$$ +BEGIN + EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; +EXCEPTION WHEN syntax_error THEN + RAISE NOTICE 'syntax error'; +END; +$$ +LANGUAGE plpgsql; +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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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; + +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; +DROP FOREIGN TABLE f_test_tbl1; +DROP FOREIGN TABLE f_test_tbl2; +DROP FOREIGN TABLE f_test_tbl3; +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 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; From cdfaf88ae8e1ff4fd7b261f2b03f63f26fb37822 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 15 Nov 2021 08:28:03 +0530 Subject: [PATCH 168/239] Add enable_join_pushdown option to control join push-down. Some join queries when evaluated on the remote server take more time than evaluating a join locally. Thus, add an option to control the join push-down to avoid such performance issues. This option can be set at the server level or the table level. If any of the tables involved in the join has this option set 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. Default is true. FDW-445, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. --- README.md | 9 ++ expected/join_pushdown.out | 176 +++++++++++++++++++++++++++++++++++ expected/join_pushdown_1.out | 172 ++++++++++++++++++++++++++++++++++ expected/join_pushdown_2.out | 172 ++++++++++++++++++++++++++++++++++ expected/join_pushdown_3.out | 172 ++++++++++++++++++++++++++++++++++ expected/join_pushdown_4.out | 172 ++++++++++++++++++++++++++++++++++ mongo_fdw.c | 22 ++++- mongo_fdw.h | 10 +- option.c | 9 +- sql/join_pushdown.sql | 51 ++++++++++ 10 files changed, 959 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 050a612..aaa7655 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,13 @@ The following options are only supported with meta driver: * `ca_dir`: SSL option. * `crl_file`: SSL option. * `weak_cert_validation`: SSL option, false [default]. + * `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. 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. Default is `true`. The following parameters can be set on a MongoDB foreign table object: @@ -167,6 +174,8 @@ The following parameters can be set on a MongoDB foreign table object: `test`. * `collection`: Name of the MongoDB collection to query. Defaults to the foreign table name used in the relevant `CREATE` command. + * `enable_join_pushdown`: Similar to the server-level option, but can be + configured at table level as well. Default is `true`. The following parameters can be supplied while creating user mapping: diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index e21cfc5..898ab59 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -24,6 +24,8 @@ 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'); 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'); @@ -1573,6 +1575,179 @@ SELECT t1.c1, t1.phv, t2.c2, 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) + 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; @@ -1580,6 +1755,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index f0d52ca..03f182c 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -24,6 +24,8 @@ 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'); 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'); @@ -1567,6 +1569,175 @@ SELECT t1.c1, t1.phv, t2.c2, 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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) + 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; @@ -1574,6 +1745,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index efad1fa..ecd3270 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -24,6 +24,8 @@ 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'); 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'); @@ -1570,6 +1572,175 @@ SELECT t1.c1, t1.phv, t2.c2, 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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) + 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; @@ -1577,6 +1748,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index ae06c4b..87ee0db 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -24,6 +24,8 @@ 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'); 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'); @@ -1575,6 +1577,175 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv RESET enable_partitionwise_join; ERROR: unrecognized configuration parameter "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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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) + 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; @@ -1582,6 +1753,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; diff --git a/expected/join_pushdown_4.out b/expected/join_pushdown_4.out index 54cc953..efcfcb5 100644 --- a/expected/join_pushdown_4.out +++ b/expected/join_pushdown_4.out @@ -24,6 +24,8 @@ 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'); 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'); @@ -1446,6 +1448,175 @@ LINE 2: FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) ... ^ RESET enable_partitionwise_join; ERROR: unrecognized configuration parameter "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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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 +-------------------------------------------------------------------- + Sort + Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 + Sort Key: d.c1, e.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 +(18 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) + 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; @@ -1453,6 +1624,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; diff --git a/mongo_fdw.c b/mongo_fdw.c index b17b23d..73c1362 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -354,6 +354,9 @@ MongoGetForeignRelSize(PlannerInfo *root, 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; } /* @@ -2834,6 +2837,22 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, jointype != JOIN_RIGHT) return false; + fpinfo = (MongoFdwRelationInfo *) joinrel->fdw_private; + fpinfo_o = (MongoFdwRelationInfo *) outerrel->fdw_private; + fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private; + + /* If join pushdown is not enabled, honor it. */ +#if PG_VERSION_NUM >= 100000 + if ((!IS_JOIN_REL(outerrel) && !fpinfo_o->options->enable_join_pushdown) || + (!IS_JOIN_REL(innerrel) && !fpinfo_i->options->enable_join_pushdown)) +#else + if ((outerrel->reloptkind != RELOPT_JOINREL && + !fpinfo_o->options->enable_join_pushdown) || + (innerrel->reloptkind != RELOPT_JOINREL && + !fpinfo_i->options->enable_join_pushdown)) +#endif + return false; + /* Recursive joins can't be pushed down */ #if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(outerrel) || IS_JOIN_REL(innerrel)) @@ -2847,9 +2866,6 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, * If either of the joining relations is marked as unsafe to pushdown, the * join can not be pushed down. */ - fpinfo = (MongoFdwRelationInfo *) joinrel->fdw_private; - fpinfo_o = (MongoFdwRelationInfo *) outerrel->fdw_private; - fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private; if (!fpinfo_o || !fpinfo_o->pushdown_safe || !fpinfo_i || !fpinfo_i->pushdown_safe) return false; diff --git a/mongo_fdw.h b/mongo_fdw.h index 486b0e5..28547da 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -155,6 +155,7 @@ #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" #endif /* Default values for option parameters */ @@ -197,7 +198,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 16; +static const uint32 ValidOptionCount = 18; #else static const uint32 ValidOptionCount = 6; #endif @@ -218,11 +219,15 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_CA_DIR, ForeignServerRelationId}, {OPTION_NAME_CRL_FILE, ForeignServerRelationId}, {OPTION_NAME_WEAK_CERT, ForeignServerRelationId}, + {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignServerRelationId}, #endif /* Foreign table options */ {OPTION_NAME_DATABASE, ForeignTableRelationId}, {OPTION_NAME_COLLECTION, ForeignTableRelationId}, +#ifdef META_DRIVER + {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignTableRelationId}, +#endif /* User mapping options */ {OPTION_NAME_USERNAME, UserMappingRelationId}, @@ -253,6 +258,7 @@ typedef struct MongoFdwOptions char *ca_dir; char *crl_file; bool weak_cert_validation; + bool enable_join_pushdown; #endif } MongoFdwOptions; @@ -337,6 +343,8 @@ typedef struct MongoFdwRelationInfo List *joinclauses; char *inner_relname; char *outer_relname; + + MongoFdwOptions *options; /* Options applicable for this relation */ } MongoFdwRelationInfo; /* diff --git a/option.c b/option.c index 04087d8..3982601 100644 --- a/option.c +++ b/option.c @@ -91,7 +91,8 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) } #ifdef META_DRIVER else if (strcmp(optionName, OPTION_NAME_SSL) == 0 || - strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0) + strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0 || + strcmp(optionName, OPTION_NAME_ENABLE_JOIN_PUSHDOWN) == 0) { /* These accept only boolean values */ (void) defGetBoolean(optionDef); @@ -156,8 +157,8 @@ mongo_get_options(Oid foreignTableId) foreignServer = GetForeignServer(foreignTable->serverid); mapping = GetUserMapping(GetUserId(), foreignTable->serverid); - optionList = mongo_list_concat(optionList, foreignTable->options); 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)); @@ -165,6 +166,7 @@ mongo_get_options(Oid foreignTableId) #ifdef META_DRIVER options->ssl = false; options->weak_cert_validation = false; + options->enable_join_pushdown = true; #endif /* Loop through the options */ @@ -203,6 +205,9 @@ mongo_get_options(Oid foreignTableId) 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 /* This is for continuation */ #endif diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 1a7a229..0d104de 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -28,6 +28,8 @@ 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'); 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); @@ -397,6 +399,54 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv 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; + 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; @@ -404,6 +454,7 @@ DELETE FROM f_test_tbl2 WHERE c1 = 50; 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 test_text; DROP FOREIGN TABLE test_varchar; DROP TABLE l_test_tbl1; From 801ad716d06ec7f4be08e0bb3f1fce109989cc6a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 16 Nov 2021 08:14:13 +0530 Subject: [PATCH 169/239] Add join push-down note in README. Vaibhav Dalvi --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index aaa7655..cc91d5f 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,17 @@ same mango 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 MySQL 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. + ### New MongoDB C Driver Support The third enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But From d0e030d22de169e5cad25f10ec32996ed884f83a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 16 Nov 2021 11:55:11 +0530 Subject: [PATCH 170/239] Make regression consistent on rhel8 by changing the host details. For invalid host and port testing, we were using the IP address "127.0.0.5" (with default Mongo port). However, on the rhel8 platform, we observed that the same IP is used as loopback IP for the localhost and thus was not testing the stuff we wanted to test and resulted in a regression. So to avoid failure on such platforms, change the address to "127.0.0.10" and port to 9999 instead. FDW-423, Rajkumar Raghuwanshi, reviewed and further fixed by Vaibhav Dalvi, tested by Varsha Mehtre. --- expected/connection_validation.out | 4 +++- expected/connection_validation_1.out | 12 +++++++----- sql/connection_validation.sql | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/expected/connection_validation.out b/expected/connection_validation.out index bbb5412..9cfcd63 100644 --- a/expected/connection_validation.out +++ b/expected/connection_validation.out @@ -27,7 +27,8 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- -- Alter one of the SERVER option -- Set wrong address for mongo_server -ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); +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 @@ -39,6 +40,7 @@ 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; diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out index 09d70ff..92678f0 100644 --- a/expected/connection_validation_1.out +++ b/expected/connection_validation_1.out @@ -27,18 +27,20 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- -- Alter one of the SERVER option -- Set wrong address for mongo_server -ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); +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 127.0.0.5:27017 +ERROR: could not connect to 127.0.0.10:9999 UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; -ERROR: could not connect to 127.0.0.5:27017 +ERROR: could not connect to 127.0.0.10:9999 DELETE FROM f_mongo_test WHERE a = 2; -ERROR: could not connect to 127.0.0.5:27017 +ERROR: could not connect to 127.0.0.10:9999 SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -ERROR: could not connect to 127.0.0.5:27017 +ERROR: could not connect to 127.0.0.10:9999 -- 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; diff --git a/sql/connection_validation.sql b/sql/connection_validation.sql index f0cee56..daacd20 100644 --- a/sql/connection_validation.sql +++ b/sql/connection_validation.sql @@ -27,7 +27,8 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- Alter one of the SERVER option -- Set wrong address for mongo_server -ALTER SERVER mongo_server OPTIONS (SET address '127.0.0.5'); +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; @@ -35,6 +36,7 @@ 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; From 562d2d2dc3d511b16d57c1cfd93b207a697efffe Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 18 Nov 2021 14:32:00 +0530 Subject: [PATCH 171/239] Stamp 5.3.0. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index 6448992..c61ab51 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50210 + 50300 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 129beac..e458415 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50210 + 50300 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 73c1362..d6e109c 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -60,9 +60,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.2.10 so number will be 50210 + * our version is 5.3.0 so number will be 50300 */ -#define CODE_VERSION 50210 +#define CODE_VERSION 50300 extern PGDLLEXPORT void _PG_init(void); const char *EscapeJsonString(const char *string); From 79908ec54ec3c299e78090e6ed616dd0e65d89fd Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 18 Jan 2022 18:25:06 +0530 Subject: [PATCH 172/239] Update EDB copyrights for 2022. FDW-467, Vaibhav Dalvi. --- Makefile | 2 +- Makefile.legacy | 2 +- Makefile.meta | 2 +- README.md | 2 +- autogen.sh | 2 +- connection.c | 2 +- deparse.c | 2 +- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 2 +- mongo_fdw.control | 2 +- mongo_fdw.h | 2 +- mongo_query.c | 2 +- mongo_query.h | 2 +- mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 2 +- option.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index 450b87e..097a098 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.legacy b/Makefile.legacy index 450b87e..097a098 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.meta b/Makefile.meta index ccbfbe1..c0b3160 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -1,6 +1,6 @@ # mongo_fdw/Makefile.meta # -# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index cc91d5f..45920bc 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ also support mongo_fdw. License ------- -Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. This program is free software: you can redistribute it and/or modify it diff --git a/autogen.sh b/autogen.sh index 0124b06..9c90288 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index 7de33c7..c53918f 100644 --- a/connection.c +++ b/connection.c @@ -4,7 +4,7 @@ * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/deparse.c b/deparse.c index 5b82f63..8157f36 100644 --- a/deparse.c +++ b/deparse.c @@ -4,7 +4,7 @@ * Query deparser for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index 6837a98..d5a9a51 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index e4603c9..4112f9b 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw.c b/mongo_fdw.c index d6e109c..cfcaed3 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index fd56d8a..e207efd 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index 28547da..e05de49 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index ddf5900..466e821 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index ff50d9b..d0edab7 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index abfa577..2e893aa 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index fc1d22f..37a537a 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index ff3c21e..ddb4abe 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index 3982601..937eb50 100644 --- a/option.c +++ b/option.c @@ -4,7 +4,7 @@ * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From 1ac1a25c677c6010d4b2f02a91ad459c2d140386 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 18 Jan 2022 19:22:26 +0530 Subject: [PATCH 173/239] Make Meta the default compilation driver instead of legacy. Earlier, the default was the legacy driver. However, Meta is more active and used prominently, thus switching default compilation to the Meta driver. To compile using legacy driver, copy the Makefile.legacy to Makefile. FDW-455, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Kashif Zeeshan. --- Makefile | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 097a098..967c226 100644 --- a/Makefile +++ b/Makefile @@ -11,28 +11,24 @@ MODULE_big = mongo_fdw # on another platform, change env_posix.os in MONGO_OBJS with the appropriate # environment object file. # -MONGO_DRIVER = mongo-c-driver -MONGO_PATH = $(MONGO_DRIVER)/src -MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ - $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os 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 -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) -OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(LIBJSON_OBJS) +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) -DMETA_DRIVER +SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) + +OBJS = connection.o option.o mongo_wrapper_meta.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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) -$(MONGO_DRIVER)/%.os: - $(MAKE) -C $(MONGO_DRIVER) $*.os -#$(LIBJSON)/json.o: -# $(MAKE) -C $(LIBJSON) - # # 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 From 87fa87a606600c028b7d58cd18fc747879d4a424 Mon Sep 17 00:00:00 2001 From: Jeevan Ladhe Date: Wed, 19 Jan 2022 16:35:55 +0530 Subject: [PATCH 174/239] README: Add instructions for driver installation and other cosmetic changes Improve README by adding detailed instructions for mongo-c and json-c library installation. Add instructions for both installation modes - using script and manual steps. Also, add description for SSL options. FDW-412, Vaibhav Dalvi, reviewed by Suraj Kharage and steps verified by Rajkumar Raghuwanshi. --- README.md | 227 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 157 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 45920bc..a8a1769 100644 --- a/README.md +++ b/README.md @@ -8,45 +8,171 @@ Postgres Advanced Server 9.6, 10, 11, 12, 13, and 14. Installation ------------ -The MongoDB FDW depends on the official MongoDB C Driver version 0.8 and -includes it as a git submodule. If you are cloning this repository for -the first time, be sure to pass the --recursive option to git clone in -order to initialize the driver submodule to a usable state. +To compile the [MongoDB][1] foreign data wrapper, 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. -If checked out this project before and for some reason your submodule -is not up-to-date, run git submodule update --init. +## 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. -When you type `cmake`, the C driver's source code also gets automatically -compiled and linked. +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. + +Build with [MongoDB][1]'s legacy branch driver + * autogen.sh --with-legacy + +Build [MongoDB][1]'s master branch driver + * autogen.sh --with-master + +The script autogen.sh will do all the necessary steps to build with legacy and +meta driver accordingly. + +## Steps for manual installation +### mongo-c +#### meta driver +1. Download and extract source code of mongoc driver for version `1.17.3` + + ```sh + wget https://github.com/mongodb/mongo-c-driver/releases/download/1.17.3/mongo-c-driver-1.17.3.tar.gz + tar xzf mongo-c-driver-1.17.3.tar.gz + rm -rf mongo-c-driver + mv mongo-c-driver-1.17.3 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][5]. + +#### Legacy driver +* Checkout, extract legacy branch + + ```sh + wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz + tar -zxf v0.8.tar.gz + rm -rf mongo-c-driver + mv mongo-c-driver-0.8 mongo-c-driver + ``` + +### json-c +1. Download and extract source code + + ```sh + wget https://github.com/json-c/json-c/archive/json-c-0.15-20200726.tar.gz + tar -xzf json-c-0.15-20200726.tar.gz + rm -rf json-c + mv json-c-json-c-0.15-20200726/ 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][6]. + +### How to compile against mongo-c Meta or Legacy driver? +To compile against legacy driver, 'Makefile.legacy' must be used and +'Makefile.meta' must be used to compile against the meta driver. For example, +this can be achieved by copying required Makefile as shown below: +For meta, + + cp Makefile.meta Makefile + +For legacy, + + cp Makefile.legacy Makefile + +The default compilation is with Meta driver. + +## 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 +``` + +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. -Note: Make sure you have permission to "/usr/local" -(default installation location) folder. 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: - ``` - $ export PATH=/usr/local/pgsql/bin/:$PATH + ```sh + export PATH=/usr/local/pgsql/bin/:$PATH ``` 2. Compile the code using make. - ``` - $ make USE_PGXS=1 + ```sh + make USE_PGXS=1 ``` -3. Finally install the foreign data wrapper. +3. Finally install the foreign data wrapper. - ``` - $ make USE_PGXS=1 install + ```sh + make USE_PGXS=1 install ``` 4. Running regression test. - ``` - $ make USE_PGXS=1 installcheck + ```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 @@ -100,51 +226,6 @@ In order to use MongoDB driver 1.17.0+, take the following steps: * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, then try running `ldconfig` -Compilation script ------------------ -Number of manual steps needs to be performed to compile and install -different type of MongoDB drivers and supported 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 permission to install the directory whether it is custom or default. - -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 -``` - -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. - -Build with [MongoDB][1]'s legacy branch driver - * autogen.sh --with-legacy - -Build [MongoDB][1]'s master branch driver - * autogen.sh --with-master - -The script will do all the necessary steps to build with legacy and -meta driver accordingly. - Usage ----- The following parameters can be set on a MongoDB foreign server object: @@ -165,12 +246,16 @@ The following options are only supported with meta driver: * `ssl`: false [default], true to enable ssl. See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to understand the options. - * `pem_file`: SSL option. - * `pem_pwd`: SSL option. - * `ca_file`: SSL option. - * `ca_dir`: SSL option. - * `crl_file`: SSL option. - * `weak_cert_validation`: SSL option, false [default]. + * `pem_file`: The .pem file that contains both the TLS/SSL certificate and + key. + * `pem_pwd`: The password to decrypt the certificate key file(i.e. pem_file) + * `ca_file`: The .pem file that contains the root certificate chain from the + Certificate Authority. + * `ca_dir`: The absolute path to the `ca_file`. + * `crl_file`: The .pem file that contains the Certificate Revocation List. + * `weak_cert_validation`: false [default], This is to enable or disable the + validation checks for TLS/SSL certificates and allows the use of invalid + certificates to connect if set to `true`. * `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. This option can also @@ -351,3 +436,5 @@ See the [`LICENSE`][4] file for full details. [2]: https://github.com/enterprisedb/mongo_fdw/issues/new [3]: CONTRIBUTING.md [4]: LICENSE +[5]: http://mongoc.org/libmongoc/1.17.3/installing.html#configuring-the-build +[6]: https://github.com/json-c/json-c/tree/json-c-0.15-20200726#build-instructions-- From e41cfde21c10c444e319e38bc320e73a4b106d04 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 16 Feb 2022 11:55:40 +0530 Subject: [PATCH 175/239] Add new server option use_remote_estimate. We were estimating the rows by calling ForeignTableDocumentCount() which is costly, and there is no way to avoid that. Thus add a new server-level option use_remote_estimate so that we will estimate the actual rows only when it is set to true. Default is false. FDW-464, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Kashif Zeeshan. --- README.md | 2 + expected/select_1.out | 18 ++-- expected/server_options.out | 57 ++++++++++--- expected/server_options_1.out | 83 ++++++++++++------ mongo_fdw.c | 156 +++++++++++++++++++--------------- mongo_fdw.h | 7 +- option.c | 14 ++- sql/server_options.sql | 35 +++++--- 8 files changed, 245 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index a8a1769..61283ed 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,8 @@ The following parameters can be set on a MongoDB foreign server object: * `address`: Address or hostname of the MongoDB server. Defaults to `127.0.0.1` * `port`: Port number of the MongoDB server. Defaults to `27017`. + * `use_remote_estimate`: Controls whether mongo_fdw uses exact rows from + remote collection to obtain cost estimates. Default is `false`. The following options are only supported with meta driver: diff --git a/expected/select_1.out b/expected/select_1.out index e458415..ca5b492 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1190,18 +1190,22 @@ SELECT d, d.c2, e.c1, e Sort Output: d.*, d.c2, e.c1, e.* Sort Key: d.*, e.c1 - -> Hash Right Join + -> Merge Left Join Output: d.*, d.c2, e.c1, e.* - Hash Cond: (e.c8 = d.c1) - -> Foreign Scan on public.f_test_tbl1 e - Output: e.c1, e.*, e.c8 - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Hash + Merge Cond: (d.c1 = e.c8) + -> Sort Output: d.*, d.c2, d.c1 + Sort Key: d.c1 -> Foreign Scan on public.f_test_tbl2 d Output: d.*, d.c2, d.c1 Foreign Namespace: mongo_fdw_regress.test_tbl2 -(14 rows) + -> Sort + Output: e.c1, e.*, e.c8 + Sort Key: e.c8 + -> Foreign Scan on public.f_test_tbl1 e + Output: e.c1, e.*, e.c8 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(18 rows) -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; diff --git a/expected/server_options.out b/expected/server_options.out index 7157f61..f0858da 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -116,18 +116,12 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; 0 | mongo_test collection (1 row) -DROP FOREIGN TABLE f_mongo_test; -DROP USER MAPPING FOR public SERVER mongo_server; -DROP SERVER mongo_server; --- Create server with authentication_database option +-- Alter server to add authentication_database option -- authentication_database options is not supported with legacy driver -- so below queries will fail when compiled with legacy driver. -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); -CREATE USER MAPPING FOR public SERVER mongo_server - OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); +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; @@ -142,6 +136,49 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; 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; diff --git a/expected/server_options_1.out b/expected/server_options_1.out index be67b0e..46191ba 100644 --- a/expected/server_options_1.out +++ b/expected/server_options_1.out @@ -91,10 +91,10 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- Set non-boolean value, should throw an error. ALTER SERVER mongo_server OPTIONS (ssl '1'); ERROR: invalid option "ssl" -HINT: Valid options in this context are: address, port. +HINT: Valid options in this context are: address, port, use_remote_estimate. ALTER SERVER mongo_server OPTIONS (ssl 'x'); ERROR: invalid option "ssl" -HINT: Valid options in this context are: address, port. +HINT: Valid options in this context are: address, port, use_remote_estimate. -- Check for default value i.e. false SELECT a, b FROM f_mongo_test ORDER BY 1, 2; a | b @@ -105,7 +105,7 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- Set 'true'. ALTER SERVER mongo_server OPTIONS (ssl 'true'); ERROR: invalid option "ssl" -HINT: Valid options in this context are: address, port. +HINT: Valid options in this context are: address, port, use_remote_estimate. -- 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; @@ -125,41 +125,72 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; 0 | mongo_test collection (1 row) -DROP FOREIGN TABLE f_mongo_test; -DROP USER MAPPING FOR public SERVER mongo_server; -DROP SERVER mongo_server; --- Create server with authentication_database option +-- Alter server to add authentication_database option -- authentication_database options is not supported with legacy driver -- so below queries will fail when compiled with legacy driver. -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); +ALTER SERVER mongo_server OPTIONS (ADD authentication_database 'NOT_EXIST_DB'); ERROR: invalid option "authentication_database" -HINT: Valid options in this context are: address, port. -CREATE USER MAPPING FOR public SERVER mongo_server - OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); -ERROR: server "mongo_server" does not exist -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); -ERROR: server "mongo_server" does not exist +HINT: Valid options in this context are: address, port, use_remote_estimate. +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: relation "f_mongo_test" does not exist -LINE 1: SELECT a, b FROM f_mongo_test ORDER BY 1, 2; - ^ +ERROR: could not connect to localhost:27017 +HINT: Mongo driver connection error: -- Now changed to valid authentication_database so select query should work. ALTER SERVER mongo_server OPTIONS (SET authentication_database 'mongo_fdw_regress'); -ERROR: server "mongo_server" does not exist +ERROR: option "authentication_database" not found SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -ERROR: relation "f_mongo_test" does not exist -LINE 1: SELECT a, b FROM f_mongo_test ORDER BY 1, 2; - ^ +ERROR: could not connect to localhost:27017 +HINT: Mongo driver connection error: +ALTER SERVER mongo_server + OPTIONS (DROP authentication_database); +ERROR: option "authentication_database" not found +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; -ERROR: foreign table "f_mongo_test" does not exist DROP USER MAPPING FOR public SERVER mongo_server; -ERROR: server "mongo_server" does not exist DROP SERVER mongo_server; -ERROR: server "mongo_server" does not exist DROP EXTENSION mongo_fdw; diff --git a/mongo_fdw.c b/mongo_fdw.c index cfcaed3..ceb6426 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -281,7 +281,6 @@ MongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { - double documentCount = ForeignTableDocumentCount(foreigntableid); RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); MongoFdwRelationInfo *fpinfo; MongoFdwOptions *options; @@ -316,26 +315,37 @@ MongoGetForeignRelSize(PlannerInfo *root, /* Base foreign tables need to be pushed down always. */ fpinfo->pushdown_safe = true; - if (documentCount > 0.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 rowSelectivity; + double documentCount = ForeignTableDocumentCount(foreigntableid); - /* - * 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); + 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."))); } - else - ereport(DEBUG1, - (errmsg("could not retrieve document count for collection"), - errhint("Falling back to default estimates in planning."))); - options = mongo_get_options(foreigntableid); relname = options->collectionName; database = options->svr_database; fpinfo->base_relname = relname; @@ -372,66 +382,78 @@ MongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { - 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; - double documentCount; - List *opExpressionList; + Path *foreignPath; + MongoFdwOptions *options; Cost startupCost = 0.0; Cost totalCost = 0.0; - Path *foreignPath; - MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) baserel->fdw_private; - documentCount = ForeignTableDocumentCount(foreigntableid); + /* Fetch options */ + options = mongo_get_options(foreigntableid); - if (documentCount > 0.0) + /* + * Retrieve exact document count for remote collection if asked, otherwise, + * use default estimate in planning. + */ + if (options->use_remote_estimate) { - /* - * 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); + double documentCount = ForeignTableDocumentCount(foreigntableid); - /* - * 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; + 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; - pageCount = (BlockNumber) rint(foreignTableSize / BLCKSZ); - totalDiskAccessCost = seq_page_cost * pageCount; + /* + * 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); - /* - * 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); + /* + * 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; - connectionCost = MONGO_CONNECTION_COST_MULTIPLIER * seq_page_cost; - startupCost = baserel->baserestrictcost.startup + connectionCost; - totalCost = startupCost + totalDiskAccessCost + totalCpuCost; + 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 - ereport(DEBUG1, - (errmsg("could not retrieve document count for collection"), - errhint("Falling back to default estimates in planning."))); /* Create a foreign path node */ foreignPath = (Path *) create_foreignscan_path(root, baserel, diff --git a/mongo_fdw.h b/mongo_fdw.h index e05de49..87d5395 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -144,6 +144,7 @@ #define OPTION_NAME_COLLECTION "collection" #define OPTION_NAME_USERNAME "username" #define OPTION_NAME_PASSWORD "password" +#define OPTION_NAME_USE_REMOTE_ESTIMATE "use_remote_estimate" #ifdef META_DRIVER #define OPTION_NAME_READ_PREFERENCE "read_preference" #define OPTION_NAME_AUTHENTICATION_DATABASE "authentication_database" @@ -198,15 +199,16 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 18; +static const uint32 ValidOptionCount = 19; #else -static const uint32 ValidOptionCount = 6; +static const uint32 ValidOptionCount = 7; #endif static const MongoValidOption ValidOptionArray[] = { /* Foreign server options */ {OPTION_NAME_ADDRESS, ForeignServerRelationId}, {OPTION_NAME_PORT, ForeignServerRelationId}, + {OPTION_NAME_USE_REMOTE_ESTIMATE, ForeignServerRelationId}, #ifdef META_DRIVER {OPTION_NAME_READ_PREFERENCE, ForeignServerRelationId}, @@ -247,6 +249,7 @@ typedef struct MongoFdwOptions char *collectionName; char *svr_username; char *svr_password; + bool use_remote_estimate; /* use remote estimate for rows */ #ifdef META_DRIVER char *readPreference; char *authenticationDatabase; diff --git a/option.c b/option.c index 937eb50..35d0597 100644 --- a/option.c +++ b/option.c @@ -89,15 +89,17 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) errmsg("port value \"%d\" is out of range for type %s", port, "unsigned short"))); } + else if (strcmp(optionName, OPTION_NAME_USE_REMOTE_ESTIMATE) == 0 #ifdef META_DRIVER - else if (strcmp(optionName, OPTION_NAME_SSL) == 0 || - strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0 || - strcmp(optionName, OPTION_NAME_ENABLE_JOIN_PUSHDOWN) == 0) + || strcmp(optionName, OPTION_NAME_WEAK_CERT) == 0 || + strcmp(optionName, OPTION_NAME_ENABLE_JOIN_PUSHDOWN) == 0 || + strcmp(optionName, OPTION_NAME_SSL) == 0 +#endif + ) { /* These accept only boolean values */ (void) defGetBoolean(optionDef); } -#endif } PG_RETURN_VOID(); @@ -163,6 +165,7 @@ mongo_get_options(Oid foreignTableId) options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); + options->use_remote_estimate = false; #ifdef META_DRIVER options->ssl = false; options->weak_cert_validation = false; @@ -228,6 +231,9 @@ mongo_get_options(Oid foreignTableId) 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 */ diff --git a/sql/server_options.sql b/sql/server_options.sql index f8082d5..01b53c1 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -78,19 +78,12 @@ ALTER SERVER mongo_server OPTIONS (SET ssl 'false'); -- Should now be successful. SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -DROP FOREIGN TABLE f_mongo_test; -DROP USER MAPPING FOR public SERVER mongo_server; -DROP SERVER mongo_server; - --- Create server with authentication_database option +-- Alter server to add authentication_database option -- authentication_database options is not supported with legacy driver -- so below queries will fail when compiled with legacy driver. -CREATE SERVER mongo_server FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address :MONGO_HOST, port :MONGO_PORT, authentication_database 'NOT_EXIST_DB'); -CREATE USER MAPPING FOR public SERVER mongo_server - OPTIONS (username :MONGO_USER_NAME, password :MONGO_PASS); -CREATE FOREIGN TABLE f_mongo_test (_id name, a int, b varchar) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'mongo_test'); +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; @@ -99,6 +92,26 @@ 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; From a46f90c5d6590f2391431af68ca34b66c4efc342 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 17 Feb 2022 11:31:20 +0530 Subject: [PATCH 176/239] Force type modifiers for numeric type. We have overlooked it. Fix it like other types. FDW-427, Vaibhav Dalvi, reviewed by Suraj Kharage, further revised by Jeevan Chalke, tested by Ajay Pal. --- data/mongo_test_data.js | 6 +++++ expected/select.out | 53 +++++++++++++++++++++++++++++++++++++++++ expected/select_1.out | 53 +++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 11 ++++++--- sql/select.sql | 28 ++++++++++++++++++++++ 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index 859d06c..c8bb065 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -17,6 +17,7 @@ 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([ @@ -73,3 +74,8 @@ db.test4.insertMany([ {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} +]); diff --git a/expected/select.out b/expected/select.out index c61ab51..7b04dc2 100644 --- a/expected/select.out +++ b/expected/select.out @@ -39,6 +39,8 @@ 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'); 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 @@ -1243,6 +1245,56 @@ SELECT d, d.c2, e.c1, e Foreign Namespace: mongo_fdw_regress.test_tbl1 (18 rows) +-- 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. -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1263,6 +1315,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/expected/select_1.out b/expected/select_1.out index ca5b492..6835b0f 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -39,6 +39,8 @@ 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'); 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 @@ -1207,6 +1209,56 @@ SELECT d, d.c2, e.c1, e Foreign Namespace: mongo_fdw_regress.test_tbl1 (18 rows) +-- 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. -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1227,6 +1279,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/mongo_fdw.c b/mongo_fdw.c index ceb6426..167b6f4 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -2051,10 +2051,15 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) case NUMERICOID: { float8 value = BsonIterDouble(bsonIterator); - Datum valueDatum = Float8GetDatum(value); + Datum valueDatum = DirectFunctionCall1(float8_numeric, + Float8GetDatum(value)); - /* Overlook type modifiers for numeric */ - columnValue = DirectFunctionCall1(float8_numeric, valueDatum); + /* + * 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: diff --git a/sql/select.sql b/sql/select.sql index 259af13..616c66d 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -38,6 +38,8 @@ 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'); SET datestyle TO ISO; @@ -287,6 +289,31 @@ 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; +-- 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -307,6 +334,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; From c6214819c1351fb0d2e257ef10932cb9f887522b Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 21 Feb 2022 16:59:28 +0530 Subject: [PATCH 177/239] Fix data type incompatibility between numeric types. If foreign table type is int and remote data is of type double, then the earlier code was returning zero, and vice-versa. Fix it by checking for the input type and converting that into an appropriate destination type. Reported on GitHub through issue #95 by Rick Otten (rotten). Fix verified by GitHub user mariosnic. FDW-418, Vaibhav Dalvi, reviewed by Suraj Kharage and me, tested by Kashif Zeeshan. --- data/mongo_test_data.js | 15 ++++++++++++++ expected/select.out | 44 +++++++++++++++++++++++++++++++++++++++++ expected/select_1.out | 44 +++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 4 ++++ mongo_wrapper.c | 32 +++++++++++++++++++++++++++++- mongo_wrapper_meta.c | 43 ++++++++++++++++++++++++++++++++++++---- sql/select.sql | 18 +++++++++++++++++ 7 files changed, 195 insertions(+), 5 deletions(-) diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index c8bb065..d2e3ece 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -12,6 +12,8 @@ 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.test1.drop(); db.test2.drop(); db.test3.drop(); @@ -79,3 +81,16 @@ 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} +]); diff --git a/expected/select.out b/expected/select.out index 7b04dc2..bd5efa7 100644 --- a/expected/select.out +++ b/expected/select.out @@ -41,6 +41,14 @@ 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'); 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 @@ -1295,6 +1303,38 @@ CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(3, 2)) 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 -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1316,6 +1356,10 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/expected/select_1.out b/expected/select_1.out index 6835b0f..c0c885e 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -41,6 +41,14 @@ 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'); 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 @@ -1259,6 +1267,38 @@ CREATE FOREIGN TABLE f_test5 (_id NAME, c1 NUMERIC(3, 2)) 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 +--------------- + 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 +---- + 25 + 25 + 25 + +(4 rows) + +SELECT a FROM f_test_tbl7 ORDER BY 1; +ERROR: value "9999999999" is out of range for type integer -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1280,6 +1320,10 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/mongo_fdw.c b/mongo_fdw.c index 167b6f4..1226865 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1874,6 +1874,10 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || bsonType == BSON_TYPE_DOUBLE) compatibleTypes = true; +#ifdef META_DRIVER + if (bsonType == BSON_TYPE_BOOL) + compatibleTypes = true; +#endif break; case BOOLOID: if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 2e893aa..b2effce 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -182,7 +182,37 @@ BsonIterSubObject(BSON_ITERATOR *it, BSON *b) int32_t BsonIterInt32(BSON_ITERATOR *it) { - return bson_iterator_int(it); + switch (bson_iterator_type(it)) + { + case BSON_DOUBLE: + { + double val = bson_iterator_double_raw(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_LONG: + { + int64 val = bson_iterator_long_raw(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_INT: + return bson_iterator_int_raw(it); + default: + return 0; + } } int64_t diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index ddb4abe..3522bb0 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -17,6 +17,8 @@ #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. @@ -345,25 +347,58 @@ BsonIterSubObject(BSON_ITERATOR *it, BSON *b) int32_t BsonIterInt32(BSON_ITERATOR *it) { - return bson_iter_int32(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_int64(it); + return bson_iter_as_int64(it); } double BsonIterDouble(BSON_ITERATOR *it) { - return bson_iter_double(it); + return bson_iter_as_double(it); } bool BsonIterBool(BSON_ITERATOR *it) { - return bson_iter_bool(it); + return bson_iter_as_bool(it); } const char * diff --git a/sql/select.sql b/sql/select.sql index 616c66d..34c9242 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -40,6 +40,14 @@ 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'); SET datestyle TO ISO; @@ -314,6 +322,12 @@ 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -335,6 +349,10 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; From a008692341cb3b4c17c349ff56ff27f61d020c00 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 21 Feb 2022 19:41:49 +0530 Subject: [PATCH 178/239] Rename a few function names to be more inline with the postgres_fdw. It becomes easy to follow and consistent across all the files. FDW-454, Vaibhav Dalvi, reviewed by Suraj Kharage. --- connection.c | 6 +- deparse.c | 54 ++--- mongo_fdw.c | 517 +++++++++++++++++++++---------------------- mongo_fdw.h | 2 +- mongo_query.c | 254 ++++++++++----------- mongo_query.h | 10 +- mongo_wrapper.c | 112 +++++----- mongo_wrapper.h | 118 +++++----- mongo_wrapper_meta.c | 184 +++++++-------- 9 files changed, 628 insertions(+), 629 deletions(-) diff --git a/connection.c b/connection.c index c53918f..124ad52 100644 --- a/connection.c +++ b/connection.c @@ -112,7 +112,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, { elog(DEBUG3, "disconnecting mongo_fdw connection %p for option changes to take effect", entry->conn); - MongoDisconnect(entry->conn); + mongoDisconnect(entry->conn); entry->conn = NULL; } @@ -122,7 +122,7 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, Oid umoid; #endif - entry->conn = MongoConnect(opt); + entry->conn = mongoConnect(opt); elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", entry->conn, opt->svr_address, opt->svr_port); @@ -198,7 +198,7 @@ mongo_cleanup_connection() continue; elog(DEBUG3, "disconnecting mongo_fdw connection %p", entry->conn); - MongoDisconnect(entry->conn); + mongoDisconnect(entry->conn); entry->conn = NULL; } } diff --git a/deparse.c b/deparse.c index 8157f36..0af9e19 100644 --- a/deparse.c +++ b/deparse.c @@ -268,8 +268,9 @@ mongo_append_expr(Expr *node, BSON *child_doc, pipeline_cxt *context) mongo_append_column_name((Var *) node, child_doc, context); break; case T_Const: - AppendConstantValue(child_doc, psprintf("%d", context->arrayIndex), - (Const *) node); + append_constant_value(child_doc, + psprintf("%d", context->arrayIndex), + (Const *) node); break; case T_OpExpr: mongo_append_op_expr((OpExpr *) node, child_doc, context); @@ -315,8 +316,8 @@ mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context) return; } - BsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex), &expr); - BsonAppendStartArray(&expr, op, &child); + bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex), &expr); + bsonAppendStartArray(&expr, op, &child); /* Save array index */ saved_array_index = context->arrayIndex; @@ -333,8 +334,8 @@ mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context) context->arrayIndex++; } - BsonAppendFinishArray(&expr, &child); - BsonAppendFinishObject(child_doc, &expr); + bsonAppendFinishArray(&expr, &child); + bsonAppendFinishObject(child_doc, &expr); /* Retain array index */ context->arrayIndex = saved_array_index; @@ -389,19 +390,20 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) 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), + 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), + bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex), &expr); /* Deparse operator name. */ - mongo_operator = MongoOperatorName(get_opname(node->opno)); + mongo_operator = mongo_operator_name(get_opname(node->opno)); - BsonAppendStartArray(&expr, mongo_operator, &child1); + bsonAppendStartArray(&expr, mongo_operator, &child1); /* Save array index */ saved_array_index = context->arrayIndex; @@ -427,11 +429,11 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) mongo_append_expr(lfirst(arg), &child1, context); } - BsonAppendFinishArray(&expr, &child1); + bsonAppendFinishArray(&expr, &child1); if (context->isBoolExpr) - BsonAppendFinishObject(&and_op, &expr); + bsonAppendFinishObject(&and_op, &expr); else - BsonAppendFinishObject(child_doc, &expr); + bsonAppendFinishObject(child_doc, &expr); /* * Add equality check for null values for columns involved in join-clauses. @@ -442,24 +444,24 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) continue; if (context->isBoolExpr) - BsonAppendStartObject(&and_op, psprintf("%d", and_index++), &expr); + bsonAppendStartObject(&and_op, psprintf("%d", and_index++), &expr); else - BsonAppendStartObject(child_doc, + bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex++), &expr); mongo_add_null_check(lfirst(arg), &expr, context); if (context->isBoolExpr) - BsonAppendFinishObject(&and_op, &expr); + bsonAppendFinishObject(&and_op, &expr); else - BsonAppendFinishObject(child_doc, &expr); + bsonAppendFinishObject(child_doc, &expr); } if (context->isBoolExpr == true) { - BsonAppendFinishArray(&and_obj, &and_op); - BsonAppendFinishObject(child_doc, &and_obj); + bsonAppendFinishArray(&and_obj, &and_op); + bsonAppendFinishObject(child_doc, &and_obj); } /* Retain array index */ @@ -497,7 +499,7 @@ mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context) else field = psprintf("$%s", columnInfo->colName); - BsonAppendUTF8(child_doc, psprintf("%d", context->arrayIndex), field); + bsonAppendUTF8(child_doc, psprintf("%d", context->arrayIndex), field); } /* @@ -528,8 +530,8 @@ mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context) else field = psprintf("$%s", columnInfo->colName); - BsonAppendStartArray(expr, "$ne", &ne_expr); - BsonAppendUTF8(&ne_expr, "0", field); - BsonAppendNull(&ne_expr, "1"); - BsonAppendFinishArray(expr, &ne_expr); + bsonAppendStartArray(expr, "$ne", &ne_expr); + bsonAppendUTF8(&ne_expr, "0", field); + bsonAppendNull(&ne_expr, "1"); + bsonAppendFinishArray(expr, &ne_expr); } diff --git a/mongo_fdw.c b/mongo_fdw.c index 1226865..4d47f13 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -65,80 +65,77 @@ PG_MODULE_MAGIC; #define CODE_VERSION 50300 extern PGDLLEXPORT void _PG_init(void); -const char *EscapeJsonString(const char *string); -void BsonToJsonString(StringInfo output, BSON_ITERATOR iter, bool isArray); - PG_FUNCTION_INFO_V1(mongo_fdw_handler); PG_FUNCTION_INFO_V1(mongo_fdw_version); /* FDW callback routines */ -static void MongoGetForeignRelSize(PlannerInfo *root, +static void mongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); -static void MongoGetForeignPaths(PlannerInfo *root, +static void mongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); -static ForeignScan *MongoGetForeignPlan(PlannerInfo *root, +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, +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, +static TupleTableSlot *mongoExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot); -static void MongoEndForeignModify(EState *estate, +static void mongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo); #if PG_VERSION_NUM >= 140000 -static void MongoAddForeignUpdateTargets(PlannerInfo *root, +static void mongoAddForeignUpdateTargets(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation); #else -static void MongoAddForeignUpdateTargets(Query *parsetree, +static void mongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation); #endif -static void MongoBeginForeignModify(ModifyTableState *mtstate, +static void mongoBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, int eflags); -static TupleTableSlot *MongoExecForeignInsert(EState *estate, +static TupleTableSlot *mongoExecForeignInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot); -static List *MongoPlanForeignModify(PlannerInfo *root, +static List *mongoPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index); -static void MongoExplainForeignModify(ModifyTableState *mtstate, +static void mongoExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, ExplainState *es); -static bool MongoAnalyzeForeignTable(Relation relation, +static bool mongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); #if PG_VERSION_NUM >= 110000 -static void MongoBeginForeignInsert(ModifyTableState *mtstate, +static void mongoBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo); -static void MongoEndForeignInsert(EState *estate, +static void mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo); #endif #ifdef META_DRIVER -static void MongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, +static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, @@ -148,28 +145,28 @@ static void MongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, /* * Helper functions */ -static double ForeignTableDocumentCount(Oid foreignTableId); -static HTAB *ColumnMappingHash(Oid foreignTableId, List *columnList, - List *colNameList, List *colIsInnerList, - bool isJoin); -static void FillTupleSlot(const BSON *bsonDocument, - const char *bsonDocumentKey, - HTAB *columnMappingHash, - Datum *columnValues, - bool *columnNulls, - bool isJoin); -static bool ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId); -static Datum ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId); -static Datum ColumnValue(BSON_ITERATOR *bsonIterator, - Oid columnTypeId, - int32 columnTypeMod); -static void MongoFreeScanState(MongoFdwModifyState *fmstate); -static int MongoAcquireSampleRows(Relation relation, - int errorLevel, - HeapTuple *sampleRows, - int targetRowCount, - double *totalRowCount, - double *totalDeadRowCount); +static double foreign_table_document_count(Oid foreignTableId); +static HTAB *column_mapping_hash(Oid foreignTableId, List *columnList, + List *colNameList, List *colIsInnerList, + bool isJoin); +static void fill_tuple_slot(const BSON *bsonDocument, + const char *bsonDocumentKey, + HTAB *columnMappingHash, + Datum *columnValues, + bool *columnNulls, + bool isJoin); +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); #ifdef META_DRIVER static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, @@ -178,6 +175,11 @@ static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinPathExtraData *extra); static void mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo); #endif +#ifndef META_DRIVER +static const char *escape_json_string(const char *string); +static void bson_to_json_string(StringInfo output, BSON_ITERATOR iter, + bool isArray); +#endif /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -220,39 +222,39 @@ 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; + 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; + 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; + fdwRoutine->ExplainForeignScan = mongoExplainForeignScan; + fdwRoutine->ExplainForeignModify = mongoExplainForeignModify; /* Support for ANALYZE */ - fdwRoutine->AnalyzeForeignTable = MongoAnalyzeForeignTable; + fdwRoutine->AnalyzeForeignTable = mongoAnalyzeForeignTable; #if PG_VERSION_NUM >= 110000 /* Partition routing and/or COPY from */ - fdwRoutine->BeginForeignInsert = MongoBeginForeignInsert; - fdwRoutine->EndForeignInsert = MongoEndForeignInsert; + fdwRoutine->BeginForeignInsert = mongoBeginForeignInsert; + fdwRoutine->EndForeignInsert = mongoEndForeignInsert; #endif #ifdef META_DRIVER /* Support function for join push-down */ - fdwRoutine->GetForeignJoinPaths = MongoGetForeignJoinPaths; + fdwRoutine->GetForeignJoinPaths = mongoGetForeignJoinPaths; #endif PG_RETURN_POINTER(fdwRoutine); @@ -277,7 +279,7 @@ mongo_fdw_exit(int code, Datum arg) * Obtains relation size estimates for mongo foreign table. */ static void -MongoGetForeignRelSize(PlannerInfo *root, +mongoGetForeignRelSize(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { @@ -324,7 +326,7 @@ MongoGetForeignRelSize(PlannerInfo *root, */ if (options->use_remote_estimate) { - double documentCount = ForeignTableDocumentCount(foreigntableid); + double documentCount = foreign_table_document_count(foreigntableid); if (documentCount > 0.0) { @@ -370,7 +372,7 @@ MongoGetForeignRelSize(PlannerInfo *root, } /* - * MongoGetForeignPaths + * 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 @@ -378,7 +380,7 @@ MongoGetForeignRelSize(PlannerInfo *root, * single table scan path. */ static void -MongoGetForeignPaths(PlannerInfo *root, +mongoGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid) { @@ -396,7 +398,7 @@ MongoGetForeignPaths(PlannerInfo *root, */ if (options->use_remote_estimate) { - double documentCount = ForeignTableDocumentCount(foreigntableid); + double documentCount = foreign_table_document_count(foreigntableid); if (documentCount > 0.0) { @@ -475,14 +477,14 @@ MongoGetForeignPaths(PlannerInfo *root, } /* - * MongoGetForeignPlan + * 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, +mongoGetForeignPlan(PlannerInfo *root, RelOptInfo *foreignrel, Oid foreigntableid, ForeignPath *best_path, @@ -770,11 +772,11 @@ MongoGetForeignPlan(PlannerInfo *root, } /* - * MongoExplainForeignScan + * mongoExplainForeignScan * Produces extra output for the Explain command. */ static void -MongoExplainForeignScan(ForeignScanState *node, ExplainState *es) +mongoExplainForeignScan(ForeignScanState *node, ExplainState *es) { ForeignScan *fsplan = (ForeignScan *) node->ss.ps.plan; RangeTblEntry *rte; @@ -812,7 +814,7 @@ MongoExplainForeignScan(ForeignScanState *node, ExplainState *es) } static void -MongoExplainForeignModify(ModifyTableState *mtstate, +mongoExplainForeignModify(ModifyTableState *mtstate, ResultRelInfo *rinfo, List *fdw_private, int subplan_index, @@ -835,7 +837,7 @@ MongoExplainForeignModify(ModifyTableState *mtstate, } /* - * MongoBeginForeignScan + * 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. @@ -844,7 +846,7 @@ MongoExplainForeignModify(ModifyTableState *mtstate, * column names to column index and type information. */ static void -MongoBeginForeignScan(ForeignScanState *node, int eflags) +mongoBeginForeignScan(ForeignScanState *node, int eflags) { MONGO_CONN *mongoConnection; List *columnList; @@ -909,8 +911,9 @@ MongoBeginForeignScan(ForeignScanState *node, int eflags) colIsInnerList = list_nth(fdw_private, mongoFdwPrivateColIsInnerList); } - columnMappingHash = ColumnMappingHash(rte->relid, columnList, colNameList, - colIsInnerList, fmstate->isJoinRel); + columnMappingHash = column_mapping_hash(rte->relid, columnList, + colNameList, colIsInnerList, + fmstate->isJoinRel); /* Create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; @@ -921,7 +924,7 @@ MongoBeginForeignScan(ForeignScanState *node, int eflags) } /* - * MongoIterateForeignScan + * mongoIterateForeignScan * Opens a Mongo cursor that uses the database name, collection name, and * the remote query to send to the server. * @@ -929,7 +932,7 @@ MongoBeginForeignScan(ForeignScanState *node, int eflags) * and stores the converted tuple into the ScanTupleSlot as a virtual tuple. */ static TupleTableSlot * -MongoIterateForeignScan(ForeignScanState *node) +mongoIterateForeignScan(ForeignScanState *node) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state; TupleTableSlot *tupleSlot = node->ss.ss_ScanTupleSlot; @@ -953,7 +956,7 @@ MongoIterateForeignScan(ForeignScanState *node) * performance on the MongoDB server-side, so we instead filter out * columns on our side. */ - queryDocument = QueryDocument(node); + queryDocument = mongo_query_document(node); /* * Decide input collection to the aggregation. In case of join, outer @@ -964,7 +967,7 @@ MongoIterateForeignScan(ForeignScanState *node) else collectionName = fmstate->options->collectionName; - mongoCursor = MongoCursorCreate(fmstate->mongoConnection, + mongoCursor = mongoCursorCreate(fmstate->mongoConnection, fmstate->options->svr_database, collectionName, queryDocument); @@ -985,13 +988,13 @@ MongoIterateForeignScan(ForeignScanState *node) memset(columnValues, 0, columnCount * sizeof(Datum)); memset(columnNulls, true, columnCount * sizeof(bool)); - if (MongoCursorNext(mongoCursor, NULL)) + if (mongoCursorNext(mongoCursor, NULL)) { - const BSON *bsonDocument = MongoCursorBson(mongoCursor); + const BSON *bsonDocument = mongoCursorBson(mongoCursor); const char *bsonDocumentKey = NULL; /* Top level document */ - FillTupleSlot(bsonDocument, bsonDocumentKey, columnMappingHash, - columnValues, columnNulls, fmstate->isJoinRel); + fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash, + columnValues, columnNulls, fmstate->isJoinRel); ExecStoreVirtualTuple(tupleSlot); } @@ -1000,12 +1003,12 @@ MongoIterateForeignScan(ForeignScanState *node) } /* - * MongoEndForeignScan + * mongoEndForeignScan * Finishes scanning the foreign table, closes the cursor and the * connection to MongoDB, and reclaims scan related resources. */ static void -MongoEndForeignScan(ForeignScanState *node) +mongoEndForeignScan(ForeignScanState *node) { MongoFdwModifyState *fmstate; @@ -1018,12 +1021,12 @@ MongoEndForeignScan(ForeignScanState *node) mongo_free_options(fmstate->options); fmstate->options = NULL; } - MongoFreeScanState(fmstate); + mongo_free_scan_state(fmstate); } } /* - * MongoReScanForeignScan + * mongoReScanForeignScan * Rescans the foreign table. * * Note that rescans in Mongo end up being notably more expensive than what the @@ -1031,20 +1034,20 @@ MongoEndForeignScan(ForeignScanState *node) * functionality. */ static void -MongoReScanForeignScan(ForeignScanState *node) +mongoReScanForeignScan(ForeignScanState *node) { MongoFdwModifyState *fmstate = (MongoFdwModifyState *) node->fdw_state; /* Close down the old cursor */ if (fmstate->mongoCursor) { - MongoCursorDestroy(fmstate->mongoCursor); + mongoCursorDestroy(fmstate->mongoCursor); fmstate->mongoCursor = NULL; } } static List * -MongoPlanForeignModify(PlannerInfo *root, +mongoPlanForeignModify(PlannerInfo *root, ModifyTable *plan, Index resultRelation, int subplan_index) @@ -1126,11 +1129,11 @@ MongoPlanForeignModify(PlannerInfo *root, } /* - * MongoBeginForeignModify + * mongoBeginForeignModify * Begin an insert/update/delete operation on a foreign table. */ static void -MongoBeginForeignModify(ModifyTableState *mtstate, +mongoBeginForeignModify(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo, List *fdw_private, int subplan_index, @@ -1225,11 +1228,11 @@ MongoBeginForeignModify(ModifyTableState *mtstate, } /* - * MongoExecForeignInsert + * mongoExecForeignInsert * Insert one row into a foreign table. */ static TupleTableSlot * -MongoExecForeignInsert(EState *estate, +mongoExecForeignInsert(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot) @@ -1242,7 +1245,7 @@ MongoExecForeignInsert(EState *estate, fmstate = (MongoFdwModifyState *) resultRelInfo->ri_FdwState; - bsonDoc = BsonCreate(); + bsonDoc = bsonCreate(); typoid = get_atttype(RelationGetRelid(resultRelInfo->ri_RelationDesc), 1); @@ -1283,46 +1286,46 @@ MongoExecForeignInsert(EState *estate, continue; #if PG_VERSION_NUM < 110000 - AppendMongoValue(bsonDoc, - slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, - value, - isnull, - slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); + append_mongo_value(bsonDoc, + slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, + value, + isnull, + slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); #else - AppendMongoValue(bsonDoc, - TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, - value, - isnull, - TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); + append_mongo_value(bsonDoc, + TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, + value, + isnull, + TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); #endif } } - BsonFinish(bsonDoc); + bsonFinish(bsonDoc); /* Now we are ready to insert tuple/document into MongoDB */ - MongoInsert(fmstate->mongoConnection, fmstate->options->svr_database, + mongoInsert(fmstate->mongoConnection, fmstate->options->svr_database, fmstate->options->collectionName, bsonDoc); - BsonDestroy(bsonDoc); + bsonDestroy(bsonDoc); return slot; } /* - * MongoAddForeignUpdateTargets + * 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, +mongoAddForeignUpdateTargets(PlannerInfo *root, Index rtindex, RangeTblEntry *target_rte, Relation target_relation) #else static void -MongoAddForeignUpdateTargets(Query *parsetree, +mongoAddForeignUpdateTargets(Query *parsetree, RangeTblEntry *target_rte, Relation target_relation) #endif @@ -1374,7 +1377,7 @@ MongoAddForeignUpdateTargets(Query *parsetree, } static TupleTableSlot * -MongoExecForeignUpdate(EState *estate, +mongoExecForeignUpdate(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot) @@ -1403,8 +1406,8 @@ MongoExecForeignUpdate(EState *estate, typoid = get_atttype(foreignTableId, 1); - document = BsonCreate(); - BsonAppendStartObject(document, "$set", &set); + document = bsonCreate(); + bsonAppendStartObject(document, "$set", &set); /* Get following parameters from slot */ if (slot != NULL && fmstate->target_attrs != NIL) @@ -1431,42 +1434,42 @@ MongoExecForeignUpdate(EState *estate, value = slot_getattr(slot, attnum, &isnull); #ifdef META_DRIVER - AppendMongoValue(&set, attr->attname.data, value, - isnull ? true : false, attr->atttypid); + append_mongo_value(&set, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #else - AppendMongoValue(document, attr->attname.data, value, - isnull ? true : false, attr->atttypid); + append_mongo_value(document, attr->attname.data, value, + isnull ? true : false, attr->atttypid); #endif } } - BsonAppendFinishObject(document, &set); - BsonFinish(document); + bsonAppendFinishObject(document, &set); + bsonFinish(document); - op = BsonCreate(); - if (!AppendMongoValue(op, columnName, datum, false, typoid)) + op = bsonCreate(); + if (!append_mongo_value(op, columnName, datum, false, typoid)) { - BsonDestroy(document); + bsonDestroy(document); return NULL; } - BsonFinish(op); + bsonFinish(op); /* We are ready to update the row into MongoDB */ - MongoUpdate(fmstate->mongoConnection, fmstate->options->svr_database, + mongoUpdate(fmstate->mongoConnection, fmstate->options->svr_database, fmstate->options->collectionName, op, document); - BsonDestroy(op); - BsonDestroy(document); + bsonDestroy(op); + bsonDestroy(document); /* Return NULL if nothing was updated on the remote end */ return slot; } /* - * MongoExecForeignDelete + * mongoExecForeignDelete * Delete one row from a foreign table */ static TupleTableSlot * -MongoExecForeignDelete(EState *estate, +mongoExecForeignDelete(EState *estate, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, TupleTableSlot *planSlot) @@ -1494,30 +1497,30 @@ MongoExecForeignDelete(EState *estate, typoid = get_atttype(foreignTableId, 1); - document = BsonCreate(); - if (!AppendMongoValue(document, columnName, datum, false, typoid)) + document = bsonCreate(); + if (!append_mongo_value(document, columnName, datum, false, typoid)) { - BsonDestroy(document); + bsonDestroy(document); return NULL; } - BsonFinish(document); + bsonFinish(document); /* Now we are ready to delete a single document from MongoDB */ - MongoDelete(fmstate->mongoConnection, fmstate->options->svr_database, + mongoDelete(fmstate->mongoConnection, fmstate->options->svr_database, fmstate->options->collectionName, document); - BsonDestroy(document); + bsonDestroy(document); /* Return NULL if nothing was updated on the remote end */ return slot; } /* - * MongoEndForeignModify + * mongoEndForeignModify * Finish an insert/update/delete operation on a foreign table */ static void -MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) +mongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) { MongoFdwModifyState *fmstate; @@ -1529,19 +1532,19 @@ MongoEndForeignModify(EState *estate, ResultRelInfo *resultRelInfo) mongo_free_options(fmstate->options); fmstate->options = NULL; } - MongoFreeScanState(fmstate); + mongo_free_scan_state(fmstate); pfree(fmstate); } } /* - * ForeignTableDocumentCount + * 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 -ForeignTableDocumentCount(Oid foreignTableId) +foreign_table_document_count(Oid foreignTableId) { MongoFdwOptions *options; MONGO_CONN *mongoConnection; @@ -1566,7 +1569,7 @@ ForeignTableDocumentCount(Oid foreignTableId) */ mongoConnection = mongo_get_connection(server, user, options); - documentCount = MongoAggregateCount(mongoConnection, options->svr_database, + documentCount = mongoAggregateCount(mongoConnection, options->svr_database, options->collectionName, emptyQuery); mongo_free_options(options); @@ -1575,15 +1578,15 @@ ForeignTableDocumentCount(Oid foreignTableId) } /* - * ColumnMappingHash + * 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 * -ColumnMappingHash(Oid foreignTableId, List *columnList, List *colNameList, - List *colIsInnerList, bool isJoin) +column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, + List *colIsInnerList, bool isJoin) { ListCell *columnCell; HTAB *columnMappingHash; @@ -1679,7 +1682,7 @@ ColumnMappingHash(Oid foreignTableId, List *columnList, List *colNameList, } /* - * FillTupleSlot + * 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 @@ -1689,19 +1692,16 @@ ColumnMappingHash(Oid foreignTableId, List *columnList, List *colNameList, * should always be passed as NULL. */ static void -FillTupleSlot(const BSON *bsonDocument, - const char *bsonDocumentKey, - HTAB *columnMappingHash, - Datum *columnValues, - bool *columnNulls, - bool isJoin) +fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, + HTAB *columnMappingHash, Datum *columnValues, + bool *columnNulls, bool isJoin) { ColumnMapping *columnMapping; bool handleFound = false; void *hashKey; BSON_ITERATOR bsonIterator = {NULL, 0}; - if (BsonIterInit(&bsonIterator, (BSON *) bsonDocument) == false) + if (bsonIterInit(&bsonIterator, (BSON *) bsonDocument) == false) elog(ERROR, "failed to initialize BSON iterator"); hashKey = "__doc"; @@ -1716,7 +1716,7 @@ FillTupleSlot(const BSON *bsonDocument, Datum columnValue; char *str; - str = BsonAsJson(bsonDocument); + str = bsonAsJson(bsonDocument); result = cstring_to_text_with_len(str, strlen(str)); lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); @@ -1768,10 +1768,10 @@ FillTupleSlot(const BSON *bsonDocument, return; } - while (BsonIterNext(&bsonIterator)) + while (bsonIterNext(&bsonIterator)) { - const char *bsonKey = BsonIterKey(&bsonIterator); - BSON_TYPE bsonType = BsonIterType(&bsonIterator); + const char *bsonKey = bsonIterKey(&bsonIterator); + BSON_TYPE bsonType = bsonIterType(&bsonIterator); Oid columnTypeId = InvalidOid; Oid columnArrayTypeId = InvalidOid; bool compatibleTypes = false; @@ -1815,9 +1815,9 @@ FillTupleSlot(const BSON *bsonDocument, { BSON subObject; - BsonIterSubObject(&bsonIterator, &subObject); - FillTupleSlot(&subObject, bsonFullKey, columnMappingHash, - columnValues, columnNulls, isJoin); + bsonIterSubObject(&bsonIterator, &subObject); + fill_tuple_slot(&subObject, bsonFullKey, columnMappingHash, + columnValues, columnNulls, isJoin); continue; } } @@ -1830,7 +1830,7 @@ FillTupleSlot(const BSON *bsonDocument, if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) compatibleTypes = true; else - compatibleTypes = ColumnTypesCompatible(bsonType, columnTypeId); + compatibleTypes = column_types_compatible(bsonType, columnTypeId); /* If types are incompatible, leave this column null */ if (!compatibleTypes) @@ -1840,17 +1840,17 @@ FillTupleSlot(const BSON *bsonDocument, /* Fill in corresponding column value and null flag */ if (OidIsValid(columnArrayTypeId)) - columnValues[attnum] = ColumnValueArray(&bsonIterator, - columnArrayTypeId); + columnValues[attnum] = column_value_array(&bsonIterator, + columnArrayTypeId); else - columnValues[attnum] = ColumnValue(&bsonIterator, columnTypeId, - columnMapping->columnTypeMod); + columnValues[attnum] = column_value(&bsonIterator, columnTypeId, + columnMapping->columnTypeMod); columnNulls[attnum] = false; } } /* - * ColumnTypesCompatible + * column_types_compatible * Checks if the given BSON type can be converted to the given PostgreSQL * type. * @@ -1858,7 +1858,7 @@ FillTupleSlot(const BSON *bsonDocument, * applied by BSON APIs. */ static bool -ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) +column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) { bool compatibleTypes = false; @@ -1942,7 +1942,7 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) } /* - * ColumnValueArray + * 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. @@ -1951,7 +1951,7 @@ ColumnTypesCompatible(BSON_TYPE bsonType, Oid columnTypeId) * returns the array datum. */ static Datum -ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) +column_value_array(BSON_ITERATOR *bsonIterator, Oid valueTypeId) { Datum *columnValueArray = palloc(INITIAL_ARRAY_CAPACITY * sizeof(Datum)); uint32 arrayCapacity = INITIAL_ARRAY_CAPACITY; @@ -1964,13 +1964,13 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) BSON_ITERATOR bsonSubIterator = {NULL, 0}; - BsonIterSubIter(bsonIterator, &bsonSubIterator); - while (BsonIterNext(&bsonSubIterator)) + bsonIterSubIter(bsonIterator, &bsonSubIterator); + while (bsonIterNext(&bsonSubIterator)) { - BSON_TYPE bsonType = BsonIterType(&bsonSubIterator); + BSON_TYPE bsonType = bsonIterType(&bsonSubIterator); bool compatibleTypes = false; - compatibleTypes = ColumnTypesCompatible(bsonType, valueTypeId); + compatibleTypes = column_types_compatible(bsonType, valueTypeId); if (bsonType == BSON_TYPE_NULL || !compatibleTypes) continue; @@ -1983,8 +1983,8 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) } /* Use default type modifier (0) to convert column value */ - columnValueArray[arrayIndex] = ColumnValue(&bsonSubIterator, - valueTypeId, 0); + columnValueArray[arrayIndex] = column_value(&bsonSubIterator, + valueTypeId, 0); arrayIndex++; } @@ -2005,13 +2005,14 @@ ColumnValueArray(BSON_ITERATOR *bsonIterator, Oid valueTypeId) } /* - * ColumnValue + * 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 -ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) +column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, + int32 columnTypeMod) { Datum columnValue; @@ -2019,42 +2020,42 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) { case INT2OID: { - int16 value = (int16) BsonIterInt32(bsonIterator); + int16 value = (int16) bsonIterInt32(bsonIterator); columnValue = Int16GetDatum(value); } break; case INT4OID: { - int32 value = BsonIterInt32(bsonIterator); + int32 value = bsonIterInt32(bsonIterator); columnValue = Int32GetDatum(value); } break; case INT8OID: { - int64 value = BsonIterInt64(bsonIterator); + int64 value = bsonIterInt64(bsonIterator); columnValue = Int64GetDatum(value); } break; case FLOAT4OID: { - float4 value = (float4) BsonIterDouble(bsonIterator); + float4 value = (float4) bsonIterDouble(bsonIterator); columnValue = Float4GetDatum(value); } break; case FLOAT8OID: { - float8 value = BsonIterDouble(bsonIterator); + float8 value = bsonIterDouble(bsonIterator); columnValue = Float8GetDatum(value); } break; case NUMERICOID: { - float8 value = BsonIterDouble(bsonIterator); + float8 value = bsonIterDouble(bsonIterator); Datum valueDatum = DirectFunctionCall1(float8_numeric, Float8GetDatum(value)); @@ -2068,14 +2069,14 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) break; case BOOLOID: { - bool value = BsonIterBool(bsonIterator); + bool value = bsonIterBool(bsonIterator); columnValue = BoolGetDatum(value); } break; case BPCHAROID: { - const char *value = BsonIterString(bsonIterator); + const char *value = bsonIterString(bsonIterator); Datum valueDatum = CStringGetDatum(value); columnValue = DirectFunctionCall3(bpcharin, valueDatum, @@ -2085,7 +2086,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) break; case VARCHAROID: { - const char *value = BsonIterString(bsonIterator); + const char *value = bsonIterString(bsonIterator); Datum valueDatum = CStringGetDatum(value); columnValue = DirectFunctionCall3(varcharin, valueDatum, @@ -2095,7 +2096,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) break; case TEXTOID: { - const char *value = BsonIterString(bsonIterator); + const char *value = bsonIterString(bsonIterator); columnValue = CStringGetTextDatum(value); } @@ -2105,7 +2106,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) char value[NAMEDATALEN]; Datum valueDatum = 0; - bson_oid_t *bsonObjectId = (bson_oid_t *) BsonIterOid(bsonIterator); + bson_oid_t *bsonObjectId = (bson_oid_t *) bsonIterOid(bsonIterator); bson_oid_to_string(bsonObjectId, value); @@ -2121,20 +2122,20 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) char *value; bytea *result; #ifdef META_DRIVER - switch (BsonIterType(bsonIterator)) + switch (bsonIterType(bsonIterator)) { case BSON_TYPE_OID: - value = (char *) BsonIterOid(bsonIterator); + value = (char *) bsonIterOid(bsonIterator); value_len = 12; break; default: - value = (char *) BsonIterBinData(bsonIterator, + value = (char *) bsonIterBinData(bsonIterator, (uint32_t *) &value_len); break; } #else - value_len = BsonIterBinLen(bsonIterator); - value = (char *) BsonIterBinData(bsonIterator); + value_len = bsonIterBinLen(bsonIterator); + value = (char *) bsonIterBinData(bsonIterator); #endif result = (bytea *) palloc(value_len + VARHDRSZ); memcpy(VARDATA(result), value, value_len); @@ -2144,7 +2145,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) break; case DATEOID: { - int64 valueMillis = BsonIterDate(bsonIterator); + int64 valueMillis = bsonIterDate(bsonIterator); int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; Datum timestampDatum = TimestampGetDatum(timestamp); @@ -2155,7 +2156,7 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) case TIMESTAMPOID: case TIMESTAMPTZOID: { - int64 valueMillis = BsonIterDate(bsonIterator); + int64 valueMillis = bsonIterDate(bsonIterator); int64 timestamp = (valueMillis * 1000L) - POSTGRES_TO_UNIX_EPOCH_USECS; /* Overlook type modifiers for timestamp */ @@ -2176,12 +2177,12 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) #ifdef META_DRIVER /* Convert BSON to JSON value */ - BsonToJsonStringValue(buffer, bsonIterator, + bsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); #else /* Convert BSON to JSON value */ - BsonToJsonString(buffer, *bsonIterator, - BSON_TYPE_ARRAY == type); + bson_to_json_string(buffer, *bsonIterator, + BSON_TYPE_ARRAY == type); #endif result = cstring_to_text_with_len(buffer->data, buffer->len); lex = makeJsonLexContext(result, false); @@ -2200,8 +2201,9 @@ ColumnValue(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int32 columnTypeMod) return columnValue; } -void -BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) +#ifndef META_DRIVER +static void +bson_to_json_string(StringInfo output, BSON_ITERATOR i, bool isArray) { const char *key; bool isFirstElement; @@ -2215,27 +2217,19 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) endSymbol = ']'; } -#ifndef META_DRIVER - { - const char *bsonData = bson_iterator_value(&i); - - bson_iterator_from_buffer(&i, bsonData); - } -#endif - appendStringInfoChar(output, beginSymbol); isFirstElement = true; - while (BsonIterNext(&i)) + while (bsonIterNext(&i)) { if (!isFirstElement) appendStringInfoChar(output, ','); - bsonType = BsonIterType(&i); + bsonType = bsonIterType(&i); if (bsonType == 0) break; - key = BsonIterKey(&i); + key = bsonIterKey(&i); if (!isArray) appendStringInfo(output, "\"%s\":", key); @@ -2243,33 +2237,33 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) switch (bsonType) { case BSON_TYPE_DOUBLE: - appendStringInfo(output, "%f", BsonIterDouble(&i)); + appendStringInfo(output, "%f", bsonIterDouble(&i)); break; case BSON_TYPE_UTF8: appendStringInfo(output, "\"%s\"", - EscapeJsonString(BsonIterString(&i))); + escape_json_string(bsonIterString(&i))); break; case BSON_TYPE_SYMBOL: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("\"symbol\" BSON type is deprecated and unsupported"), - errhint("Symbol: %s", BsonIterString(&i)))); + errhint("Symbol: %s", bsonIterString(&i)))); break; case BSON_TYPE_OID: { char oidhex[25]; - BsonOidToString(BsonIterOid(&i), oidhex); + bsonOidToString(bsonIterOid(&i), oidhex); appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); break; } case BSON_TYPE_BOOL: appendStringInfoString(output, - BsonIterBool(&i) ? "true" : "false"); + bsonIterBool(&i) ? "true" : "false"); break; case BSON_TYPE_DATE_TIME: appendStringInfo(output, "{\"$date\":%ld}", - (long int) BsonIterDate(&i)); + (long int) bsonIterDate(&i)); break; case BSON_TYPE_BINDATA: /* It's possible to encode the data with base64 here. */ @@ -2289,13 +2283,13 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for \"regex\" BSON type is not implemented"), - errhint("Regex: %s", BsonIterRegex(&i)))); + errhint("Regex: %s", bsonIterRegex(&i)))); break; case BSON_TYPE_CODE: ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), errmsg("support for \"code\" BSON type is not implemented"), - errhint("Code: %s", BsonIterCode(&i)))); + errhint("Code: %s", bsonIterCode(&i)))); break; case BSON_TYPE_CODEWSCOPE: ereport(ERROR, @@ -2303,10 +2297,10 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) errmsg("support for \"code\" with scope` BSON type is not implemented"))); break; case BSON_TYPE_INT32: - appendStringInfo(output, "%d", BsonIterInt32(&i)); + appendStringInfo(output, "%d", bsonIterInt32(&i)); break; case BSON_TYPE_INT64: - appendStringInfo(output, "%lu", (uint64_t) BsonIterInt64(&i)); + appendStringInfo(output, "%lu", (uint64_t) bsonIterInt64(&i)); break; case BSON_TYPE_TIMESTAMP: ereport(ERROR, @@ -2314,10 +2308,10 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) errmsg("internal `timestamp` BSON type is and unsupported"))); break; case BSON_TYPE_DOCUMENT: - BsonToJsonString(output, i, false); + bson_to_json_string(output, i, false); break; case BSON_TYPE_ARRAY: - BsonToJsonString(output, i, true); + bson_to_json_string(output, i, true); break; default: ereport(ERROR, @@ -2330,11 +2324,11 @@ BsonToJsonString(StringInfo output, BSON_ITERATOR i, bool isArray) } /* - * EscapeJsonString + * escape_json_string * Escapes a string for safe inclusion in JSON. */ -const char * -EscapeJsonString(const char *string) +static const char * +escape_json_string(const char *string) { StringInfo buffer; const char *ptr; @@ -2388,27 +2382,28 @@ EscapeJsonString(const char *string) len - segmentStartIdx); return buffer->data; } +#endif /* - * MongoFreeScanState + * mongo_free_scan_state * Closes the cursor and connection to MongoDB, and reclaims all Mongo * related resources allocated for the foreign scan. */ static void -MongoFreeScanState(MongoFdwModifyState *fmstate) +mongo_free_scan_state(MongoFdwModifyState *fmstate) { if (fmstate == NULL) return; if (fmstate->queryDocument) { - BsonDestroy(fmstate->queryDocument); + bsonDestroy(fmstate->queryDocument); fmstate->queryDocument = NULL; } if (fmstate->mongoCursor) { - MongoCursorDestroy(fmstate->mongoCursor); + mongoCursorDestroy(fmstate->mongoCursor); fmstate->mongoCursor = NULL; } @@ -2417,11 +2412,11 @@ MongoFreeScanState(MongoFdwModifyState *fmstate) } /* - * MongoAnalyzeForeignTable + * mongoAnalyzeForeignTable * Collects statistics for the given foreign table. */ static bool -MongoAnalyzeForeignTable(Relation relation, +mongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages) { @@ -2434,7 +2429,7 @@ MongoAnalyzeForeignTable(Relation relation, double foreignTableSize; foreignTableId = RelationGetRelid(relation); - documentCount = ForeignTableDocumentCount(foreignTableId); + documentCount = foreign_table_document_count(foreignTableId); if (documentCount > 0.0) { @@ -2459,13 +2454,13 @@ MongoAnalyzeForeignTable(Relation relation, errhint("Could not collect statistics about foreign table."))); (*totalpages) = pageCount; - (*func) = MongoAcquireSampleRows; + (*func) = mongo_acquire_sample_rows; return true; } /* - * MongoAcquireSampleRows + * mongo_acquire_sample_rows * Acquires a random sample of rows from the foreign table. * * Selected rows are returned in the caller allocated sampleRows array, @@ -2481,12 +2476,12 @@ MongoAnalyzeForeignTable(Relation relation, * index scans). */ static int -MongoAcquireSampleRows(Relation relation, - int errorLevel, - HeapTuple *sampleRows, - int targetRowCount, - double *totalRowCount, - double *totalDeadRowCount) +mongo_acquire_sample_rows(Relation relation, + int errorLevel, + HeapTuple *sampleRows, + int targetRowCount, + double *totalRowCount, + double *totalDeadRowCount) { MONGO_CONN *mongoConnection; int sampleRowCount = 0; @@ -2501,7 +2496,7 @@ MongoAcquireSampleRows(Relation relation, AttrNumber columnId; HTAB *columnMappingHash; MONGO_CURSOR *mongoCursor; - BSON *queryDocument = BsonCreate(); + BSON *queryDocument = bsonCreate(); List *columnList = NIL; char *relationName; MemoryContext oldContext = CurrentMemoryContext; @@ -2546,7 +2541,7 @@ MongoAcquireSampleRows(Relation relation, */ mongoConnection = mongo_get_connection(server, user, options); - if (!BsonFinish(queryDocument)) + if (!bsonFinish(queryDocument)) { #ifdef META_DRIVER ereport(ERROR, @@ -2560,10 +2555,10 @@ MongoAcquireSampleRows(Relation relation, } /* Create cursor for collection name and set query */ - mongoCursor = MongoCursorCreate(mongoConnection, options->svr_database, + mongoCursor = mongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); - columnMappingHash = ColumnMappingHash(foreignTableId, columnList, NIL, NIL, - false); + columnMappingHash = column_mapping_hash(foreignTableId, columnList, NIL, + NIL, false); /* * Use per-tuple memory context to prevent leak of memory used to read @@ -2596,17 +2591,17 @@ MongoAcquireSampleRows(Relation relation, memset(columnValues, 0, columnCount * sizeof(Datum)); memset(columnNulls, true, columnCount * sizeof(bool)); - if (MongoCursorNext(mongoCursor, NULL)) + if (mongoCursorNext(mongoCursor, NULL)) { - const BSON *bsonDocument = MongoCursorBson(mongoCursor); + const BSON *bsonDocument = mongoCursorBson(mongoCursor); const char *bsonDocumentKey = NULL; /* Top level document */ /* Fetch next tuple */ MemoryContextReset(tupleContext); MemoryContextSwitchTo(tupleContext); - FillTupleSlot(bsonDocument, bsonDocumentKey, - columnMappingHash, columnValues, columnNulls, false); + fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash, + columnValues, columnNulls, false); MemoryContextSwitchTo(oldContext); } @@ -2676,7 +2671,7 @@ MongoAcquireSampleRows(Relation relation, } /* Only clean up the query struct, but not its data */ - BsonDestroy(queryDocument); + bsonDestroy(queryDocument); /* Clean up */ MemoryContextDelete(tupleContext); @@ -2704,14 +2699,14 @@ mongo_fdw_version(PG_FUNCTION_ARGS) #if PG_VERSION_NUM >= 110000 /* - * MongoBeginForeignInsert + * 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, +mongoBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo) { ereport(ERROR, @@ -2720,14 +2715,14 @@ MongoBeginForeignInsert(ModifyTableState *mtstate, } /* - * MongoEndForeignInsert + * 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) +mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) { ereport(ERROR, (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), @@ -2737,11 +2732,11 @@ MongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) #ifdef META_DRIVER /* - * MongoGetForeignJoinPaths + * mongoGetForeignJoinPaths * Add possible ForeignPath to joinrel, if the join is safe to push down. */ static void -MongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, +mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra) { diff --git a/mongo_fdw.h b/mongo_fdw.h index 87d5395..0e25ca8 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -420,7 +420,7 @@ extern void mongo_cleanup_connection(void); extern void mongo_release_connection(MONGO_CONN *conn); /* Function declarations related to creating the mongo query */ -extern BSON *QueryDocument(ForeignScanState *scanStateNode); +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 ); diff --git a/mongo_query.c b/mongo_query.c index 466e821..fd23100 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -80,21 +80,21 @@ typedef struct foreign_loc_cxt } foreign_loc_cxt; /* Local functions forward declarations */ -static Expr *FindArgumentOfType(List *argumentList, NodeTag argumentType); -static List *EqualityOperatorList(List *operatorList); -static List *UniqueColumnList(List *operatorList); -static List *ColumnOperatorList(Var *column, List *operatorList); -static void AppendParamValue(BSON *queryDocument, const char *keyName, - Param *paramNode, - ForeignScanState *scanStateNode); +static Expr *find_argument_of_type(List *argumentList, NodeTag argumentType); +static List *equality_operator_list(List *operatorList); +static List *unique_column_list(List *operatorList); +static List *column_operator_list(Var *column, List *operatorList); +static void append_param_value(BSON *queryDocument, const char *keyName, + Param *paramNode, + ForeignScanState *scanStateNode); 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); #ifdef META_DRIVER -static HTAB *ColumnInfoHash(List *colname_list, List *colnum_list, - List *rti_list, List *isouter_list); +static HTAB *column_info_hash(List *colname_list, List *colnum_list, + List *rti_list, List *isouter_list); static void mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, List *colname_list, @@ -106,12 +106,12 @@ static void mongo_append_joinclauses_to_inner_pipeline(List *joinclause, #endif /* - * FindArgumentOfType + * find_argument_of_type * Walks over the given argument list, looks for an argument with the * given type, and returns the argument if it is found. */ static Expr * -FindArgumentOfType(List *argumentList, NodeTag argumentType) +find_argument_of_type(List *argumentList, NodeTag argumentType) { Expr *foundArgument = NULL; ListCell *argumentCell; @@ -135,7 +135,7 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) } /* - * QueryDocument + * mongo_query_document * Takes in the applicable operator expressions for relation and also the * join clauses for join relation and converts these expressions and join * clauses into equivalent queries in MongoDB. @@ -204,11 +204,11 @@ FindArgumentOfType(List *argumentList, NodeTag argumentType) * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". */ BSON * -QueryDocument(ForeignScanState *scanStateNode) +mongo_query_document(ForeignScanState *scanStateNode) { ForeignScan *fsplan = (ForeignScan *) scanStateNode->ss.ps.plan; - BSON *queryDocument = BsonCreate(); - BSON *filter = BsonCreate(); + BSON *queryDocument = bsonCreate(); + BSON *filter = bsonCreate(); List *PrivateList = fsplan->fdw_private; List *opExpressionList = list_nth(PrivateList, mongoFdwPrivateRemoteExprList); @@ -225,7 +225,7 @@ QueryDocument(ForeignScanState *scanStateNode) int jointype; /* Prepare array of stages */ - BsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); + bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); if (fmstate->isJoinRel) { @@ -256,8 +256,8 @@ QueryDocument(ForeignScanState *scanStateNode) natts == list_length(rti_list) && natts == list_length(isouter_list)); - columnInfoHash = ColumnInfoHash(colname_list, colnum_list, rti_list, - isouter_list); + columnInfoHash = column_info_hash(colname_list, colnum_list, rti_list, + isouter_list); } #endif @@ -285,7 +285,7 @@ QueryDocument(ForeignScanState *scanStateNode) * to insert the latter (<, >, <=, >=, <>) as separate sub-documents * into the BSON query object. */ - equalityOperatorList = EqualityOperatorList(opExpressionList); + equalityOperatorList = equality_operator_list(opExpressionList); comparisonOperatorList = list_difference(opExpressionList, equalityOperatorList); @@ -302,9 +302,9 @@ QueryDocument(ForeignScanState *scanStateNode) equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); argumentList = equalityOperator->args; - column = (Var *) FindArgumentOfType(argumentList, T_Var); - constant = (Const *) FindArgumentOfType(argumentList, T_Const); - paramNode = (Param *) FindArgumentOfType(argumentList, T_Param); + column = (Var *) find_argument_of_type(argumentList, T_Var); + constant = (Const *) find_argument_of_type(argumentList, T_Const); + paramNode = (Param *) find_argument_of_type(argumentList, T_Param); if (relationId != 0) { @@ -336,10 +336,10 @@ QueryDocument(ForeignScanState *scanStateNode) #endif if (constant != NULL) - AppendConstantValue(filter, columnName, constant); + append_constant_value(filter, columnName, constant); else - AppendParamValue(filter, columnName, paramNode, - scanStateNode); + append_param_value(filter, columnName, paramNode, + scanStateNode); } /* @@ -349,7 +349,7 @@ QueryDocument(ForeignScanState *scanStateNode) * define the upper and lower bound of a range, Mongo uses only one of * these expressions during an index search. */ - columnList = UniqueColumnList(comparisonOperatorList); + columnList = unique_column_list(comparisonOperatorList); /* Append comparison expressions, grouped by columns, to the query */ foreach(columnCell, columnList) @@ -391,11 +391,11 @@ QueryDocument(ForeignScanState *scanStateNode) #endif /* Find all expressions that correspond to the column */ - columnOperatorList = ColumnOperatorList(column, - comparisonOperatorList); + columnOperatorList = column_operator_list(column, + comparisonOperatorList); /* For comparison expressions, start a sub-document */ - BsonAppendStartObject(filter, columnName, &childDocument); + bsonAppendStartObject(filter, columnName, &childDocument); foreach(columnOperatorCell, columnOperatorList) { @@ -407,20 +407,21 @@ QueryDocument(ForeignScanState *scanStateNode) columnOperator = (OpExpr *) lfirst(columnOperatorCell); argumentList = columnOperator->args; - constant = (Const *) FindArgumentOfType(argumentList, T_Const); + constant = (Const *) find_argument_of_type(argumentList, + T_Const); operatorName = get_opname(columnOperator->opno); - mongoOperatorName = MongoOperatorName(operatorName); + mongoOperatorName = mongo_operator_name(operatorName); #ifdef META_DRIVER - AppendConstantValue(&childDocument, mongoOperatorName, - constant); + append_constant_value(&childDocument, mongoOperatorName, + constant); #else - AppendConstantValue(filter, mongoOperatorName, constant); + append_constant_value(filter, mongoOperatorName, constant); #endif } - BsonAppendFinishObject(filter, &childDocument); + bsonAppendFinishObject(filter, &childDocument); } } - if (!BsonFinish(filter)) + if (!bsonFinish(filter)) { #ifdef META_DRIVER ereport(ERROR, @@ -443,22 +444,22 @@ QueryDocument(ForeignScanState *scanStateNode) BSON outer_match_stage; BSON unwind_stage; BSON unwind; - BSON *inner_pipeline_doc = BsonCreate(); + BSON *inner_pipeline_doc = bsonCreate(); ListCell *cell1; ListCell *cell2; /* $lookup stage. This is to perform JOIN */ - BsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), &lookup_object); - BsonAppendStartObject(&lookup_object, "$lookup", &lookup); - BsonAppendUTF8(&lookup, "from", inner_relname); + 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); + bsonAppendStartObject(&lookup, "let", &let_exprs); forboth(cell1, colname_list, cell2, isouter_list) { char *colname = strVal(lfirst(cell1)); @@ -475,13 +476,13 @@ QueryDocument(ForeignScanState *scanStateNode) char *varname = psprintf("v_%s", colname); char *field = psprintf("$%s", colname); - BsonAppendUTF8(&let_exprs, varname, field); + bsonAppendUTF8(&let_exprs, varname, field); } } - BsonAppendFinishObject(&lookup, &let_exprs); /* End "let" */ + bsonAppendFinishObject(&lookup, &let_exprs); /* End "let" */ /* Form inner pipeline required in $lookup stage to execute $match */ - BsonAppendStartArray(inner_pipeline_doc, "pipeline", &inner_pipeline); + bsonAppendStartArray(inner_pipeline_doc, "pipeline", &inner_pipeline); if (joinclauses) { pipeline_cxt context; @@ -492,39 +493,39 @@ QueryDocument(ForeignScanState *scanStateNode) /* Form equivalent join qual clauses in MongoDB */ mongo_prepare_inner_pipeline(joinclauses, &inner_pipeline, colname_list, isouter_list, &context); - BsonAppendFinishArray(inner_pipeline_doc, &inner_pipeline); + 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); + bsonAppendUTF8(&lookup, "as", "Join_Result"); + bsonAppendFinishObject(&lookup_object, &lookup); + bsonAppendFinishObject(&root_pipeline, &lookup_object); /* $match stage. This is to add a filter */ - BsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), &outer_match_stage); - BsonAppendBson(&outer_match_stage, "$match", filter); - BsonAppendFinishObject(&root_pipeline, &outer_match_stage); + bsonAppendBson(&outer_match_stage, "$match", filter); + bsonAppendFinishObject(&root_pipeline, &outer_match_stage); /* * $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++), + bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), &unwind_stage); - BsonAppendStartObject(&unwind_stage, "$unwind", &unwind); - BsonAppendUTF8(&unwind, "path", "$Join_Result"); + bsonAppendStartObject(&unwind_stage, "$unwind", &unwind); + bsonAppendUTF8(&unwind, "path", "$Join_Result"); if (jointype == JOIN_INNER) - BsonAppendBool(&unwind, "preserveNullAndEmptyArrays", false); + bsonAppendBool(&unwind, "preserveNullAndEmptyArrays", false); else - BsonAppendBool(&unwind, "preserveNullAndEmptyArrays", true); - BsonAppendFinishObject(&unwind_stage, &unwind); - BsonAppendFinishObject(&root_pipeline, &unwind_stage); + bsonAppendBool(&unwind, "preserveNullAndEmptyArrays", true); + bsonAppendFinishObject(&unwind_stage, &unwind); + bsonAppendFinishObject(&root_pipeline, &unwind_stage); - if (!BsonFinish(queryDocument)) + if (!bsonFinish(queryDocument)) ereport(ERROR, (errmsg("could not create document for query"), errhint("BSON flags: %d", queryDocument->flags))); @@ -536,15 +537,15 @@ QueryDocument(ForeignScanState *scanStateNode) BSON match_stage; /* $match stage. This is to add a filter for the WHERE clause */ - BsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), &match_stage); - BsonAppendBson(&match_stage, "$match", filter); - BsonAppendFinishObject(&root_pipeline, &match_stage); + bsonAppendBson(&match_stage, "$match", filter); + bsonAppendFinishObject(&root_pipeline, &match_stage); } - BsonAppendFinishArray(queryDocument, &root_pipeline); + bsonAppendFinishArray(queryDocument, &root_pipeline); - if (!BsonFinish(queryDocument)) + if (!bsonFinish(queryDocument)) { ereport(ERROR, (errmsg("could not create document for query"), @@ -558,12 +559,12 @@ QueryDocument(ForeignScanState *scanStateNode) } /* - * MongoOperatorName + * mongo_operator_name * Takes in the given PostgreSQL comparison operator name, and returns its * equivalent in MongoDB. */ char * -MongoOperatorName(const char *operatorName) +mongo_operator_name(const char *operatorName) { const char *mongoOperatorName = NULL; const int32 nameCount = 14; @@ -598,12 +599,12 @@ MongoOperatorName(const char *operatorName) } /* - * EqualityOperatorList + * equality_operator_list * Finds the equality (=) operators in the given list, and returns these * operators in a new list. */ static List * -EqualityOperatorList(List *operatorList) +equality_operator_list(List *operatorList) { List *equalityOperatorList = NIL; ListCell *operatorCell; @@ -621,7 +622,7 @@ EqualityOperatorList(List *operatorList) } /* - * UniqueColumnList + * unique_column_list * Walks over the given operator list, and extracts the column argument in * each operator. * @@ -629,7 +630,7 @@ EqualityOperatorList(List *operatorList) * list. */ static List * -UniqueColumnList(List *operatorList) +unique_column_list(List *operatorList) { List *uniqueColumnList = NIL; ListCell *operatorCell; @@ -638,7 +639,8 @@ UniqueColumnList(List *operatorList) { OpExpr *operator = (OpExpr *) lfirst(operatorCell); List *argumentList = operator->args; - Var *column = (Var *) FindArgumentOfType(argumentList, T_Var); + Var *column = (Var *) find_argument_of_type(argumentList, + T_Var); /* List membership is determined via column's equal() function */ uniqueColumnList = list_append_unique(uniqueColumnList, column); @@ -648,12 +650,12 @@ UniqueColumnList(List *operatorList) } /* - * ColumnOperatorList + * column_operator_list * Finds all expressions that correspond to the given column, and returns * them in a new list. */ static List * -ColumnOperatorList(Var *column, List *operatorList) +column_operator_list(Var *column, List *operatorList) { List *columnOperatorList = NIL; ListCell *operatorCell; @@ -662,8 +664,8 @@ ColumnOperatorList(Var *column, List *operatorList) { OpExpr *operator = (OpExpr *) lfirst(operatorCell); List *argumentList = operator->args; - Var *foundColumn = (Var *) FindArgumentOfType(argumentList, - T_Var); + Var *foundColumn = (Var *) find_argument_of_type(argumentList, + T_Var); if (equal(column, foundColumn)) columnOperatorList = lappend(columnOperatorList, operator); @@ -673,8 +675,8 @@ ColumnOperatorList(Var *column, List *operatorList) } static void -AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, - ForeignScanState *scanStateNode) +append_param_value(BSON *queryDocument, const char *keyName, Param *paramNode, + ForeignScanState *scanStateNode) { ExprState *param_expr; Datum param_value; @@ -696,39 +698,39 @@ AppendParamValue(BSON *queryDocument, const char *keyName, Param *paramNode, param_value = ExecEvalExpr(param_expr, econtext, &isNull, NULL); #endif - AppendMongoValue(queryDocument, keyName, param_value, isNull, - paramNode->paramtype); + append_mongo_value(queryDocument, keyName, param_value, isNull, + paramNode->paramtype); } /* - * AppendConstantValue + * 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 -AppendConstantValue(BSON *queryDocument, const char *keyName, Const *constant) +append_constant_value(BSON *queryDocument, const char *keyName, Const *constant) { if (constant->constisnull) { - BsonAppendNull(queryDocument, keyName); + bsonAppendNull(queryDocument, keyName); return; } - AppendMongoValue(queryDocument, keyName, constant->constvalue, false, - constant->consttype); + append_mongo_value(queryDocument, keyName, constant->constvalue, false, + constant->consttype); } bool -AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, - bool isnull, Oid id) +append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, + bool isnull, Oid id) { bool status = false; if (isnull) { - status = BsonAppendNull(queryDocument, keyName); + status = bsonAppendNull(queryDocument, keyName); return status; } @@ -738,7 +740,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, { int16 valueInt = DatumGetInt16(value); - status = BsonAppendInt32(queryDocument, keyName, + status = bsonAppendInt32(queryDocument, keyName, (int) valueInt); } break; @@ -746,21 +748,21 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, { int32 valueInt = DatumGetInt32(value); - status = BsonAppendInt32(queryDocument, keyName, valueInt); + status = bsonAppendInt32(queryDocument, keyName, valueInt); } break; case INT8OID: { int64 valueLong = DatumGetInt64(value); - status = BsonAppendInt64(queryDocument, keyName, valueLong); + status = bsonAppendInt64(queryDocument, keyName, valueLong); } break; case FLOAT4OID: { float4 valueFloat = DatumGetFloat4(value); - status = BsonAppendDouble(queryDocument, keyName, + status = bsonAppendDouble(queryDocument, keyName, (double) valueFloat); } break; @@ -768,7 +770,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, { float8 valueFloat = DatumGetFloat8(value); - status = BsonAppendDouble(queryDocument, keyName, valueFloat); + status = bsonAppendDouble(queryDocument, keyName, valueFloat); } break; case NUMERICOID: @@ -777,14 +779,14 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, value); float8 valueFloat = DatumGetFloat8(valueDatum); - status = BsonAppendDouble(queryDocument, keyName, valueFloat); + status = bsonAppendDouble(queryDocument, keyName, valueFloat); } break; case BOOLOID: { bool valueBool = DatumGetBool(value); - status = BsonAppendBool(queryDocument, keyName, + status = bsonAppendBool(queryDocument, keyName, (int) valueBool); } break; @@ -798,7 +800,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - status = BsonAppendUTF8(queryDocument, keyName, outputString); + status = bsonAppendUTF8(queryDocument, keyName, outputString); } break; case BYTEAOID: @@ -823,13 +825,13 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, bson_oid_t oid; bson_oid_init_from_data(&oid, (const uint8_t *) data); - status = BsonAppendOid(queryDocument, keyName, &oid); + status = bsonAppendOid(queryDocument, keyName, &oid); } else - status = BsonAppendBinary(queryDocument, keyName, data, + status = bsonAppendBinary(queryDocument, keyName, data, len); #else - status = BsonAppendBinary(queryDocument, keyName, data, len); + status = bsonAppendBinary(queryDocument, keyName, data, len); #endif } break; @@ -843,8 +845,8 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - BsonOidFromString(&bsonObjectId, outputString); - status = BsonAppendOid(queryDocument, keyName, &bsonObjectId); + bsonOidFromString(&bsonObjectId, outputString); + status = bsonAppendOid(queryDocument, keyName, &bsonObjectId); } break; case DATEOID: @@ -855,7 +857,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - status = BsonAppendDate(queryDocument, keyName, + status = bsonAppendDate(queryDocument, keyName, valueMilliSecs); } break; @@ -866,7 +868,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, int64 valueMicroSecs = valueTimestamp + POSTGRES_TO_UNIX_EPOCH_USECS; int64 valueMilliSecs = valueMicroSecs / 1000; - status = BsonAppendDate(queryDocument, keyName, + status = bsonAppendDate(queryDocument, keyName, valueMilliSecs); } break; @@ -890,7 +892,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems); - BsonAppendStartArray(queryDocument, keyName, &childDocument); + bsonAppendStartArray(queryDocument, keyName, &childDocument); for (i = 0; i < num_elems; i++) { Datum valueDatum; @@ -903,14 +905,14 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, elem_values[i]); valueFloat = DatumGetFloat8(valueDatum); #ifdef META_DRIVER - status = BsonAppendDouble(&childDocument, keyName, + status = bsonAppendDouble(&childDocument, keyName, valueFloat); #else - status = BsonAppendDouble(queryDocument, keyName, + status = bsonAppendDouble(queryDocument, keyName, valueFloat); #endif } - BsonAppendFinishArray(queryDocument, &childDocument); + bsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); pfree(elem_nulls); } @@ -935,7 +937,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, &elem_values, &elem_nulls, &num_elems); - BsonAppendStartArray(queryDocument, keyName, &childDocument); + bsonAppendStartArray(queryDocument, keyName, &childDocument); for (i = 0; i < num_elems; i++) { char *valueString; @@ -949,10 +951,10 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, &typeVarLength); valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); - status = BsonAppendUTF8(queryDocument, keyName, + status = bsonAppendUTF8(queryDocument, keyName, valueString); } - BsonAppendFinishArray(queryDocument, &childDocument); + bsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); pfree(elem_nulls); } @@ -966,7 +968,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, getTypeOutputInfo(id, &outputFunctionId, &typeVarLength); outputString = OidOutputFunctionCall(outputFunctionId, value); - o = JsonTokenerPrase(outputString); + o = jsonTokenerPrase(outputString); if (o == NULL) { @@ -975,7 +977,7 @@ AppendMongoValue(BSON *queryDocument, const char *keyName, Datum value, break; } - status = JsonToBsonAppendElement(queryDocument, keyName, o); + status = jsonToBsonAppendElement(queryDocument, keyName, o); } break; default: @@ -1225,7 +1227,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, * operators for joinclause of join relation. */ if (!(strncmp(oname, EQUALITY_OPERATOR_NAME, NAMEDATALEN) == 0) && - (MongoOperatorName(oname) == NULL)) + (mongo_operator_name(oname) == NULL)) return false; /* @@ -1492,15 +1494,15 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) #ifdef META_DRIVER /* - * ColumnInfoHash + * 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 * -ColumnInfoHash(List *colname_list, List *colnum_list, List *rti_list, - List *isouter_list) +column_info_hash(List *colname_list, List *colnum_list, List *rti_list, + List *isouter_list) { HTAB *columnInfoHash; ColInfoHashKey key; @@ -1559,8 +1561,8 @@ ColumnInfoHash(List *colname_list, List *colnum_list, List *rti_list, * mongo_prepare_inner_pipeline * Form inner query pipeline syntax equivalent to postgresql join clauses. * - * From the example given on QueryDocument, the following part of MongoDB query - * formed by this function: + * From the example given on mongo_query_document, the following part of + * MongoDB query formed by this function: * * "pipeline": [ * { @@ -1583,20 +1585,20 @@ mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, List *colname_list, List *isouter_list, pipeline_cxt *context) { - BSON *and_query_doc = BsonCreate(); + BSON *and_query_doc = bsonCreate(); BSON match_object; BSON match_stage; BSON expr; BSON and_op; int inner_pipeline_index = 0; - BsonAppendStartObject(inner_pipeline, + bsonAppendStartObject(inner_pipeline, psprintf("%d", inner_pipeline_index++), &match_object); - BsonAppendStartObject(&match_object, "$match", &match_stage); - BsonAppendStartObject(&match_stage, "$expr", &expr); + bsonAppendStartObject(&match_object, "$match", &match_stage); + bsonAppendStartObject(&match_stage, "$expr", &expr); - BsonAppendStartArray(and_query_doc, "$and", &and_op); + bsonAppendStartArray(and_query_doc, "$and", &and_op); context->arrayIndex = 0; @@ -1606,10 +1608,10 @@ mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, /* 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); - BsonAppendFinishObject(&match_object, &match_stage); - BsonAppendFinishObject(inner_pipeline, &match_object); + bsonAppendFinishArray(and_query_doc, &and_op); + bsonAppendFinishObject(&match_stage, &expr); + bsonAppendFinishObject(&match_object, &match_stage); + bsonAppendFinishObject(inner_pipeline, &match_object); } /* diff --git a/mongo_query.h b/mongo_query.h index d0edab7..daeb0e3 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -105,13 +105,13 @@ enum mongoFdwScanPrivateIndex }; /* Function to be used in mongo_fdw.c */ -extern bool AppendMongoValue(BSON *queryDocument, const char *keyName, - Datum value, bool isnull, Oid id); +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 *MongoOperatorName(const char *operatorName); -extern void AppendConstantValue(BSON *queryDocument, const char *keyName, - Const *constant); +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); diff --git a/mongo_wrapper.c b/mongo_wrapper.c index b2effce..e3e2a5e 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -24,7 +24,7 @@ #define QUAL_STRING_LEN 512 MONGO_CONN * -MongoConnect(MongoFdwOptions *opt) +mongoConnect(MongoFdwOptions *opt) { MONGO_CONN *conn; @@ -63,14 +63,14 @@ MongoConnect(MongoFdwOptions *opt) } void -MongoDisconnect(MONGO_CONN *conn) +mongoDisconnect(MONGO_CONN *conn) { mongo_destroy(conn); mongo_dealloc(conn); } bool -MongoInsert(MONGO_CONN *conn, char *database, char *collection, bson *b) +mongoInsert(MONGO_CONN *conn, char *database, char *collection, bson *b) { char qual[QUAL_STRING_LEN]; @@ -84,7 +84,7 @@ MongoInsert(MONGO_CONN *conn, char *database, char *collection, bson *b) } bool -MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, +mongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) { char qual[QUAL_STRING_LEN]; @@ -99,7 +99,7 @@ MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, } bool -MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) +mongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { char qual[QUAL_STRING_LEN]; @@ -113,7 +113,7 @@ MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) } MONGO_CURSOR * -MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) +mongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) { MONGO_CURSOR *c; char qual[QUAL_STRING_LEN]; @@ -127,26 +127,26 @@ MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) } const bson * -MongoCursorBson(MONGO_CURSOR *c) +mongoCursorBson(MONGO_CURSOR *c) { return mongo_cursor_bson(c); } bool -MongoCursorNext(MONGO_CURSOR *c, BSON *b) +mongoCursorNext(MONGO_CURSOR *c, BSON *b) { return (mongo_cursor_next(c) == MONGO_OK); } void -MongoCursorDestroy(MONGO_CURSOR *c) +mongoCursorDestroy(MONGO_CURSOR *c) { mongo_cursor_destroy(c); mongo_cursor_dealloc(c); } BSON * -BsonCreate() +bsonCreate() { BSON *doc = NULL; @@ -157,14 +157,14 @@ BsonCreate() } void -BsonDestroy(BSON *b) +bsonDestroy(BSON *b) { bson_destroy(b); bson_dealloc(b); } bool -BsonIterInit(BSON_ITERATOR *it, BSON *b) +bsonIterInit(BSON_ITERATOR *it, BSON *b) { bson_iterator_init(it, b); @@ -172,7 +172,7 @@ BsonIterInit(BSON_ITERATOR *it, BSON *b) } bool -BsonIterSubObject(BSON_ITERATOR *it, BSON *b) +bsonIterSubObject(BSON_ITERATOR *it, BSON *b) { bson_iterator_subobject_init(it, b, 0); @@ -180,7 +180,7 @@ BsonIterSubObject(BSON_ITERATOR *it, BSON *b) } int32_t -BsonIterInt32(BSON_ITERATOR *it) +bsonIterInt32(BSON_ITERATOR *it) { switch (bson_iterator_type(it)) { @@ -216,67 +216,67 @@ BsonIterInt32(BSON_ITERATOR *it) } int64_t -BsonIterInt64(BSON_ITERATOR *it) +bsonIterInt64(BSON_ITERATOR *it) { return bson_iterator_long(it); } double -BsonIterDouble(BSON_ITERATOR *it) +bsonIterDouble(BSON_ITERATOR *it) { return bson_iterator_double(it); } bool -BsonIterBool(BSON_ITERATOR *it) +bsonIterBool(BSON_ITERATOR *it) { return bson_iterator_bool(it); } const char * -BsonIterString(BSON_ITERATOR *it) +bsonIterString(BSON_ITERATOR *it) { return bson_iterator_string(it); } const char * -BsonIterBinData(BSON_ITERATOR *it) +bsonIterBinData(BSON_ITERATOR *it) { return bson_iterator_bin_data(it); } int -BsonIterBinLen(BSON_ITERATOR *it) +bsonIterBinLen(BSON_ITERATOR *it) { return bson_iterator_bin_len(it); } bson_oid_t * -BsonIterOid(BSON_ITERATOR *it) +bsonIterOid(BSON_ITERATOR *it) { return bson_iterator_oid(it); } time_t -BsonIterDate(BSON_ITERATOR *it) +bsonIterDate(BSON_ITERATOR *it) { return bson_iterator_date(it); } int -BsonIterType(BSON_ITERATOR *it) +bsonIterType(BSON_ITERATOR *it) { return bson_iterator_type(it); } int -BsonIterNext(BSON_ITERATOR *it) +bsonIterNext(BSON_ITERATOR *it) { return bson_iterator_next(it); } bool -BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) +bsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { bson_iterator_subiterator(it, sub); @@ -284,108 +284,108 @@ BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) } void -BsonOidFromString(bson_oid_t *o, char *str) +bsonOidFromString(bson_oid_t *o, char *str) { bson_oid_from_string(o, str); } bool -BsonAppendOid(BSON *b, const char *key, bson_oid_t *v) +bsonAppendOid(BSON *b, const char *key, bson_oid_t *v) { return (bson_append_oid(b, key, v) == MONGO_OK); } bool -BsonAppendBool(BSON *b, const char *key, bool v) +bsonAppendBool(BSON *b, const char *key, bool v) { return (bson_append_int(b, key, v) == MONGO_OK); } bool -BsonAppendNull(BSON *b, const char *key) +bsonAppendNull(BSON *b, const char *key) { return (bson_append_null(b, key) == MONGO_OK); } bool -BsonAppendInt32(BSON *b, const char *key, int v) +bsonAppendInt32(BSON *b, const char *key, int v) { return (bson_append_int(b, key, v) == MONGO_OK); } bool -BsonAppendInt64(BSON *b, const char *key, int64_t v) +bsonAppendInt64(BSON *b, const char *key, int64_t v) { return (bson_append_long(b, key, v) == MONGO_OK); } bool -BsonAppendDouble(BSON *b, const char *key, double v) +bsonAppendDouble(BSON *b, const char *key, double v) { return (bson_append_double(b, key, v) == MONGO_OK); } bool -BsonAppendUTF8(BSON *b, const char *key, char *v) +bsonAppendUTF8(BSON *b, const char *key, char *v) { return (bson_append_string(b, key, v) == MONGO_OK); } bool -BsonAppendBinary(BSON *b, const char *key, char *v, size_t len) +bsonAppendBinary(BSON *b, const char *key, char *v, size_t len) { return (bson_append_binary(b, key, BSON_BIN_BINARY, v, len) == MONGO_OK); } bool -BsonAppendDate(BSON *b, const char *key, time_t v) +bsonAppendDate(BSON *b, const char *key, time_t v) { return (bson_append_date(b, key, v) == MONGO_OK); } bool -BsonAppendStartArray(BSON *b, const char *key, BSON *c) +bsonAppendStartArray(BSON *b, const char *key, BSON *c) { return (bson_append_start_array(b, key) == MONGO_OK); } bool -BsonAppendFinishArray(BSON *b, BSON *c) +bsonAppendFinishArray(BSON *b, BSON *c) { return (bson_append_finish_array(b) == MONGO_OK); } bool -BsonAppendStartObject(BSON *b, char *key, BSON *r) +bsonAppendStartObject(BSON *b, char *key, BSON *r) { return (bson_append_start_object(b, key) == MONGO_OK); } bool -BsonAppendFinishObject(BSON *b, BSON *r) +bsonAppendFinishObject(BSON *b, BSON *r) { return (bson_append_finish_object(b) == MONGO_OK); } bool -BsonAppendBson(BSON *b, char *key, BSON *c) +bsonAppendBson(BSON *b, char *key, BSON *c) { return (bson_append_bson(b, key, c) == MONGO_OK); } bool -BsonFinish(BSON *b) +bsonFinish(BSON *b) { return (bson_finish(b) == MONGO_OK); } json_object * -JsonTokenerPrase(char *s) +jsonTokenerPrase(char *s) { return json_tokener_parse(s); } bool -JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) +jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { bool status = true; @@ -420,15 +420,15 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - BsonOidFromString(&bsonObjectId, + bsonOidFromString(&bsonObjectId, (char *) json_object_get_string(joj)); - status = BsonAppendOid(bb, k, &bsonObjectId); + 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)); + status = bsonAppendDate(bb, k, json_object_get_int64(joj)); break; } @@ -436,7 +436,7 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { json_object_object_foreach(v, kk, vv) - JsonToBsonAppendElement(bb, kk, vv); + jsonToBsonAppendElement(bb, kk, vv); } bson_append_finish_object(bb); } @@ -450,7 +450,7 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) for (i = 0; i < json_object_array_length(v); i++) { sprintf(buf, "%d", i); - JsonToBsonAppendElement(bb, buf, + jsonToBsonAppendElement(bb, buf, json_object_array_get_idx(v, i)); } bson_append_finish_object(bb); @@ -467,50 +467,50 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) } double -MongoAggregateCount(MONGO_CONN *conn, const char *database, +mongoAggregateCount(MONGO_CONN *conn, const char *database, const char *collection, const BSON *b) { return mongo_count(conn, database, collection, b); } void -BsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer) +bsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer) { bson_iterator_from_buffer(i, buffer); } void -BsonOidToString(const bson_oid_t *o, char str[25]) +bsonOidToString(const bson_oid_t *o, char str[25]) { bson_oid_to_string(o, str); } const char * -BsonIterCode(BSON_ITERATOR *i) +bsonIterCode(BSON_ITERATOR *i) { return bson_iterator_code(i); } const char * -BsonIterRegex(BSON_ITERATOR *i) +bsonIterRegex(BSON_ITERATOR *i) { return bson_iterator_regex(i); } const char * -BsonIterKey(BSON_ITERATOR *i) +bsonIterKey(BSON_ITERATOR *i) { return bson_iterator_key(i); } const char * -BsonIterValue(BSON_ITERATOR *i) +bsonIterValue(BSON_ITERATOR *i) { return bson_iterator_value(i); } char * -BsonAsJson(const BSON *bsonDocument) +bsonAsJson(const BSON *bsonDocument) { ereport(ERROR, (errmsg("full document retrival only available in MongoC meta driver"))); diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 37a537a..d978527 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -27,87 +27,87 @@ #include #ifdef META_DRIVER -MONGO_CONN *MongoConnect(MongoFdwOptions *opt); +MONGO_CONN *mongoConnect(MongoFdwOptions *opt); #else -MONGO_CONN *MongoConnect(MongoFdwOptions *opt); +MONGO_CONN *mongoConnect(MongoFdwOptions *opt); #endif -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, +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, +bool mongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b); -MONGO_CURSOR *MongoCursorCreate(MONGO_CONN *conn, char *database, +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 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); +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); +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); #ifdef META_DRIVER -const char *BsonIterBinData(BSON_ITERATOR *it, uint32_t *len); +const char *bsonIterBinData(BSON_ITERATOR *it, uint32_t *len); #else -const char *BsonIterBinData(BSON_ITERATOR *it); -int BsonIterBinLen(BSON_ITERATOR *it); +const char *bsonIterBinData(BSON_ITERATOR *it); +int bsonIterBinLen(BSON_ITERATOR *it); #endif #ifdef META_DRIVER -const bson_oid_t *BsonIterOid(BSON_ITERATOR *it); +const bson_oid_t *bsonIterOid(BSON_ITERATOR *it); #else -bson_oid_t *BsonIterOid(BSON_ITERATOR *it); +bson_oid_t *bsonIterOid(BSON_ITERATOR *it); #endif -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); +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); #ifdef META_DRIVER -const bson_value_t *BsonIterValue(BSON_ITERATOR *i); +const bson_value_t *bsonIterValue(BSON_ITERATOR *i); #else -const char *BsonIterValue(BSON_ITERATOR *i); +const char *bsonIterValue(BSON_ITERATOR *i); #endif -void BsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer); +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 BsonFinish(BSON *b); -bool JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v); -json_object *JsonTokenerPrase(char *s); +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 bsonFinish(BSON *b); +bool jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v); +json_object *jsonTokenerPrase(char *s); -char *BsonAsJson(const BSON *bsonDocument); +char *bsonAsJson(const BSON *bsonDocument); -void BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, +void bsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray); -void DumpJsonObject(StringInfo output, BSON_ITERATOR *iter); -void DumpJsonArray(StringInfo output, BSON_ITERATOR *iter); +void dumpJsonObject(StringInfo output, BSON_ITERATOR *iter); +void dumpJsonArray(StringInfo output, BSON_ITERATOR *iter); #endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 3522bb0..41e96b7 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -20,11 +20,11 @@ #define ITER_TYPE(i) ((bson_type_t) * ((i)->raw + (i)->type)) /* - * MongoConnect + * mongoConnect * Connect to MongoDB server using Host/ip and Port number. */ MONGO_CONN * -MongoConnect(MongoFdwOptions *opt) +mongoConnect(MongoFdwOptions *opt) { MONGO_CONN *client; char *uri; @@ -157,22 +157,22 @@ MongoConnect(MongoFdwOptions *opt) } /* - * MongoDisconnect + * mongoDisconnect * Disconnect from MongoDB server. */ void -MongoDisconnect(MONGO_CONN *conn) +mongoDisconnect(MONGO_CONN *conn) { if (conn) mongoc_client_destroy(conn); } /* - * MongoInsert + * mongoInsert * Insert a document 'b' into MongoDB. */ bool -MongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) +mongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) { mongoc_collection_t *c; bson_error_t error; @@ -191,11 +191,11 @@ MongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) } /* - * MongoUpdate + * mongoUpdate * Update a document 'b' into MongoDB. */ bool -MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, +mongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, BSON *op) { mongoc_collection_t *c; @@ -215,11 +215,11 @@ MongoUpdate(MONGO_CONN *conn, char *database, char *collection, BSON *b, } /* - * MongoDelete + * mongoDelete * Delete MongoDB's document. */ bool -MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) +mongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { mongoc_collection_t *c; bson_error_t error; @@ -239,12 +239,12 @@ MongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) } /* - * MongoCursorCreate + * 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) +mongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) { mongoc_collection_t *c; MONGO_CURSOR *cur; @@ -264,45 +264,45 @@ MongoCursorCreate(MONGO_CONN *conn, char *database, char *collection, BSON *q) } /* - * MongoCursorDestroy - * Destroy cursor created by calling MongoCursorCreate function. + * mongoCursorDestroy + * Destroy cursor created by calling mongoCursorCreate function. */ void -MongoCursorDestroy(MONGO_CURSOR *c) +mongoCursorDestroy(MONGO_CURSOR *c) { mongoc_cursor_destroy(c); } /* - * MongoCursorNext + * mongoCursorBson * Get the current document from cursor. */ const BSON * -MongoCursorBson(MONGO_CURSOR *c) +mongoCursorBson(MONGO_CURSOR *c) { return mongoc_cursor_current(c); } /* - * MongoCursorNext + * mongoCursorNext * Get the next document from the cursor. */ bool -MongoCursorNext(MONGO_CURSOR *c, BSON *b) +mongoCursorNext(MONGO_CURSOR *c, BSON *b) { return mongoc_cursor_next(c, (const BSON **) &b); } /* - * BsonCreate + * 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) +bsonCreate(void) { BSON *doc; @@ -313,27 +313,27 @@ BsonCreate(void) } /* - * BsonDestroy - * Destroy Bson object created by BsonCreate function. + * bsonDestroy + * Destroy Bson object created by bsonCreate function. */ void -BsonDestroy(BSON *b) +bsonDestroy(BSON *b) { bson_destroy(b); } /* - * BsonIterInit + * bsonIterInit * Initialize the bson Iterator. */ bool -BsonIterInit(BSON_ITERATOR *it, BSON *b) +bsonIterInit(BSON_ITERATOR *it, BSON *b) { return bson_iter_init(it, b); } bool -BsonIterSubObject(BSON_ITERATOR *it, BSON *b) +bsonIterSubObject(BSON_ITERATOR *it, BSON *b) { const uint8_t *buffer; uint32_t len; @@ -345,7 +345,7 @@ BsonIterSubObject(BSON_ITERATOR *it, BSON *b) } int32_t -BsonIterInt32(BSON_ITERATOR *it) +bsonIterInt32(BSON_ITERATOR *it) { BSON_ASSERT(it); switch ((int) ITER_TYPE(it)) @@ -384,25 +384,25 @@ BsonIterInt32(BSON_ITERATOR *it) } int64_t -BsonIterInt64(BSON_ITERATOR *it) +bsonIterInt64(BSON_ITERATOR *it) { return bson_iter_as_int64(it); } double -BsonIterDouble(BSON_ITERATOR *it) +bsonIterDouble(BSON_ITERATOR *it) { return bson_iter_as_double(it); } bool -BsonIterBool(BSON_ITERATOR *it) +bsonIterBool(BSON_ITERATOR *it) { return bson_iter_as_bool(it); } const char * -BsonIterString(BSON_ITERATOR *it) +bsonIterString(BSON_ITERATOR *it) { uint32_t len = 0; @@ -410,7 +410,7 @@ BsonIterString(BSON_ITERATOR *it) } const char * -BsonIterBinData(BSON_ITERATOR *it, uint32_t *len) +bsonIterBinData(BSON_ITERATOR *it, uint32_t *len) { const uint8_t *binary = NULL; bson_subtype_t subtype = BSON_SUBTYPE_BINARY; @@ -421,135 +421,135 @@ BsonIterBinData(BSON_ITERATOR *it, uint32_t *len) } const bson_oid_t * -BsonIterOid(BSON_ITERATOR *it) +bsonIterOid(BSON_ITERATOR *it) { return bson_iter_oid(it); } time_t -BsonIterDate(BSON_ITERATOR *it) +bsonIterDate(BSON_ITERATOR *it) { return bson_iter_date_time(it); } const char * -BsonIterKey(BSON_ITERATOR *it) +bsonIterKey(BSON_ITERATOR *it) { return bson_iter_key(it); } int -BsonIterType(BSON_ITERATOR *it) +bsonIterType(BSON_ITERATOR *it) { return bson_iter_type(it); } int -BsonIterNext(BSON_ITERATOR *it) +bsonIterNext(BSON_ITERATOR *it) { return bson_iter_next(it); } bool -BsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) +bsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { return bson_iter_recurse(it, sub); } void -BsonOidFromString(bson_oid_t *o, char *str) +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) +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) +bsonAppendBool(BSON *b, const char *key, bool v) { return bson_append_bool(b, key, -1, v); } bool -BsonAppendStartObject(BSON *b, char *key, BSON *r) +bsonAppendStartObject(BSON *b, char *key, BSON *r) { return bson_append_document_begin(b, key, strlen(key), r); } bool -BsonAppendFinishObject(BSON *b, BSON *r) +bsonAppendFinishObject(BSON *b, BSON *r) { return bson_append_document_end(b, r); } bool -BsonAppendNull(BSON *b, const char *key) +bsonAppendNull(BSON *b, const char *key) { return bson_append_null(b, key, strlen(key)); } bool -BsonAppendInt32(BSON *b, const char *key, int v) +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) +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) +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) +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) +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) +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) +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) +bsonAppendStartArray(BSON *b, const char *key, BSON *c) { return bson_append_array_begin(b, key, -1, c); } bool -BsonAppendFinishArray(BSON *b, BSON *c) +bsonAppendFinishArray(BSON *b, BSON *c) { return bson_append_array_end(b, c); } bool -BsonFinish(BSON *b) +bsonFinish(BSON *b) { /* * There is no need for bson_finish in Meta Driver. We are doing nothing, @@ -559,29 +559,29 @@ BsonFinish(BSON *b) } bool -JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) +jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) { bool status = true; if (!v) { - BsonAppendNull(bb, k); + bsonAppendNull(bb, k); return status; } switch (json_object_get_type(v)) { case json_type_int: - BsonAppendInt32(bb, k, json_object_get_int(v)); + bsonAppendInt32(bb, k, json_object_get_int(v)); break; case json_type_boolean: - BsonAppendBool(bb, k, json_object_get_boolean(v)); + bsonAppendBool(bb, k, json_object_get_boolean(v)); break; case json_type_double: - BsonAppendDouble(bb, k, json_object_get_double(v)); + bsonAppendDouble(bb, k, json_object_get_double(v)); break; case json_type_string: - BsonAppendUTF8(bb, k, (char *) json_object_get_string(v)); + bsonAppendUTF8(bb, k, (char *) json_object_get_string(v)); break; case json_type_object: { @@ -595,23 +595,23 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - BsonOidFromString(&bsonObjectId, (char *) json_object_get_string(joj)); - status = BsonAppendOid(bb, k, &bsonObjectId); + 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)); + status = bsonAppendDate(bb, k, json_object_get_int64(joj)); break; } - BsonAppendStartObject(bb, (char *) k, &t); + bsonAppendStartObject(bb, (char *) k, &t); { json_object_object_foreach(v, kk, vv) - JsonToBsonAppendElement(&t, kk, vv); + jsonToBsonAppendElement(&t, kk, vv); } - BsonAppendFinishObject(bb, &t); + bsonAppendFinishObject(bb, &t); } break; case json_type_array: @@ -620,13 +620,13 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) char buf[10]; BSON t; - BsonAppendStartArray(bb, k, &t); + bsonAppendStartArray(bb, k, &t); for (i = 0; i < json_object_array_length(v); i++) { sprintf(buf, "%d", i); - JsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i)); + jsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i)); } - BsonAppendFinishObject(bb, &t); + bsonAppendFinishObject(bb, &t); } break; default: @@ -640,17 +640,17 @@ JsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) } json_object * -JsonTokenerPrase(char *s) +jsonTokenerPrase(char *s) { return json_tokener_parse(s); } /* - * MongoAggregateCount + * mongoAggregateCount * Count the number of documents. */ double -MongoAggregateCount(MONGO_CONN *conn, const char *database, +mongoAggregateCount(MONGO_CONN *conn, const char *database, const char *collection, const BSON *b) { BSON *command; @@ -658,13 +658,13 @@ MongoAggregateCount(MONGO_CONN *conn, const char *database, double count = 0; mongoc_cursor_t *cursor; - command = BsonCreate(); - reply = BsonCreate(); - BsonAppendUTF8(command, "count", (char *) collection); + command = bsonCreate(); + reply = bsonCreate(); + bsonAppendUTF8(command, "count", (char *) collection); if (b) /* Not empty */ - BsonAppendBson(command, "query", (BSON *) b); + bsonAppendBson(command, "query", (BSON *) b); - BsonFinish(command); + bsonFinish(command); cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, 0, command, NULL, NULL); @@ -680,51 +680,51 @@ MongoAggregateCount(MONGO_CONN *conn, const char *database, bson_copy_to(doc, reply); if (bson_iter_init_find(&it, reply, "n")) - count = BsonIterDouble(&it); + count = bsonIterDouble(&it); } mongoc_cursor_destroy(cursor); } - BsonDestroy(reply); - BsonDestroy(command); + bsonDestroy(reply); + bsonDestroy(command); return count; } void -BsonOidToString(const bson_oid_t *o, char str[25]) +bsonOidToString(const bson_oid_t *o, char str[25]) { bson_oid_to_string(o, str); } const char * -BsonIterCode(BSON_ITERATOR *i) +bsonIterCode(BSON_ITERATOR *i) { return bson_iter_code(i, NULL); } const char * -BsonIterRegex(BSON_ITERATOR *i) +bsonIterRegex(BSON_ITERATOR *i) { return bson_iter_regex(i, NULL); } const bson_value_t * -BsonIterValue(BSON_ITERATOR *i) +bsonIterValue(BSON_ITERATOR *i) { return bson_iter_value(i); } void -BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) +bsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) { if (isArray) - DumpJsonArray(output, iter); + dumpJsonArray(output, iter); else - DumpJsonObject(output, iter); + dumpJsonObject(output, iter); } /* - * DumpJsonObject + * dumpJsonObject * Converts BSON document to a JSON string. * * isArray signifies if bsonData is contents of array or object. @@ -734,7 +734,7 @@ BsonToJsonStringValue(StringInfo output, BSON_ITERATOR *iter, bool isArray) * [1] http://docs.mongodb.org/manual/reference/mongodb-extended-json/ */ void -DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) +dumpJsonObject(StringInfo output, BSON_ITERATOR *iter) { uint32_t len; const uint8_t *data; @@ -754,7 +754,7 @@ DumpJsonObject(StringInfo output, BSON_ITERATOR *iter) } void -DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) +dumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { uint32_t len; const uint8_t *data; @@ -774,7 +774,7 @@ DumpJsonArray(StringInfo output, BSON_ITERATOR *iter) } char * -BsonAsJson(const BSON *bsonDocument) +bsonAsJson(const BSON *bsonDocument) { return bson_as_json(bsonDocument, NULL); } From 28df773497d5723dd6d96c3c4261ef57a06b9300 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Sun, 27 Mar 2022 20:11:50 +0530 Subject: [PATCH 179/239] Remove support for v9.6. Standard Support for both EDB Postgres Advanced Server 9.6 and PostgreSQL 9.6 ended in February 2022. Adjust Makefile so that we restrict compilation of mongo_fdw code against v9.6. Update the README accordingly. Also, cleanup the code for the same. FDW-488, Vaibhav Dalvi, reviewed by Suraj Kharage. --- Makefile | 4 +- Makefile.legacy | 4 +- Makefile.meta | 4 +- README.md | 2 +- connection.c | 20 - expected/join_pushdown.out | 25 +- expected/join_pushdown_1.out | 25 +- expected/join_pushdown_2.out | 25 +- expected/join_pushdown_3.out | 25 +- expected/join_pushdown_4.out | 1647 ---------------------------------- mongo_fdw.c | 52 -- mongo_query.c | 8 - sql/join_pushdown.sql | 25 +- 13 files changed, 22 insertions(+), 1844 deletions(-) delete mode 100644 expected/join_pushdown_4.out diff --git a/Makefile b/Makefile index 967c226..1da5209 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) - $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) + $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 097a098..22bb977 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) - $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) + $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index c0b3160..5e7b1c7 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 9.6 10 11 12 13 14)) - $(error PostgreSQL 9.6, 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) + $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) endif diff --git a/README.md b/README.md index 61283ed..c6f6367 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 9.6, 10, 11, 12, 13, and 14. +Postgres Advanced Server 10, 11, 12, 13, and 14. Installation ------------ diff --git a/connection.c b/connection.c index 124ad52..92168d4 100644 --- a/connection.c +++ b/connection.c @@ -118,10 +118,6 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, if (entry->conn == NULL) { -#if PG_VERSION_NUM < 90600 - Oid umoid; -#endif - entry->conn = mongoConnect(opt); elog(DEBUG3, "new mongo_fdw connection %p for server \"%s:%d\"", entry->conn, opt->svr_address, opt->svr_port); @@ -135,25 +131,9 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, entry->server_hashvalue = GetSysCacheHashValue1(FOREIGNSERVEROID, ObjectIdGetDatum(server->serverid)); -#if PG_VERSION_NUM >= 90600 entry->mapping_hashvalue = GetSysCacheHashValue1(USERMAPPINGOID, ObjectIdGetDatum(user->umid)); -#else - /* Pre-9.6, UserMapping doesn't store its OID, so look it up again */ - umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER, - ObjectIdGetDatum(user->userid), - ObjectIdGetDatum(user->serverid)); - if (!OidIsValid(umoid)) - { - /* Not found for the specific user -- try PUBLIC */ - umoid = GetSysCacheOid2(USERMAPPINGUSERSERVER, - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(user->serverid)); - } - entry->mapping_hashvalue = - GetSysCacheHashValue1(USERMAPPINGOID, ObjectIdGetDatum(umoid)); -#endif } #ifdef META_DRIVER diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 898ab59..a00d4dd 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -1342,32 +1342,13 @@ SELECT e.c1, d.c2 -- Test partition-wise join SET enable_partitionwise_join TO on; --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +-- 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'); -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +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) diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index 03f182c..4eb6f87 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -1338,32 +1338,13 @@ SELECT e.c1, d.c2 -- Test partition-wise join SET enable_partitionwise_join TO on; --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +-- 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'); -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +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) diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index ecd3270..e4e44e7 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1341,32 +1341,13 @@ SELECT e.c1, d.c2 -- Test partition-wise join SET enable_partitionwise_join TO on; --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +-- 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'); -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +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) diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 87ee0db..48412fe 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -1342,32 +1342,13 @@ SELECT e.c1, d.c2 -- Test partition-wise join SET enable_partitionwise_join TO on; ERROR: unrecognized configuration parameter "enable_partitionwise_join" --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +-- 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'); -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +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) diff --git a/expected/join_pushdown_4.out b/expected/join_pushdown_4.out deleted file mode 100644 index efcfcb5..0000000 --- a/expected/join_pushdown_4.out +++ /dev/null @@ -1,1647 +0,0 @@ -\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 'edb' user with 'edb' 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'); -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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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 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) - -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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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, 3 OFFSET 50; - 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; - 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) - -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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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, 3; - 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) - --- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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 = 20 ORDER BY 1, 3; - 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, 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 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 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 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, 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) - -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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; - c1 | c2 | c1 | c2 | c6 | c8 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - | | 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 | | -(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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 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, d.c1; - 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> 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, d.c1; - c1 | c1 ------+---- - 100 | 20 -(1 row) - --- 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, 3; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> 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, 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) - --- 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, 3; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: l.c1, l.c8 - -> 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, 3; - 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) - -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; -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) - -> Limit - -> 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) -(8 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) - -> Limit - -> 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) -(8 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) - -RESET enable_hashjoin; -RESET enable_nestloop; --- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> 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 WHERE d.c1 = 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 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - 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 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) - --- 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: t.c1_1, t.c2_1, t.c1_3 - Sort Key: t.c1_3, t.c1_1 - CTE t - -> Foreign Scan - Output: d.c1, d.c3, e.c1 - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) - -> CTE Scan on t - Output: t.c1_1, t.c2_1, t.c1_3 -(9 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) - --- This won't push-down because WHERE only pushes operator expression. -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 - -> Merge Join - Merge Cond: (e.c1 = d.c8) - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(13 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. -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 - -> Merge Left Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 - -> 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 -(19 rows) - --- Don't pushdown when full document retrieval is involved. -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) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 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) - --- 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 - -> Merge Full Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(13 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) - --- Test partition-wise join -SET enable_partitionwise_join TO on; -ERROR: unrecognized configuration parameter "enable_partitionwise_join" --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; -NOTICE: syntax error -CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES FROM (1) TO (4) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test1'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE FOREIGN TABLE ftprt1_p1 PARTITION OF fprt1 FOR VALUES... - ^ -CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test2'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES... - ^ -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; -NOTICE: syntax error -CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES FROM (1) TO (4) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test3'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE FOREIGN TABLE ftprt2_p1 PARTITION OF fprt2 FOR VALUES... - ^ -CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES FROM (5) TO (8) - SERVER mongo_server OPTIONS (database 'mongo_fdw_regress', collection 'test4'); -ERROR: syntax error at or near "PARTITION" -LINE 1: CREATE FOREIGN TABLE ftprt2_p2 PARTITION OF fprt2 FOR VALUES... - ^ --- 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; -ERROR: relation "fprt1" does not exist -LINE 3: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER... - ^ -SELECT t1.c1, t2.c2 - FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER BY 1,2; -ERROR: relation "fprt1" does not exist -LINE 2: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) ORDER... - ^ --- 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; -ERROR: relation "fprt1" does not exist -LINE 3: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER... - ^ -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; -ERROR: relation "fprt1" does not exist -LINE 2: FROM fprt1 t1 INNER JOIN fprt2 t2 ON (t1.c1 = t2.c2) INNER... - ^ -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; -ERROR: relation "fprt1" does not exist -LINE 3: FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2 - ^ -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; -ERROR: relation "fprt1" does not exist -LINE 2: FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2 - ^ --- With PHVs, partitionwise join selected but no join pushdown --- Table alias in foreign scan is different for v12, v11 and v10. -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; -ERROR: relation "fprt1" does not exist -LINE 3: FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) ... - ^ -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; -ERROR: relation "fprt1" does not exist -LINE 2: FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) ... - ^ -RESET enable_partitionwise_join; -ERROR: unrecognized configuration parameter "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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.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 -(18 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.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 -(18 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.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 -(18 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.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 -(18 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) - -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; -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 test_text; -DROP FOREIGN TABLE test_varchar; -DROP TABLE l_test_tbl1; -DROP FOREIGN TABLE ftprt1_p1; -ERROR: foreign table "ftprt1_p1" does not exist -DROP FOREIGN TABLE ftprt1_p2; -ERROR: foreign table "ftprt1_p2" does not exist -DROP FOREIGN TABLE ftprt2_p1; -ERROR: foreign table "ftprt2_p1" does not exist -DROP FOREIGN TABLE ftprt2_p2; -ERROR: foreign table "ftprt2_p2" does not exist -DROP TABLE IF EXISTS fprt1; -NOTICE: table "fprt1" does not exist, skipping -DROP TABLE IF EXISTS fprt2; -NOTICE: table "fprt2" does not exist, skipping -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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 4d47f13..0488a35 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -15,9 +15,7 @@ #include "postgres.h" #include "mongo_wrapper.h" -#if PG_VERSION_NUM >= 90300 #include "access/htup_details.h" -#endif #if PG_VERSION_NUM < 120000 #include "access/sysattr.h" #endif @@ -459,17 +457,13 @@ mongoGetForeignPaths(PlannerInfo *root, /* Create a foreign path node */ foreignPath = (Path *) create_foreignscan_path(root, baserel, -#if PG_VERSION_NUM >= 90600 NULL, /* default pathtarget */ -#endif baserel->rows, startupCost, totalCost, NIL, /* no pathkeys */ baserel->lateral_relids, -#if PG_VERSION_NUM >= 90500 NULL, /* no extra plan */ -#endif NULL); /* no fdw_private data */ /* Add foreign path as the only possible path */ @@ -538,14 +532,8 @@ mongoGetForeignPlan(PlannerInfo *root, } } -#if PG_VERSION_NUM >= 90600 scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs, PVC_RECURSE_PLACEHOLDERS); -#else - scan_var_list = pull_var_clause((Node *) foreignrel->reltargetlist, - PVC_RECURSE_AGGREGATES, - PVC_RECURSE_PLACEHOLDERS); -#endif /* System attributes are not allowed. */ foreach(lc, scan_var_list) @@ -604,11 +592,7 @@ mongoGetForeignPlan(PlannerInfo *root, pull_var_clause((Node *) local_exprs, PVC_RECURSE_PLACEHOLDERS)); -#if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(foreignrel)) -#else - if (foreignrel->reloptkind == RELOPT_JOINREL) -#endif { /* * For join relations, the planner needs a targetlist, which represents @@ -676,11 +660,7 @@ mongoGetForeignPlan(PlannerInfo *root, * is part of the outer relation or not. This information would be useful * at the time of execution to prepare the MongoDB query. */ -#if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(foreignrel)) -#else - if (foreignrel->reloptkind == RELOPT_JOINREL) -#endif { /* * We use MongoJoinQualInfo to pass various information related to @@ -734,11 +714,7 @@ mongoGetForeignPlan(PlannerInfo *root, * information required to form a MongoDB query in the planning state and * passing it to the execution state through fdw_private. */ -#if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(foreignrel)) -#else - if (foreignrel->reloptkind == RELOPT_JOINREL) -#endif { fdw_private = lappend(fdw_private, makeString(fpinfo->relation_name->data)); @@ -761,11 +737,9 @@ mongoGetForeignPlan(PlannerInfo *root, scan_relid, NIL, /* No expressions to evaluate */ fdw_private -#if PG_VERSION_NUM >= 90500 ,fdw_scan_tlist ,NIL ,outer_plan -#endif ); return foreignScan; @@ -1085,11 +1059,7 @@ mongoPlanForeignModify(PlannerInfo *root, } else if (operation == CMD_UPDATE) { -#if PG_VERSION_NUM >= 90500 Bitmapset *tmpset = bms_copy(rte->updatedCols); -#else - Bitmapset *tmpset = bms_copy(rte->modifiedCols); -#endif AttrNumber col; while ((col = bms_first_member(tmpset)) >= 0) @@ -2868,24 +2838,12 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, fpinfo_i = (MongoFdwRelationInfo *) innerrel->fdw_private; /* If join pushdown is not enabled, honor it. */ -#if PG_VERSION_NUM >= 100000 if ((!IS_JOIN_REL(outerrel) && !fpinfo_o->options->enable_join_pushdown) || (!IS_JOIN_REL(innerrel) && !fpinfo_i->options->enable_join_pushdown)) -#else - if ((outerrel->reloptkind != RELOPT_JOINREL && - !fpinfo_o->options->enable_join_pushdown) || - (innerrel->reloptkind != RELOPT_JOINREL && - !fpinfo_i->options->enable_join_pushdown)) -#endif return false; /* Recursive joins can't be pushed down */ -#if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(outerrel) || IS_JOIN_REL(innerrel)) -#else - if (outerrel->reloptkind == RELOPT_JOINREL || - innerrel->reloptkind == RELOPT_JOINREL) -#endif return false; /* @@ -2904,14 +2862,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, if (fpinfo_o->local_conds || fpinfo_i->local_conds) return false; -#if PG_VERSION_NUM >= 90600 scan_var_list = pull_var_clause((Node *) joinrel->reltarget->exprs, PVC_RECURSE_PLACEHOLDERS); -#else - scan_var_list = pull_var_clause((Node *) joinrel->reltargetlist, - PVC_RECURSE_AGGREGATES, - PVC_RECURSE_PLACEHOLDERS); -#endif /* * Don't push-down join when whole row reference and/or full document @@ -3001,12 +2953,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, Relids relids; /* PlaceHolderInfo refers to parent relids, not child relids. */ -#if PG_VERSION_NUM >= 100000 relids = IS_OTHER_REL(joinrel) ? joinrel->top_parent_relids : joinrel->relids; -#else - relids = joinrel->relids; -#endif /* PG_VERSION_NUM >= 100000 */ if (bms_is_subset(phinfo->ph_eval_at, relids) && bms_nonempty_difference(relids, phinfo->ph_eval_at)) diff --git a/mongo_query.c b/mongo_query.c index fd23100..ff7a44b 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -692,11 +692,7 @@ append_param_value(BSON *queryDocument, const char *keyName, Param *paramNode, param_expr = ExecInitExpr((Expr *) paramNode, (PlanState *) scanStateNode); /* Evaluate the parameter expression */ -#if PG_VERSION_NUM >= 100000 param_value = ExecEvalExpr(param_expr, econtext, &isNull); -#else - param_value = ExecEvalExpr(param_expr, econtext, &isNull, NULL); -#endif append_mongo_value(queryDocument, keyName, param_value, isNull, paramNode->paramtype); @@ -1051,11 +1047,7 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, else columnList = list_append_unique(columnList, var); -#if PG_VERSION_NUM >= 100000 if (IS_JOIN_REL(foreignrel)) -#else - if (foreignrel->reloptkind == RELOPT_JOINREL) -#endif { MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; char *columnName; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 0d104de..406c6d6 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -326,33 +326,14 @@ SELECT e.c1, d.c2 -- Test partition-wise join SET enable_partitionwise_join TO on; --- Create the partition table in plpgsql block as those are failing with --- different error messages on back-branches. --- All test cases related to partition-wise join gives an error on v96 as --- partition syntax is not supported there. -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt1 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c1)'; -EXCEPTION WHEN others THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +-- 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'); -DO -$$ -BEGIN - EXECUTE 'CREATE TABLE fprt2 (_id NAME, c1 INTEGER, c2 INTEGER, c3 TEXT) PARTITION BY RANGE(c2)'; -EXCEPTION WHEN syntax_error THEN - RAISE NOTICE 'syntax error'; -END; -$$ -LANGUAGE plpgsql; +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) From b20b1307b8e173d3d9442bafa3ac0a425b831633 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Sun, 27 Mar 2022 22:37:48 +0530 Subject: [PATCH 180/239] Fix server crash when updating the document with text array element. For the array element, in the meta driver, we should be appending the value to the child document. However, mistakenly we were adding the value to the main query document leading to the server crash. Fix the same by correctly passing the child document to bsonAppendUTF8(). Reported on GitHub through issue #155 by zhang699. FDW-466, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajay Pal. --- expected/dml.out | 24 ++++++++++++++++++++++++ mongo_query.c | 5 +++++ sql/dml.sql | 10 ++++++++++ 3 files changed, 39 insertions(+) diff --git a/expected/dml.out b/expected/dml.out index 2631f88..5cd76cf 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -21,6 +21,8 @@ CREATE FOREIGN TABLE f_mongo_test2 (_id name, a int, b varchar) SERVER mongo_ser -- 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; @@ -238,11 +240,33 @@ 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) + -- 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/mongo_query.c b/mongo_query.c index ff7a44b..3f6b084 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -947,8 +947,13 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, &typeVarLength); valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); +#ifdef META_DRIVER + status = bsonAppendUTF8(&childDocument, keyName, + valueString); +#else status = bsonAppendUTF8(queryDocument, keyName, valueString); +#endif } bsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); diff --git a/sql/dml.sql b/sql/dml.sql index 0a06a6a..a624984 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -24,6 +24,8 @@ CREATE FOREIGN TABLE f_mongo_test2 (_id name, a int, b varchar) SERVER mongo_ser -- 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. @@ -155,12 +157,20 @@ 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; -- 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; From 87a21c55e25c8d2a4c85f67ee96ae1a7a3cc6817 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 28 Apr 2022 13:28:12 +0530 Subject: [PATCH 181/239] Fix server crash while updating/deleting with null _id value. For update/delete operations, we rely on a unique value, and for that, we use the "_id" column, which is supposed to be non-null. However, the user can insert a NULL value to this column on the MongoDB side. And as we don't have handling for the same, we ended up with a server crash along the way. Fix it by correctly appending the NULL value to this column in the update/delete query. Along the way, tighten the checks around the "_id" column. We enforce that the first column of the table must be named "_id" and of type "NAME". Also, on the MongoDB side, it should be of the "ObjectId" type. Moreover, we are not inserting any value for the "_id" column by ignoring the passed-in value. Since the MongoDB server inserts the unique value for the missing "_id" column, we are ignoring it. Emit the debug1 log message for the same. FDW-481, Vaibhav Dalvi, reviewed by Suraj Kharage and me, tested by Ajay Pal. --- README.md | 3 ++ data/mongo_test_data.js | 10 +++++ expected/dml.out | 86 +++++++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 40 +++++++++++++++++-- sql/dml.sql | 41 ++++++++++++++++++++ 5 files changed, 176 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6f6367..eccf88e 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,9 @@ db.warehouse.find -- insert row in table 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 ( { diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index d2e3ece..b24548f 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -14,6 +14,8 @@ 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(); @@ -94,3 +96,11 @@ db.test_tbl5.insertMany([ {a: 25.09}, {a: true} ]); +db.test_tbl7.insertMany([ + {_id: null, 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"}, +]); diff --git a/expected/dml.out b/expected/dml.out index 5cd76cf..b094a93 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -261,12 +261,98 @@ SELECT a, b FROM f_mongo_test6 ORDER BY a; ---+--- (0 rows) +--FDW-481: UPDATE/DELETE shouldn't lead to crash when _id is NULL. +-- 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" +DROP FOREIGN TABLE f_mongo_test7; +-- UPDATE/DELETE when _id is NULL. Shouldn't crash. +CREATE FOREIGN TABLE f_mongo_test7 (_id 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) + +SELECT * FROM f_mongo_test7 WHERE a = 10 ORDER BY 1; + _id | a | b +-----+----+------ + | 10 | ROW1 +(1 row) + +UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE _id IS NULL; +SELECT a, b FROM f_mongo_test7 ORDER BY 1; + a | b +----+--------- + 10 | UPDATED + 20 | ROW2 +(2 rows) + +DELETE FROM f_mongo_test7 WHERE a = 20; +SELECT a, b FROM f_mongo_test7 ORDER BY 1; + a | b +----+--------- + 10 | UPDATED +(1 row) + +-- Retain original data of test_tbl7 +UPDATE f_mongo_test7 SET b = 'ROW1' WHERE a = 10; +INSERT INTO f_mongo_test7 VALUES(0, 20, 'ROW2'); +-- 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 0488a35..ae17e55 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1253,7 +1253,14 @@ mongoExecForeignInsert(EState *estate, * 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; + } #if PG_VERSION_NUM < 110000 append_mongo_value(bsonDoc, @@ -1374,8 +1381,16 @@ mongoExecForeignUpdate(EState *estate, columnName = get_attname(foreignTableId, 1, false); #endif + /* 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); @@ -1416,7 +1431,7 @@ mongoExecForeignUpdate(EState *estate, bsonFinish(document); op = bsonCreate(); - if (!append_mongo_value(op, columnName, datum, false, typoid)) + if (!append_mongo_value(op, columnName, datum, isNull, typoid)) { bsonDestroy(document); return NULL; @@ -1465,10 +1480,18 @@ mongoExecForeignDelete(EState *estate, columnName = get_attname(foreignTableId, 1, false); #endif + /* 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, false, typoid)) + if (!append_mongo_value(document, columnName, datum, isNull, typoid)) { bsonDestroy(document); return NULL; @@ -1869,14 +1892,23 @@ column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) #endif 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. */ - if (bsonType == BSON_TYPE_OID) - compatibleTypes = true; + compatibleTypes = true; break; case DATEOID: case TIMESTAMPOID: diff --git a/sql/dml.sql b/sql/dml.sql index a624984..503bb5f 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -165,12 +165,53 @@ 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; +--FDW-481: UPDATE/DELETE shouldn't lead to crash when _id is NULL. +-- 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; +DROP FOREIGN TABLE f_mongo_test7; + +-- UPDATE/DELETE when _id is NULL. Shouldn't crash. +CREATE FOREIGN TABLE f_mongo_test7 (_id 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; +SELECT * FROM f_mongo_test7 WHERE a = 10 ORDER BY 1; +UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE _id IS NULL; +SELECT a, b FROM f_mongo_test7 ORDER BY 1; +DELETE FROM f_mongo_test7 WHERE a = 20; +SELECT a, b FROM f_mongo_test7 ORDER BY 1; + +-- Retain original data of test_tbl7 +UPDATE f_mongo_test7 SET b = 'ROW1' WHERE a = 10; +INSERT INTO f_mongo_test7 VALUES(0, 20, 'ROW2'); + +-- 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; From 255a83dc5cd506e1bf4fc0d60fac757076b4adcd Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 28 Apr 2022 14:41:13 +0530 Subject: [PATCH 182/239] Add support for PostgreSQL 15 and EDB Postgres Advanced Server 15. Code changes involves replacing pg_atoi() with strtol(). FDW-489, Vaibhav Dalvi, reviewed by Suraj Kharage and me. --- Makefile | 4 +- Makefile.legacy | 4 +- Makefile.meta | 4 +- README.md | 2 +- expected/join_pushdown_4.out | 1730 ++++++++++++++++++++++++++++++++++ expected/select.out | 21 +- expected/select_1.out | 21 +- option.c | 26 +- sql/select.sql | 6 + 9 files changed, 1786 insertions(+), 32 deletions(-) create mode 100644 expected/join_pushdown_4.out diff --git a/Makefile b/Makefile index 1da5209..3812c49 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) - $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) + $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 22bb977..7dcd448 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) - $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) + $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index 5e7b1c7..da7cead 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14)) - $(error PostgreSQL 10, 11, 12, 13, or 14 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) + $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/README.md b/README.md index eccf88e..a42d88f 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 10, 11, 12, 13, and 14. +Postgres Advanced Server 10, 11, 12, 13, 14, and 15. Installation ------------ diff --git a/expected/join_pushdown_4.out b/expected/join_pushdown_4.out new file mode 100644 index 0000000..003ab35 --- /dev/null +++ b/expected/join_pushdown_4.out @@ -0,0 +1,1730 @@ +\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 'edb' user with 'edb' 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'); +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 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) + +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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3 OFFSET 50; + 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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) + +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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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, 3; + 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) + +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(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 = 20 ORDER BY 1, 3; + 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, 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 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 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + 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) +(4 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, 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) + +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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; + c1 | c2 | c1 | c2 | c6 | c8 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> Foreign Scan + Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) +(4 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, d.c1; + 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, d.c1; + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Sort Key: e.c3 + -> 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, d.c1; + c1 | c1 +-----+---- + 100 | 20 +(1 row) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------- + Sort + Sort Key: d.c1, e.c1 + -> 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, 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) + +-- 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, 3; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------- + Sort + Sort Key: l.c1, l.c8 + -> 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, 3; + 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) + +SET enable_hashjoin TO OFF; +SET enable_nestloop TO OFF; +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) + -> Limit + -> 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) +(8 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) + -> Limit + -> 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) +(8 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) + +RESET enable_hashjoin; +RESET enable_nestloop; +-- 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: e.c1 + -> 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 WHERE d.c1 = 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 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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------ + Sort + Sort Key: d.c1, e.c1 + -> Foreign Scan + Filter: ((c1 = 10) OR (c8 = 30)) + 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 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) + +-- 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) + +-- This won't push-down because WHERE only pushes operator expression. +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 + -> Hash Join + Hash Cond: (e.c1 = d.c8) + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash + -> Foreign Scan on f_test_tbl1 d + Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(10 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. +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) + +-- Don't pushdown when full document retrieval is involved. +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) + -> Foreign Scan on test_varchar + Foreign Namespace: mongo_fdw_regress.warehouse +(10 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) + +-- 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) + +-- 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 +------------------------------------------------------------------- + Merge Append + Sort Key: t1.c1, t1.c2 + -> Merge Join + Output: t1_1.c1, t1_1.c2 + Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) + -> Sort + Output: t1_1.c1, t1_1.c2 + Sort Key: t1_1.c1, t1_1.c2 + -> Foreign Scan on public.ftprt1_p1 t1_1 + Output: t1_1.c1, t1_1.c2 + Filter: ((t1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Sort + Output: t2_1.c2, t2_1.c1 + Sort Key: t2_1.c2, t2_1.c1 + -> Foreign Scan on public.ftprt2_p1 t2_1 + Output: t2_1.c2, t2_1.c1 + Foreign Namespace: mongo_fdw_regress.test3 + -> Merge Join + Output: t1_2.c1, t1_2.c2 + Merge Cond: ((t1_2.c1 = t2_2.c2) AND (t1_2.c2 = t2_2.c1)) + -> Sort + Output: t1_2.c1, t1_2.c2 + Sort Key: t1_2.c1, t1_2.c2 + -> Foreign Scan on public.ftprt1_p2 t1_2 + Output: t1_2.c1, t1_2.c2 + Filter: ((t1_2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Sort + Output: t2_2.c2, t2_2.c1 + Sort Key: t2_2.c2, t2_2.c1 + -> Foreign Scan on public.ftprt2_p2 t2_2 + Output: t2_2.c2, t2_2.c1 + Foreign Namespace: mongo_fdw_regress.test4 +(34 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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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 +-------------------------------------------------------------------------------- + Sort + Output: fprt1.c1, 't1_phv'::text, fprt2.c2, ('t2_phv'::text) + Sort Key: fprt1.c1, fprt2.c2 + -> Append + -> Hash Left Join + Output: fprt1_1.c1, 't1_phv'::text, fprt2_1.c2, ('t2_phv'::text) + Hash Cond: (fprt1_1.c1 = fprt2_1.c2) + -> Foreign Scan on public.ftprt1_p1 fprt1_1 + Output: fprt1_1.c1 + Filter: ((fprt1_1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Hash + Output: fprt2_1.c2, ('t2_phv'::text) + -> Foreign Scan on public.ftprt2_p1 fprt2_1 + Output: fprt2_1.c2, 't2_phv'::text + Filter: ((fprt2_1.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test3 + -> Hash Left Join + Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text) + Hash Cond: (fprt1_2.c1 = fprt2_2.c2) + -> Foreign Scan on public.ftprt1_p2 fprt1_2 + Output: fprt1_2.c1 + Filter: ((fprt1_2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Hash + Output: fprt2_2.c2, ('t2_phv'::text) + -> Foreign Scan on public.ftprt2_p2 fprt2_2 + Output: fprt2_2.c2, 't2_phv'::text + Filter: ((fprt2_2.c2 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test4 +(30 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) + +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; +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 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 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; diff --git a/expected/select.out b/expected/select.out index bd5efa7..9612068 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1228,6 +1228,9 @@ SELECT t1.c2, t1 FROM f_test_tbl1 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; @@ -1236,23 +1239,21 @@ SELECT d, d.c2, e.c1, e Sort Output: d.*, d.c2, e.c1, e.* Sort Key: d.*, e.c1 - -> Merge Left Join + -> Hash Left Join Output: d.*, d.c2, e.c1, e.* - Merge Cond: (d.c1 = e.c8) - -> Sort + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d Output: d.*, d.c2, d.c1 - Sort Key: d.c1 - -> Foreign Scan on public.f_test_tbl2 d - Output: d.*, d.c2, d.c1 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash Output: e.c1, e.*, e.c8 - Sort Key: e.c8 -> Foreign Scan on public.f_test_tbl1 e Output: e.c1, e.*, e.c8 Foreign Namespace: mongo_fdw_regress.test_tbl1 -(18 rows) +(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; diff --git a/expected/select_1.out b/expected/select_1.out index c0c885e..0794b76 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1192,6 +1192,9 @@ SELECT t1.c2, t1 FROM f_test_tbl1 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; @@ -1200,23 +1203,21 @@ SELECT d, d.c2, e.c1, e Sort Output: d.*, d.c2, e.c1, e.* Sort Key: d.*, e.c1 - -> Merge Left Join + -> Hash Left Join Output: d.*, d.c2, e.c1, e.* - Merge Cond: (d.c1 = e.c8) - -> Sort + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d Output: d.*, d.c2, d.c1 - Sort Key: d.c1 - -> Foreign Scan on public.f_test_tbl2 d - Output: d.*, d.c2, d.c1 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash Output: e.c1, e.*, e.c8 - Sort Key: e.c8 -> Foreign Scan on public.f_test_tbl1 e Output: e.c1, e.*, e.c8 Foreign Namespace: mongo_fdw_regress.test_tbl1 -(18 rows) +(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; diff --git a/option.c b/option.c index 35d0597..9fc6168 100644 --- a/option.c +++ b/option.c @@ -80,14 +80,30 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) /* If port option is given, error out if its value isn't an integer */ if (strncmp(optionName, OPTION_NAME_PORT, NAMEDATALEN) == 0) { - int32 port; + char *intString = defGetString(optionDef); + long port; + char *endp; - port = pg_atoi(defGetString(optionDef), sizeof(int32), 0); - if (port < 0 || port > USHRT_MAX) + 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 \"%d\" is out of range for type %s", - port, "unsigned short"))); + 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 #ifdef META_DRIVER diff --git a/sql/select.sql b/sql/select.sql index 34c9242..290f61e 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -293,9 +293,15 @@ SELECT count(tableoid) FROM f_test_tbl1; 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. From 19af3ca6295f9f02cab3b95d161198acff300fb9 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 14:19:22 +0530 Subject: [PATCH 183/239] Add some dummy startup and total default costs. We were using zero, which is not realistic. Thus add some dummy numbers, those are not realistic, but better than zero. And helps in regression runs. FDW-512, Vaibhav Dalvi, reviewed by Suraj Kharage. --- expected/join_pushdown.out | 82 +- expected/join_pushdown_1.out | 166 ++-- expected/join_pushdown_2.out | 166 ++-- expected/join_pushdown_3.out | 132 ++- expected/join_pushdown_4.out | 1730 ---------------------------------- mongo_fdw.c | 35 +- sql/join_pushdown.sql | 8 +- 7 files changed, 260 insertions(+), 2059 deletions(-) delete mode 100644 expected/join_pushdown_4.out diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index a00d4dd..6cc7ed7 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -549,25 +549,23 @@ SELECT d.c1, e.c1 100 | 20 (1 row) +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, 3; - QUERY PLAN --------------------------------------------------------------------- - Incremental Sort + QUERY PLAN +-------------------------------------------------------------- + Sort Sort Key: d.c1, e.c1 - Presorted Key: d.c1 - -> Merge Left Join - Merge Cond: (d.c1 = e.c8) - -> Sort - Sort Key: d.c1 - -> Foreign Scan on f_test_tbl2 d - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: e.c8 + -> 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 -(12 rows) +(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, 3; @@ -592,6 +590,8 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (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 @@ -634,8 +634,6 @@ SELECT l.c1, l.c6, l.c8 1600 | | (16 rows) -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; EXPLAIN (COSTS OFF) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -684,8 +682,6 @@ SELECT l.c1, l.c6, l.c8 100 | 800.3 | 20 (1 row) -RESET enable_hashjoin; -RESET enable_nestloop; -- Execute JOIN through PREPARE statement. PREPARE pre_stmt_left_join AS SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 @@ -1034,18 +1030,15 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 -------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Merge Join - Merge Cond: (e.c1 = d.c8) - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: d.c8 + -> Hash Join + Hash Cond: (e.c1 = d.c8) + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash -> Foreign Scan on f_test_tbl1 d Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(13 rows) +(10 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; @@ -1142,21 +1135,18 @@ SELECT d, e Merge Cond: (e.c1 = f.c8) -> Sort Sort Key: e.c1 - -> Merge Left Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> 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 -(19 rows) +(16 rows) -- Don't pushdown when full document retrieval is involved. EXPLAIN (COSTS OFF) @@ -1172,9 +1162,10 @@ SELECT json_data.key AS key1, json_data.value AS value1 Foreign Namespace: mongo_fdw_regress.warehouse -> Function Scan on json_each_text json_data Filter: (key <> '_id'::text) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 rows) + -> 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"; @@ -1283,17 +1274,14 @@ SELECT d.c1, e.c1 Limit -> Sort Sort Key: d.c2 - -> Merge Full Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 + -> 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 -(13 rows) +(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; diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index 4eb6f87..ee11238 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -549,6 +549,8 @@ SELECT d.c1, e.c1 100 | 20 (1 row) +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 @@ -588,6 +590,8 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (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 @@ -630,8 +634,6 @@ SELECT l.c1, l.c6, l.c8 1600 | | (16 rows) -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; EXPLAIN (COSTS OFF) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -680,8 +682,6 @@ SELECT l.c1, l.c6, l.c8 100 | 800.3 | 20 (1 row) -RESET enable_hashjoin; -RESET enable_nestloop; -- Execute JOIN through PREPARE statement. PREPARE pre_stmt_left_join AS SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 @@ -1030,18 +1030,15 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 -------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Merge Join - Merge Cond: (e.c1 = d.c8) - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: d.c8 + -> Hash Join + Hash Cond: (e.c1 = d.c8) + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash -> Foreign Scan on f_test_tbl1 d Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(13 rows) +(10 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; @@ -1138,21 +1135,18 @@ SELECT d, e Merge Cond: (e.c1 = f.c8) -> Sort Sort Key: e.c1 - -> Merge Left Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> 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 -(19 rows) +(16 rows) -- Don't pushdown when full document retrieval is involved. EXPLAIN (COSTS OFF) @@ -1168,9 +1162,10 @@ SELECT json_data.key AS key1, json_data.value AS value1 Foreign Namespace: mongo_fdw_regress.warehouse -> Function Scan on json_each_text json_data Filter: (key <> '_id'::text) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 rows) + -> 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"; @@ -1279,17 +1274,14 @@ SELECT d.c1, e.c1 Limit -> Sort Sort Key: d.c2 - -> Merge Full Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 + -> 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 -(13 rows) +(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; @@ -1501,41 +1493,33 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 -> Append - -> Merge Left Join + -> Hash Left Join Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) - -> Sort + Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) + -> Foreign Scan on public.ftprt1_p1 Output: ftprt1_p1.c1 - Sort Key: ftprt1_p1.c1 - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Filter: ((ftprt1_p1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort + Filter: ((ftprt1_p1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Hash Output: ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt2_p1.c2 -> Foreign Scan on public.ftprt2_p1 Output: ftprt2_p1.c2, 't2_phv'::text Filter: ((ftprt2_p1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Left Join + -> Hash Left Join Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) - Merge Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) - -> Sort + Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) + -> Foreign Scan on public.ftprt1_p2 Output: ftprt1_p2.c1 - Sort Key: ftprt1_p2.c1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Filter: ((ftprt1_p2.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort + Filter: ((ftprt1_p2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Hash Output: ftprt2_p2.c2, ('t2_phv'::text) - Sort Key: ftprt2_p2.c2 -> Foreign Scan on public.ftprt2_p2 Output: ftprt2_p2.c2, 't2_phv'::text Filter: ((ftprt2_p2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(38 rows) +(30 rows) SELECT t1.c1, t1.phv, t2.c2, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN @@ -1564,22 +1548,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1605,22 +1585,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1646,22 +1622,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1689,22 +1661,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) EXPLAIN (COSTS FALSE, VERBOSE) SELECT t1.c1, t2.c2 diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index e4e44e7..4ad1d16 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -549,6 +549,8 @@ SELECT d.c1, e.c1 100 | 20 (1 row) +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 @@ -588,6 +590,8 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (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 @@ -630,8 +634,6 @@ SELECT l.c1, l.c6, l.c8 1600 | | (16 rows) -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; EXPLAIN (COSTS OFF) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -680,8 +682,6 @@ SELECT l.c1, l.c6, l.c8 100 | 800.3 | 20 (1 row) -RESET enable_hashjoin; -RESET enable_nestloop; -- Execute JOIN through PREPARE statement. PREPARE pre_stmt_left_join AS SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 @@ -1033,18 +1033,15 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 -------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Merge Join - Merge Cond: (e.c1 = d.c8) - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: d.c8 + -> Hash Join + Hash Cond: (e.c1 = d.c8) + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash -> Foreign Scan on f_test_tbl1 d Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(13 rows) +(10 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; @@ -1141,21 +1138,18 @@ SELECT d, e Merge Cond: (e.c1 = f.c8) -> Sort Sort Key: e.c1 - -> Merge Left Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> 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 -(19 rows) +(16 rows) -- Don't pushdown when full document retrieval is involved. EXPLAIN (COSTS OFF) @@ -1171,9 +1165,10 @@ SELECT json_data.key AS key1, json_data.value AS value1 Foreign Namespace: mongo_fdw_regress.warehouse -> Function Scan on json_each_text json_data Filter: (key <> '_id'::text) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 rows) + -> 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"; @@ -1282,17 +1277,14 @@ SELECT d.c1, e.c1 Limit -> Sort Sort Key: d.c2 - -> Merge Full Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 + -> 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 -(13 rows) +(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; @@ -1504,41 +1496,33 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 -> Append - -> Merge Left Join + -> Hash Left Join Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) - -> Sort + Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) + -> Foreign Scan on public.ftprt1_p1 Output: ftprt1_p1.c1 - Sort Key: ftprt1_p1.c1 - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Filter: ((ftprt1_p1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort + Filter: ((ftprt1_p1.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test1 + -> Hash Output: ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt2_p1.c2 -> Foreign Scan on public.ftprt2_p1 Output: ftprt2_p1.c2, 't2_phv'::text Filter: ((ftprt2_p1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Left Join + -> Hash Left Join Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) - Merge Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) - -> Sort + Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) + -> Foreign Scan on public.ftprt1_p2 Output: ftprt1_p2.c1 - Sort Key: ftprt1_p2.c1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Filter: ((ftprt1_p2.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort + Filter: ((ftprt1_p2.c1 % 2) = 0) + Foreign Namespace: mongo_fdw_regress.test2 + -> Hash Output: ftprt2_p2.c2, ('t2_phv'::text) - Sort Key: ftprt2_p2.c2 -> Foreign Scan on public.ftprt2_p2 Output: ftprt2_p2.c2, 't2_phv'::text Filter: ((ftprt2_p2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(38 rows) +(30 rows) SELECT t1.c1, t1.phv, t2.c2, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN @@ -1567,22 +1551,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1608,22 +1588,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1649,22 +1625,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1692,22 +1664,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) EXPLAIN (COSTS FALSE, VERBOSE) SELECT t1.c1, t2.c2 diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 48412fe..a122f12 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -549,6 +549,8 @@ SELECT d.c1, e.c1 100 | 20 (1 row) +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 @@ -588,6 +590,8 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (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 @@ -630,8 +634,6 @@ SELECT l.c1, l.c6, l.c8 1600 | | (16 rows) -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; EXPLAIN (COSTS OFF) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -680,8 +682,6 @@ SELECT l.c1, l.c6, l.c8 100 | 800.3 | 20 (1 row) -RESET enable_hashjoin; -RESET enable_nestloop; -- Execute JOIN through PREPARE statement. PREPARE pre_stmt_left_join AS SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 @@ -1033,18 +1033,15 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 -------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Merge Join - Merge Cond: (e.c1 = d.c8) - -> Sort - Sort Key: e.c1 - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Sort - Sort Key: d.c8 + -> Hash Join + Hash Cond: (e.c1 = d.c8) + -> Foreign Scan on f_test_tbl2 e + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash -> Foreign Scan on f_test_tbl1 d Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(13 rows) +(10 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; @@ -1141,21 +1138,18 @@ SELECT d, e Merge Cond: (e.c1 = f.c8) -> Sort Sort Key: e.c1 - -> Merge Left Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> 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 -(19 rows) +(16 rows) -- Don't pushdown when full document retrieval is involved. EXPLAIN (COSTS OFF) @@ -1171,9 +1165,10 @@ SELECT json_data.key AS key1, json_data.value AS value1 Foreign Namespace: mongo_fdw_regress.warehouse -> Function Scan on json_each_text json_data Filter: (key <> '_id'::text) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 rows) + -> 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"; @@ -1282,17 +1277,14 @@ SELECT d.c1, e.c1 Limit -> Sort Sort Key: d.c2 - -> Merge Full Join - Merge Cond: (d.c8 = e.c1) - -> Sort - Sort Key: d.c8 - -> Foreign Scan on f_test_tbl1 d - Foreign Namespace: mongo_fdw_regress.test_tbl1 - -> Sort - Sort Key: e.c1 + -> 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 -(13 rows) +(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; @@ -1572,22 +1564,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER SERVER mongo_server OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1613,22 +1601,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl2 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1654,22 +1638,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_join_pushdown 'true'); EXPLAIN (COSTS FALSE, VERBOSE) @@ -1697,22 +1677,18 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 Sort Key: d.c1, e.c1 - -> Merge Join + -> Hash 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 + Hash Cond: (d.c1 = e.c8) + -> Foreign Scan on public.f_test_tbl2 d + Output: d._id, d.c1, d.c2, d.c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 + -> Hash 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 -(18 rows) +(14 rows) EXPLAIN (COSTS FALSE, VERBOSE) SELECT t1.c1, t2.c2 diff --git a/expected/join_pushdown_4.out b/expected/join_pushdown_4.out deleted file mode 100644 index 003ab35..0000000 --- a/expected/join_pushdown_4.out +++ /dev/null @@ -1,1730 +0,0 @@ -\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 'edb' user with 'edb' 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'); -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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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 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) - -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, 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_tbl1 e LEFT OUTER JOIN f_test_tbl2 d ON e.c8 = d.c1 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 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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, 3 OFFSET 50; - 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 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; - 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) - -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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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, 3; - 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) - --- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(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 = 20 ORDER BY 1, 3; - 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, 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 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 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 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, 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) - -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, 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 = 20 AND e.c2 = 'EMP1') ORDER BY 1, 3; - c1 | c2 | c1 | c2 | c6 | c8 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - | | 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 | | -(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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 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, d.c1; - 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> 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, d.c1; - c1 | c1 ------+---- - 100 | 20 -(1 row) - --- 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, 3; - QUERY PLAN --------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> 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, 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) - --- 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, 3; - QUERY PLAN --------------------------------------------------------------------------------------------------------------------------- - Sort - Sort Key: l.c1, l.c8 - -> 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, 3; - 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) - -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; -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) - -> Limit - -> 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) -(8 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) - -> Limit - -> 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) -(8 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) - -RESET enable_hashjoin; -RESET enable_nestloop; --- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> 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 WHERE d.c1 = 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 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - 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 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) - --- 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) - --- This won't push-down because WHERE only pushes operator expression. -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 - -> Hash Join - Hash Cond: (e.c1 = d.c8) - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(10 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. -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) - --- Don't pushdown when full document retrieval is involved. -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) - -> Foreign Scan on test_varchar - Foreign Namespace: mongo_fdw_regress.warehouse -(10 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) - --- 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) - --- 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 -------------------------------------------------------------------- - Merge Append - Sort Key: t1.c1, t1.c2 - -> Merge Join - Output: t1_1.c1, t1_1.c2 - Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) - -> Sort - Output: t1_1.c1, t1_1.c2 - Sort Key: t1_1.c1, t1_1.c2 - -> Foreign Scan on public.ftprt1_p1 t1_1 - Output: t1_1.c1, t1_1.c2 - Filter: ((t1_1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort - Output: t2_1.c2, t2_1.c1 - Sort Key: t2_1.c2, t2_1.c1 - -> Foreign Scan on public.ftprt2_p1 t2_1 - Output: t2_1.c2, t2_1.c1 - Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Join - Output: t1_2.c1, t1_2.c2 - Merge Cond: ((t1_2.c1 = t2_2.c2) AND (t1_2.c2 = t2_2.c1)) - -> Sort - Output: t1_2.c1, t1_2.c2 - Sort Key: t1_2.c1, t1_2.c2 - -> Foreign Scan on public.ftprt1_p2 t1_2 - Output: t1_2.c1, t1_2.c2 - Filter: ((t1_2.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: t2_2.c2, t2_2.c1 - Sort Key: t2_2.c2, t2_2.c1 - -> Foreign Scan on public.ftprt2_p2 t2_2 - Output: t2_2.c2, t2_2.c1 - Foreign Namespace: mongo_fdw_regress.test4 -(34 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 --- Table alias in foreign scan is different for v12, v11 and v10. -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 --------------------------------------------------------------------------------- - Sort - Output: fprt1.c1, 't1_phv'::text, fprt2.c2, ('t2_phv'::text) - Sort Key: fprt1.c1, fprt2.c2 - -> Append - -> Hash Left Join - Output: fprt1_1.c1, 't1_phv'::text, fprt2_1.c2, ('t2_phv'::text) - Hash Cond: (fprt1_1.c1 = fprt2_1.c2) - -> Foreign Scan on public.ftprt1_p1 fprt1_1 - Output: fprt1_1.c1 - Filter: ((fprt1_1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Hash - Output: fprt2_1.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p1 fprt2_1 - Output: fprt2_1.c2, 't2_phv'::text - Filter: ((fprt2_1.c2 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test3 - -> Hash Left Join - Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text) - Hash Cond: (fprt1_2.c1 = fprt2_2.c2) - -> Foreign Scan on public.ftprt1_p2 fprt1_2 - Output: fprt1_2.c1 - Filter: ((fprt1_2.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Hash - Output: fprt2_2.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p2 fprt2_2 - Output: fprt2_2.c2, 't2_phv'::text - Filter: ((fprt2_2.c2 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test4 -(30 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) - -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; -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 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 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index ae17e55..eeadc6f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -178,6 +178,9 @@ static const char *escape_json_string(const char *string); static void bson_to_json_string(StringInfo output, BSON_ITERATOR iter, bool isArray); #endif +static void +mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, + Oid foreigntableid); /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -384,8 +387,8 @@ mongoGetForeignPaths(PlannerInfo *root, { Path *foreignPath; MongoFdwOptions *options; - Cost startupCost = 0.0; - Cost totalCost = 0.0; + Cost startupCost; + Cost totalCost; /* Fetch options */ options = mongo_get_options(foreigntableid); @@ -454,6 +457,11 @@ mongoGetForeignPaths(PlannerInfo *root, (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 */ foreignPath = (Path *) create_foreignscan_path(root, baserel, @@ -3078,3 +3086,26 @@ mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo) } } #endif /* End of META_DRIVER */ + +/* + * 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; +} diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 406c6d6..418e93d 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -135,12 +135,16 @@ SELECT d.c1, e.c1 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, d.c1; +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, 3; 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; +RESET enable_mergejoin; +RESET enable_nestloop; -- JOIN in sub-query, should be pushed down. EXPLAIN (COSTS OFF) @@ -150,8 +154,6 @@ SELECT l.c1, l.c6, l.c8 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, 3; -SET enable_hashjoin TO OFF; -SET enable_nestloop TO OFF; EXPLAIN (COSTS OFF) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -166,8 +168,6 @@ SELECT l.c1, l.c6, l.c8 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; -RESET enable_hashjoin; -RESET enable_nestloop; -- Execute JOIN through PREPARE statement. PREPARE pre_stmt_left_join AS From 7d3858836f31a5e1830b05eab476e638a5773ea3 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 14:19:22 +0530 Subject: [PATCH 184/239] Refactor deparsing code in favor of upcoming aggregate push-down patches. This involves renaming a few variable names and structure names so that they can be used for general purposes. Also, remove some unused code. FDW-137, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, tested by Kashif Zeeshan. --- README.md | 4 +-- deparse.c | 67 ++++++++++++++++++++------------------------------- mongo_fdw.c | 45 +++++++++++++++++----------------- mongo_fdw.h | 12 ++++----- mongo_query.c | 19 ++++++--------- 5 files changed, 63 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index a42d88f..448c9f6 100644 --- a/README.md +++ b/README.md @@ -208,8 +208,8 @@ 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. ### New MongoDB C Driver Support -The third enhancement is to add a new [MongoDB][1]' C driver. The -current implementation is based on the legacy driver of MongoDB. But +This enhancement is to add a new [MongoDB][1]' C driver. The current +implementation is based on the legacy driver of MongoDB. But [MongoDB][1] is provided completely new library for driver called MongoDB's meta driver. Added support for the same. Now compile time option is available to use legacy and meta driver. diff --git a/deparse.c b/deparse.c index 0af9e19..74cc2b2 100644 --- a/deparse.c +++ b/deparse.c @@ -44,8 +44,8 @@ * 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, MongoJoinQualInfo *jqinfo); -static void mongo_check_var(Var *column, MongoJoinQualInfo *jqinfo); +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, @@ -64,7 +64,7 @@ static void mongo_add_null_check(Var *column, BSON *expr, * required information from it. */ void -mongo_check_qual(Expr *node, MongoJoinQualInfo *jqinfo) +mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info) { if (node == NULL) return; @@ -72,24 +72,24 @@ mongo_check_qual(Expr *node, MongoJoinQualInfo *jqinfo) switch (nodeTag(node)) { case T_Var: - mongo_check_var((Var *) node, jqinfo); + mongo_check_var((Var *) node, qual_info); break; case T_OpExpr: - mongo_check_op_expr((OpExpr *) node, jqinfo); + mongo_check_op_expr((OpExpr *) node, qual_info); break; case T_List: { ListCell *lc; foreach(lc, (List *) node) - mongo_check_qual((Expr *) lfirst(lc), jqinfo); + mongo_check_qual((Expr *) lfirst(lc), qual_info); } break; case T_RelabelType: - mongo_check_qual(((RelabelType *) node)->arg, jqinfo); + mongo_check_qual(((RelabelType *) node)->arg, qual_info); break; case T_BoolExpr: - mongo_check_qual((Expr *)((BoolExpr *) node)->args, jqinfo); + mongo_check_qual((Expr *)((BoolExpr *) node)->args, qual_info); break; case T_Const: case T_Param: @@ -107,7 +107,7 @@ mongo_check_qual(Expr *node, MongoJoinQualInfo *jqinfo) * Check given operator expression. */ static void -mongo_check_op_expr(OpExpr *node, MongoJoinQualInfo *jqinfo) +mongo_check_op_expr(OpExpr *node, MongoRelQualInfo *qual_info) { HeapTuple tuple; Form_pg_operator form; @@ -131,14 +131,14 @@ mongo_check_op_expr(OpExpr *node, MongoJoinQualInfo *jqinfo) if (oprkind == 'r' || oprkind == 'b') { arg = list_head(node->args); - mongo_check_qual(lfirst(arg), jqinfo); + 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), jqinfo); + mongo_check_qual(lfirst(arg), qual_info); } ReleaseSysCache(tuple); @@ -149,29 +149,26 @@ mongo_check_op_expr(OpExpr *node, MongoJoinQualInfo *jqinfo) * Check the given Var and append required information related to columns * involved in qual clauses to separate lists in context. * - * Save required information in the form of a list in MongoJoinQualInfo + * 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, MongoJoinQualInfo *jqinfo) +mongo_check_var(Var *column, MongoRelQualInfo *qual_info) { RangeTblEntry *rte; char *colname; - char *tabname; - ListCell *lc; - ForeignTable *table; ColumnHashKey key; bool found; bool is_outerrel = false; - if (!(bms_is_member(column->varno, jqinfo->foreignRel->relids) && + 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 (!jqinfo->joinExprColHash) + if (!qual_info->exprColHash) { HASHCTL hashInfo; @@ -180,21 +177,21 @@ mongo_check_var(Var *column, MongoJoinQualInfo *jqinfo) hashInfo.entrysize = sizeof(ColumnHashKey); hashInfo.hcxt = CurrentMemoryContext; - jqinfo->joinExprColHash = hash_create("Join Expression Column Hash", - MaxHashTableSize, - &hashInfo, - (HASH_ELEM | HASH_BLOBS | HASH_CONTEXT)); + 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(jqinfo->joinExprColHash, (void *)&key, HASH_ENTER, &found); + hash_search(qual_info->exprColHash, (void *)&key, HASH_ENTER, &found); if (found) return; /* Get RangeTblEntry from array in PlannerInfo. */ - rte = planner_rt_fetch(column->varno, jqinfo->root); + rte = planner_rt_fetch(column->varno, qual_info->root); #if PG_VERSION_NUM >= 110000 colname = get_attname(rte->relid, column->varattno, false); @@ -202,27 +199,15 @@ mongo_check_var(Var *column, MongoJoinQualInfo *jqinfo) colname = get_relid_attribute_name(rte->relid, column->varattno); #endif - table = GetForeignTable(rte->relid); - foreach(lc, table->options) - { - DefElem *def = (DefElem *) lfirst(lc); - - if (strcmp(def->defname, "collection") == 0) - tabname = defGetString(def); - } - - if (tabname == NULL) - tabname = get_rel_name(rte->relid); - /* Is relation inner or outer? */ - if (bms_is_member(column->varno, jqinfo->outerRelids)) + if (bms_is_member(column->varno, qual_info->outerRelids)) is_outerrel = true; /* Fill the lists with elements */ - jqinfo->colNameList = lappend(jqinfo->colNameList, makeString(colname)); - jqinfo->colNumList = lappend_int(jqinfo->colNumList, column->varattno); - jqinfo->rtiList = lappend_int(jqinfo->rtiList, column->varno); - jqinfo->isOuterList = lappend_int(jqinfo->isOuterList, is_outerrel); + 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); } /* diff --git a/mongo_fdw.c b/mongo_fdw.c index eeadc6f..8c44dbb 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -171,7 +171,7 @@ 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, MongoJoinQualInfo *jqinfo); +static void mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info); #endif #ifndef META_DRIVER static const char *escape_json_string(const char *string); @@ -507,12 +507,11 @@ mongoGetForeignPlan(PlannerInfo *root, List *column_name_list = NIL; List *is_inner_column_list = NIL; #ifdef META_DRIVER - MongoJoinQualInfo *jqinfo; + MongoRelQualInfo *qual_info; #endif /* Set scan relation id */ - if (foreignrel->reloptkind == RELOPT_BASEREL || - foreignrel->reloptkind == RELOPT_OTHER_MEMBER_REL) + if (IS_SIMPLE_REL(foreignrel)) scan_relid = foreignrel->relid; else { @@ -671,21 +670,21 @@ mongoGetForeignPlan(PlannerInfo *root, if (IS_JOIN_REL(foreignrel)) { /* - * We use MongoJoinQualInfo to pass various information related to + * We use MongoRelQualInfo to pass various information related to * joining quals to fdw_private which is used to form equivalent * MongoDB query during the execution phase. */ - jqinfo = (MongoJoinQualInfo *) palloc(sizeof(MongoJoinQualInfo)); + qual_info = (MongoRelQualInfo *) palloc(sizeof(MongoRelQualInfo)); - jqinfo->root = root; - jqinfo->foreignRel = foreignrel; - jqinfo->outerRelids = fpinfo->outerrel->relids; - jqinfo->joinExprColHash = NULL; + qual_info->root = root; + qual_info->foreignRel = foreignrel; + qual_info->outerRelids = fpinfo->outerrel->relids; + qual_info->exprColHash = NULL; /* Initialize all lists */ - jqinfo->colNameList = NIL; - jqinfo->colNumList = NIL; - jqinfo->rtiList = NIL; - jqinfo->isOuterList = NIL; + qual_info->colNameList = NIL; + qual_info->colNumList = NIL; + qual_info->rtiList = NIL; + qual_info->isOuterList = NIL; /* * Extract required data of columns involved in join clauses and append @@ -693,7 +692,7 @@ mongoGetForeignPlan(PlannerInfo *root, * Also, save join type in the list. */ if (fpinfo->joinclauses) - mongo_prepare_qual_info(fpinfo->joinclauses, jqinfo); + mongo_prepare_qual_info(fpinfo->joinclauses, qual_info); /* * Extract required data of columns involved in the WHERE clause and @@ -701,10 +700,10 @@ mongoGetForeignPlan(PlannerInfo *root, * executor. */ if (fpinfo->remote_conds) - mongo_prepare_qual_info(fpinfo->remote_conds, jqinfo); + mongo_prepare_qual_info(fpinfo->remote_conds, qual_info); /* Destroy hash table used to get unique column info */ - hash_destroy(jqinfo->joinExprColHash); + hash_destroy(qual_info->exprColHash); } #endif @@ -729,10 +728,10 @@ mongoGetForeignPlan(PlannerInfo *root, fdw_private = lappend(fdw_private, column_name_list); fdw_private = lappend(fdw_private, is_inner_column_list); fdw_private = lappend(fdw_private, fpinfo->joinclauses); - fdw_private = lappend(fdw_private, jqinfo->colNameList); - fdw_private = lappend(fdw_private, jqinfo->colNumList); - fdw_private = lappend(fdw_private, jqinfo->rtiList); - fdw_private = lappend(fdw_private, jqinfo->isOuterList); + 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, list_make2(makeString(fpinfo->inner_relname), makeString(fpinfo->outer_relname))); @@ -3066,7 +3065,7 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, * clause from each qual and process it further using mongo_check_qual(). */ static void -mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo) +mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info) { ListCell *lc; @@ -3082,7 +3081,7 @@ mongo_prepare_qual_info(List *quals, MongoJoinQualInfo *jqinfo) expr = ri->clause; } - mongo_check_qual(expr, jqinfo); + mongo_check_qual(expr, qual_info); } } #endif /* End of META_DRIVER */ diff --git a/mongo_fdw.h b/mongo_fdw.h index 0e25ca8..d37e722 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -351,7 +351,7 @@ typedef struct MongoFdwRelationInfo } MongoFdwRelationInfo; /* - * MongoJoinQualInfo contains column name, varno, varattno, and its relation + * MongoRelQualInfo contains column name, varno, varattno, and its relation * name of columns involved in the join quals which is passed to execution * state through fdw_private. * @@ -371,7 +371,7 @@ typedef struct MongoFdwRelationInfo * (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 MongoJoinQualInfo: + * form the following 4 lists as part of MongoRelQualInfo: * * 1. colNameList: List of column names * a->x->b->y @@ -388,7 +388,7 @@ typedef struct MongoFdwRelationInfo * To avoid duplicate entry of columns, we use a hash table having a unique * hash key as a set of varno and varattno. */ -typedef struct MongoJoinQualInfo +typedef struct MongoRelQualInfo { PlannerInfo *root; /* global planner state */ RelOptInfo *foreignRel; /* the foreign relation we are planning for */ @@ -397,8 +397,8 @@ typedef struct MongoJoinQualInfo List *colNumList; List *rtiList; List *isOuterList; - struct HTAB *joinExprColHash; -} MongoJoinQualInfo; + struct HTAB *exprColHash; +} MongoRelQualInfo; typedef struct ColumnHashKey { @@ -432,7 +432,7 @@ 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, MongoJoinQualInfo *jqinfo); +extern void mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info); extern const char *mongo_get_jointype_name(JoinType jointype); #endif /* MONGO_FDW_H */ diff --git a/mongo_query.c b/mongo_query.c index 3f6b084..adf9822 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -97,8 +97,6 @@ static HTAB *column_info_hash(List *colname_list, List *colnum_list, List *rti_list, List *isouter_list); static void mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, - List *colname_list, - List *isouter_list, pipeline_cxt *context); static void mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, @@ -492,7 +490,7 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Form equivalent join qual clauses in MongoDB */ mongo_prepare_inner_pipeline(joinclauses, &inner_pipeline, - colname_list, isouter_list, &context); + &context); bsonAppendFinishArray(inner_pipeline_doc, &inner_pipeline); } @@ -525,11 +523,6 @@ mongo_query_document(ForeignScanState *scanStateNode) bsonAppendFinishObject(&unwind_stage, &unwind); bsonAppendFinishObject(&root_pipeline, &unwind_stage); - if (!bsonFinish(queryDocument)) - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); - fmstate->outerRelName = outer_relname; } else @@ -1417,9 +1410,12 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, if (!foreign_expr_walker((Node *) expression, &glob_cxt, &loc_cxt)) return false; - /* Expressions examined here should be boolean, i.e. noncollatable */ - Assert(loc_cxt.collation == InvalidOid); - Assert(loc_cxt.state == FDW_COLLATE_NONE); + /* + * 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; @@ -1579,7 +1575,6 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, */ static void mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, - List *colname_list, List *isouter_list, pipeline_cxt *context) { BSON *and_query_doc = bsonCreate(); From 46c37b7738c0bd8cda1e4be8c222fcdfe5a2ed59 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 14:19:22 +0530 Subject: [PATCH 185/239] Push down aggregates to remote MongoDB servers. 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. Also, aggregate filters and orders are not pushed down as MongoDB does not support them. FDW-137, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, further changes, mostly cosmetic, by me, tested by Kashif Zeeshan. --- README.md | 9 + deparse.c | 82 +++++- mongo_fdw.c | 725 ++++++++++++++++++++++++++++++++++++++++++++++---- mongo_fdw.h | 63 ++++- mongo_query.c | 455 +++++++++++++++++++++++++++++-- mongo_query.h | 52 ++-- 6 files changed, 1289 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 448c9f6..8044136 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,15 @@ 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. + ### New MongoDB C Driver Support This enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But diff --git a/deparse.c b/deparse.c index 74cc2b2..5591119 100644 --- a/deparse.c +++ b/deparse.c @@ -91,6 +91,66 @@ mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info) 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 */ @@ -147,7 +207,8 @@ mongo_check_op_expr(OpExpr *node, MongoRelQualInfo *qual_info) /* * mongo_check_var * Check the given Var and append required information related to columns - * involved in qual clauses to separate lists in context. + * 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 @@ -187,6 +248,25 @@ mongo_check_var(Var *column, MongoRelQualInfo *qual_info) 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 8c44dbb..801391d 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -51,6 +51,7 @@ #include "utils/jsonfuncs.h" #endif #include "utils/rel.h" +#include "utils/selfuncs.h" #include "utils/syscache.h" /* Declarations for dynamic loading */ @@ -138,6 +139,18 @@ static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); +#if PG_VERSION_NUM >= 110000 +static void mongoGetForeignUpperPaths(PlannerInfo *root, + UpperRelationKind stage, + RelOptInfo *input_rel, + RelOptInfo *output_rel, + void *extra); +#else +static void mongoGetForeignUpperPaths(PlannerInfo *root, + UpperRelationKind stage, + RelOptInfo *input_rel, + RelOptInfo *output_rel); +#endif #endif /* @@ -146,13 +159,13 @@ static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, static double foreign_table_document_count(Oid foreignTableId); static HTAB *column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, List *colIsInnerList, - bool isJoin); + uint32 relType); static void fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, bool *columnNulls, - bool isJoin); + 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, @@ -172,6 +185,21 @@ static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *innerrel, JoinPathExtraData *extra); static void mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info); +#if PG_VERSION_NUM >= 110000 +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); +#else +static bool mongo_foreign_grouping_ok(PlannerInfo *root, + RelOptInfo *grouped_rel); +static void mongo_add_foreign_grouping_paths(PlannerInfo *root, + RelOptInfo *input_rel, + RelOptInfo *grouped_rel); +#endif #endif #ifndef META_DRIVER static const char *escape_json_string(const char *string); @@ -256,6 +284,9 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) #ifdef META_DRIVER /* Support function for join push-down */ fdwRoutine->GetForeignJoinPaths = mongoGetForeignJoinPaths; + + /* Support functions for upper relation push-down */ + fdwRoutine->GetForeignUpperPaths = mongoGetForeignUpperPaths; #endif PG_RETURN_POINTER(fdwRoutine); @@ -309,7 +340,7 @@ mongoGetForeignRelSize(PlannerInfo *root, RestrictInfo *ri = (RestrictInfo *) lfirst(lc); if (IsA(ri->clause, OpExpr) && - mongo_is_foreign_expr(root, baserel, ri->clause, false)) + mongo_is_foreign_expr(root, baserel, ri->clause, false, false)) fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri); else fpinfo->local_conds = lappend(fpinfo->local_conds, ri); @@ -506,8 +537,11 @@ mongoGetForeignPlan(PlannerInfo *root, List *fdw_scan_tlist = NIL; List *column_name_list = NIL; List *is_inner_column_list = NIL; + List *quals = NIL; + MongoFdwRelType mongofdwreltype; #ifdef META_DRIVER MongoRelQualInfo *qual_info; + MongoFdwRelationInfo *ofpinfo; #endif /* Set scan relation id */ @@ -515,7 +549,7 @@ mongoGetForeignPlan(PlannerInfo *root, scan_relid = foreignrel->relid; else { - /* Join relation - set scan_relid to 0. */ + /* Join/Upper relation - set scan_relid to 0. */ scan_relid = 0; Assert(!restrictionClauses); @@ -539,8 +573,12 @@ mongoGetForeignPlan(PlannerInfo *root, } } - scan_var_list = pull_var_clause((Node *) foreignrel->reltarget->exprs, - PVC_RECURSE_PLACEHOLDERS); + 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) @@ -587,7 +625,8 @@ mongoGetForeignPlan(PlannerInfo *root, 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)) + mongo_is_foreign_expr(root, foreignrel, rinfo->clause, false, + false)) remote_exprs = lappend(remote_exprs, rinfo->clause); else local_exprs = lappend(local_exprs, rinfo->clause); @@ -595,9 +634,14 @@ mongoGetForeignPlan(PlannerInfo *root, /* Add local expression Var nodes to scan_var_list. */ scan_var_list = list_concat_unique(NIL, scan_var_list); - scan_var_list = list_concat_unique(scan_var_list, - pull_var_clause((Node *) local_exprs, - PVC_RECURSE_PLACEHOLDERS)); + 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)) { @@ -655,63 +699,162 @@ mongoGetForeignPlan(PlannerInfo *root, 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; + #ifdef META_DRIVER /* - * Prepare separate lists of column names, varno, varattno, and whether it - * is part of the outer relation or not. This information would be useful + * 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)) + if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) { + ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; + /* * We use MongoRelQualInfo to pass various information related to - * joining quals to fdw_private which is used to form equivalent - * MongoDB query during the execution phase. + * 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->outerRelids = fpinfo->outerrel->relids; + + /* + * 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 + { + /* For baserel */ + Assert(mongofdwreltype == JOIN_REL); + qual_info->foreignRel = foreignrel; + qual_info->outerRelids = fpinfo->outerrel->relids; + } qual_info->exprColHash = NULL; - /* Initialize all lists */ 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; /* * Extract required data of columns involved in join clauses and append * it into the various lists required to pass it to the executor. - * Also, save join type in the list. + * + * Check and extract data for outer relation and its join clauses in + * case of aggregation on top of the join operation. */ - if (fpinfo->joinclauses) + 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 (fpinfo->remote_conds) + 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; + /* Destroy hash table used to get unique column info */ hash_destroy(qual_info->exprColHash); } + else #endif + quals = remote_exprs; /* * 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, remote_exprs); + fdw_private = list_make2(columnList, quals); + + /* Append relation type */ + fdw_private = lappend(fdw_private, makeInteger(mongofdwreltype)); #ifdef META_DRIVER /* @@ -721,21 +864,38 @@ mongoGetForeignPlan(PlannerInfo *root, * information required to form a MongoDB query in the planning state and * passing it to the execution state through fdw_private. */ - if (IS_JOIN_REL(foreignrel)) + if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) { - 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); - fdw_private = lappend(fdw_private, fpinfo->joinclauses); 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, 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, - list_make2(makeString(fpinfo->inner_relname), - makeString(fpinfo->outer_relname))); - fdw_private = lappend(fdw_private, makeInteger(fpinfo->jointype)); + 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) + { + fdw_private = lappend(fdw_private, + list_make2(makeString(fpinfo->inner_relname), + makeString(fpinfo->outer_relname))); + fdw_private = lappend(fdw_private, fpinfo->joinclauses); + fdw_private = lappend(fdw_private, makeInteger(fpinfo->jointype)); + } + else if (mongofdwreltype == UPPER_JOIN_REL) + { + fdw_private = lappend(fdw_private, + list_make2(makeString(ofpinfo->inner_relname), + makeString(ofpinfo->outer_relname))); + fdw_private = lappend(fdw_private, ofpinfo->joinclauses); + fdw_private = lappend(fdw_private, makeInteger(ofpinfo->jointype)); + } } #endif @@ -858,15 +1018,10 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) * member RTE as a representative; we would get the same result from any. */ if (fsplan->scan.scanrelid > 0) - { rtindex = fsplan->scan.scanrelid; - fmstate->isJoinRel = false; - } else - { rtindex = bms_next_member(fsplan->fs_relids, -1); - fmstate->isJoinRel = true; - } + rte = rt_fetch(rtindex, estate->es_range_table); userid = rte->checkAsUser ? rte->checkAsUser : GetUserId(); @@ -885,8 +1040,9 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) mongoConnection = mongo_get_connection(server, user, options); columnList = list_nth(fdw_private, mongoFdwPrivateColumnList); + fmstate->relType = intVal(list_nth(fdw_private, mongoFdwPrivateRelType)); - if (fmstate->isJoinRel == true) + if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { colNameList = list_nth(fdw_private, mongoFdwPrivateColNameList); colIsInnerList = list_nth(fdw_private, mongoFdwPrivateColIsInnerList); @@ -894,7 +1050,7 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) columnMappingHash = column_mapping_hash(rte->relid, columnList, colNameList, colIsInnerList, - fmstate->isJoinRel); + fmstate->relType); /* Create and set foreign execution state */ fmstate->columnMappingHash = columnMappingHash; @@ -943,7 +1099,8 @@ mongoIterateForeignScan(ForeignScanState *node) * Decide input collection to the aggregation. In case of join, outer * relation should be given as input collection to the aggregation. */ - if (fmstate->isJoinRel) + if (fmstate->relType == JOIN_REL || + fmstate->relType == UPPER_JOIN_REL) collectionName = fmstate->outerRelName; else collectionName = fmstate->options->collectionName; @@ -975,7 +1132,7 @@ mongoIterateForeignScan(ForeignScanState *node) const char *bsonDocumentKey = NULL; /* Top level document */ fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash, - columnValues, columnNulls, fmstate->isJoinRel); + columnValues, columnNulls, fmstate->relType); ExecStoreVirtualTuple(tupleSlot); } @@ -1586,13 +1743,14 @@ foreign_table_document_count(Oid foreignTableId) */ static HTAB * column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, - List *colIsInnerList, bool isJoin) + 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; @@ -1613,7 +1771,7 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, bool handleFound = false; void *hashKey; - if (isJoin) + if (relType == JOIN_REL) { int is_innerrel = list_nth_int(colIsInnerList, listIndex); @@ -1642,6 +1800,44 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, 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) + { +#if PG_VERSION_NUM < 110000 + columnName = get_relid_attribute_name(foreignTableId, + column->varattno); +#else + columnName = get_attname(foreignTableId, column->varattno, + false); +#endif + } + 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 { #if PG_VERSION_NUM < 110000 @@ -1661,10 +1857,10 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, /* * Save attribute number of the current column in the resulting tuple. - * For join relation, it is continuously increasing integers starting - * from 0, and for simple relation, it's varattno. + * For join/upper relation, it is continuously increasing integers + * starting from 0, and for simple relation, it's varattno. */ - if(isJoin) + if (relType != BASE_REL) { columnMapping->columnIndex = attnum; attnum++; @@ -1673,9 +1869,21 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, columnMapping->columnIndex = column->varattno - 1; /* Save other information */ - columnMapping->columnTypeId = column->vartype; - columnMapping->columnTypeMod = column->vartypmod; - columnMapping->columnArrayTypeId = get_element_type(column->vartype); + 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; @@ -1694,7 +1902,7 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, static void fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, HTAB *columnMappingHash, Datum *columnValues, - bool *columnNulls, bool isJoin) + bool *columnNulls, uint32 relType) { ColumnMapping *columnMapping; bool handleFound = false; @@ -1779,6 +1987,10 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, const char *bsonFullKey; void *hashKey; int32 attnum; + bool is_agg = false; + + if (!strncmp(bsonKey, "AGG_RESULT_KEY", 5) && bsonType == BSON_TYPE_INT32) + is_agg = true; columnMapping = NULL; if (bsonDocumentKey != NULL) @@ -1817,17 +2029,17 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, bsonIterSubObject(&bsonIterator, &subObject); fill_tuple_slot(&subObject, bsonFullKey, columnMappingHash, - columnValues, columnNulls, isJoin); + columnValues, columnNulls, relType); continue; } } /* If no corresponding column or null BSON value, continue */ - if (columnMapping == NULL || bsonType == BSON_TYPE_NULL) + if (!is_agg && (columnMapping == NULL || bsonType == BSON_TYPE_NULL)) continue; /* Check if columns have compatible types */ - if (OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY) + if ((OidIsValid(columnArrayTypeId) && bsonType == BSON_TYPE_ARRAY)) compatibleTypes = true; else compatibleTypes = column_types_compatible(bsonType, columnTypeId); @@ -1836,7 +2048,8 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, if (!compatibleTypes) continue; - attnum = columnMapping->columnIndex; + if (columnMapping != NULL) + attnum = columnMapping->columnIndex; /* Fill in corresponding column value and null flag */ if (OidIsValid(columnArrayTypeId)) @@ -2567,7 +2780,7 @@ mongo_acquire_sample_rows(Relation relation, mongoCursor = mongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); columnMappingHash = column_mapping_hash(foreignTableId, columnList, NIL, - NIL, false); + NIL, BASE_REL); /* * Use per-tuple memory context to prevent leak of memory used to read @@ -2610,7 +2823,7 @@ mongo_acquire_sample_rows(Relation relation, MemoryContextSwitchTo(tupleContext); fill_tuple_slot(bsonDocument, bsonDocumentKey, columnMappingHash, - columnValues, columnNulls, false); + columnValues, columnNulls, BASE_REL); MemoryContextSwitchTo(oldContext); } @@ -2951,7 +3164,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, bool is_remote_clause = mongo_is_foreign_expr(root, joinrel, rinfo->clause, - true); + true, + false); if (IS_OUTER_JOIN(jointype) && !RINFO_IS_PUSHED_DOWN(rinfo, joinrel->relids)) @@ -3061,7 +3275,7 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, /* * mongo_prepare_qual_info - * Gather information of columns involved in the join quals by extracting + * Gather information of columns involved in the quals by extracting * clause from each qual and process it further using mongo_check_qual(). */ static void @@ -3084,6 +3298,401 @@ mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info) 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. + */ +#if PG_VERSION_NUM >= 110000 +static bool +mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, + Node *havingQual) +#else +static bool +mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) +#endif +{ + Query *query = root->parse; +#if PG_VERSION_NUM >= 110000 + PathTarget *grouping_target = grouped_rel->reltarget; +#else + PathTarget *grouping_target = root->upper_targets[UPPERREL_GROUP_AGG]; +#endif + MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) grouped_rel->fdw_private; + MongoFdwRelationInfo *ofpinfo; + ListCell *lc; + int i; + List *tlist = NIL; + + /* Grouping Sets are not pushable */ + if (query->groupingSets) + return false; + + /* Get the fpinfo of the underlying scan relation. */ + ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; + + /* + * 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, 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, 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, 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 = (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 PG_VERSION_NUM >= 110000 + if (havingQual) + { + ListCell *lc; + + foreach(lc, (List *) havingQual) +#else + if (root->hasHavingQual && query->havingQual) + { + ListCell *lc; + foreach(lc, (List *) query->havingQual) +#endif + { + 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 >= 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, false, 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; + ListCell *lc; + + 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, true, + 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. + * + * Right now, we only support aggregate, grouping and having clause pushdown. + */ +#if PG_VERSION_NUM >= 110000 +static void +mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, + RelOptInfo *input_rel, RelOptInfo *output_rel, + void *extra) +#else +static void +mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, + RelOptInfo *input_rel, RelOptInfo *output_rel) +#endif +{ + 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 || output_rel->fdw_private) + return; + + fpinfo = (MongoFdwRelationInfo *) palloc0(sizeof(MongoFdwRelationInfo)); + fpinfo->pushdown_safe = false; + output_rel->fdw_private = fpinfo; + +#if PG_VERSION_NUM >= 110000 + mongo_add_foreign_grouping_paths(root, input_rel, output_rel, + (GroupPathExtraData *) extra); +#else + mongo_add_foreign_grouping_paths(root, input_rel, output_rel); +#endif +} + +/* + * 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. + */ +#if PG_VERSION_NUM >= 110000 +static void +mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, + RelOptInfo *grouped_rel, + GroupPathExtraData *extra) +#else +static void +mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, + RelOptInfo *grouped_rel) +#endif +{ + 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; + + /* Assess if it is safe to push down aggregation and grouping. */ +#if PG_VERSION_NUM >= 110000 + if (!mongo_foreign_grouping_ok(root, grouped_rel, extra->havingQual)) +#else + if (!mongo_foreign_grouping_ok(root, grouped_rel)) +#endif + return; + + /* + * 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 >= 120000 + 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 */ +#elif PG_VERSION_NUM >= 110000 + grouppath = create_foreignscan_path(root, + grouped_rel, + grouped_rel->reltarget, + num_groups, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + grouped_rel->lateral_relids, + NULL, + NIL); /* no fdw_private */ +#else + grouppath = create_foreignscan_path(root, + grouped_rel, + root->upper_targets[UPPERREL_GROUP_AGG], + num_groups, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + grouped_rel->lateral_relids, + NULL, + NIL); /* no fdw_private */ +#endif + + /* Add generated path into grouped_rel by add_path(). */ + add_path(grouped_rel, (Path *) grouppath); +} #endif /* End of META_DRIVER */ /* diff --git a/mongo_fdw.h b/mongo_fdw.h index d37e722..f129494 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -179,6 +179,9 @@ #define mongo_list_concat(l1, l2) list_concat((l1), (l2)) #endif +/* 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 @@ -289,8 +292,9 @@ typedef struct MongoFdwModifyState MongoFdwOptions *options; AttrNumber rowidAttno; /* attnum of resjunk rowid column */ - /* Join information */ - bool isJoinRel; /* Is this JOIN operation? */ + /* Join/Upper relation information */ + uint32 relType; /* relation type. Base, Join, Upper, or + * Upper on join */ char *outerRelName; /* Outer relation name */ } MongoFdwModifyState; @@ -348,12 +352,18 @@ typedef struct MongoFdwRelationInfo char *outer_relname; MongoFdwOptions *options; /* Options applicable for this relation */ + + /* Grouping information */ + List *grouped_tlist; + List *groupbyColList; } MongoFdwRelationInfo; /* * MongoRelQualInfo contains column name, varno, varattno, and its relation - * name of columns involved in the join quals which is passed to execution - * state through fdw_private. + * 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 @@ -362,6 +372,7 @@ typedef struct MongoFdwRelationInfo * 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) @@ -387,6 +398,26 @@ typedef struct MongoFdwRelationInfo * * 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 { @@ -398,6 +429,12 @@ typedef struct MongoRelQualInfo 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 @@ -406,6 +443,19 @@ typedef struct ColumnHashKey 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); @@ -425,7 +475,10 @@ 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_join_cond); + Expr *expression, bool is_join_cond, + 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); diff --git a/mongo_query.c b/mongo_query.c index adf9822..c6dede3 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -60,6 +60,7 @@ typedef struct foreign_glob_cxt Relids relids; /* relids of base relations in the underlying * scan */ bool is_join_cond; /* "true" for join relations */ + bool is_having_cond; /* "true" for HAVING clause condition */ } foreign_glob_cxt; /* @@ -134,9 +135,10 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) /* * mongo_query_document - * Takes in the applicable operator expressions for relation and also the - * join clauses for join relation and converts these expressions and join - * clauses into equivalent queries in MongoDB. + * 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. @@ -200,6 +202,30 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * expressions in a BSON document. For example, simple expressions: * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" becomes * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". + * + * 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'; + * + * Equivalent MongoDB query: + * + * db.t1.aggregate([ + * { + * "$group": + * { + * "_id": {"name": "$name"}, + * "v_agg0": {"$sum": "$age"}, + * "v_having": {"$min": "$name"} + * } + * }, + * { + * "$match": {"v_having": "xyz"} + * } + * ]) */ BSON * mongo_query_document(ForeignScanState *scanStateNode) @@ -225,12 +251,9 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Prepare array of stages */ bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); - if (fmstate->isJoinRel) + if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { - List *colnum_list; - List *rti_list; List *innerouter_relname; - int natts; joinclauses = list_nth(PrivateList, mongoFdwPrivateJoinClauseList); if (joinclauses) @@ -240,6 +263,14 @@ mongo_query_document(ForeignScanState *scanStateNode) mongoFdwPrivateJoinInnerOuterRelName); inner_relname = strVal(list_nth(innerouter_relname, 0)); outer_relname = strVal(list_nth(innerouter_relname, 1)); + } + + if (fmstate->relType != BASE_REL) + { + List *colnum_list; + List *rti_list; + int natts; + colname_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseColNameList); colnum_list = list_nth(PrivateList, @@ -433,7 +464,7 @@ mongo_query_document(ForeignScanState *scanStateNode) } #ifdef META_DRIVER - if (fmstate->isJoinRel) + if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { BSON inner_pipeline; BSON lookup_object; @@ -463,7 +494,13 @@ mongo_query_document(ForeignScanState *scanStateNode) char *colname = strVal(lfirst(cell1)); bool is_outer = lfirst_int(cell2); - if (is_outer) + /* + * 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) { /* * Add prefix "v_" to column name to form variable name. Need @@ -536,6 +573,219 @@ mongo_query_document(ForeignScanState *scanStateNode) 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) + { + BSON match_stage; + BSON *filter = bsonCreate(); + List *equalityOperatorList; + List *comparisonOperatorList; + ListCell *equalityOperatorCell; + ListCell *comparisonoperatorCell; + + /* $match stage. Add a filter for the HAVING clause */ + bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), + &match_stage); + + equalityOperatorList = equality_operator_list(having_expr); + comparisonOperatorList = list_difference(having_expr, + equalityOperatorList); + /* Append equality expressions to the query */ + foreach(equalityOperatorCell, equalityOperatorList) + { + OpExpr *equalityOperator; + Const *constant; + List *argumentList; + + equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); + argumentList = equalityOperator->args; + constant = (Const *) find_argument_of_type(argumentList, + T_Const); + + if (constant != NULL) + append_constant_value(filter, "v_having", constant); + } + + foreach(comparisonoperatorCell, comparisonOperatorList) + { + BSON childDocument; + OpExpr *operator; + List *argumentList; + Const *constant; + char *operatorName; + char *mongoOperatorName; + + /* For comparison expressions, start a sub-document */ + bsonAppendStartObject(filter, "v_having", &childDocument); + + operator = (OpExpr *) lfirst(comparisonoperatorCell); + argumentList = operator->args; + constant = (Const *) find_argument_of_type(argumentList, + T_Const); + operatorName = get_opname(operator->opno); + mongoOperatorName = mongo_operator_name(operatorName); +#ifdef META_DRIVER + append_constant_value(&childDocument, mongoOperatorName, + constant); +#else + append_constant_value(filter, mongoOperatorName, constant); +#endif + bsonAppendFinishObject(filter, &childDocument); + } + + bsonAppendBson(&match_stage, "$match", filter); + bsonAppendFinishObject(&root_pipeline, &match_stage); + + if (!bsonFinish(filter)) + { +#ifdef META_DRIVER + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON flags: %d", queryDocument->flags))); +#else + ereport(ERROR, + (errmsg("could not create document for query"), + errhint("BSON error: %d", queryDocument->err))); +#endif + } + } + } + bsonAppendFinishArray(queryDocument, &root_pipeline); if (!bsonFinish(queryDocument)) @@ -1008,6 +1258,20 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, { List *columnList = NIL; ListCell *lc; + RelOptInfo *scanrel; +#if PG_VERSION_NUM >= 100000 + MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; + MongoFdwRelationInfo *ofpinfo; +#endif + +#if PG_VERSION_NUM >= 100000 + scanrel = IS_UPPER_REL(foreignrel) ? fpinfo->outerrel : foreignrel; +#else + scanrel = foreignrel; +#endif + + if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel)) + ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; foreach(lc, scan_var_list) { @@ -1015,10 +1279,21 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, RangeTblEntry *rte = planner_rt_fetch(var->varno, root); int is_innerrel = false; - Assert(IsA(var, Var)); + /* + * Add aggregation target also in the needed column list. This would + * be handled in the function column_mapping_hash. + */ + if (IsA(var, Aggref)) + { + columnList = list_append_unique(columnList, var); + continue; + } + + if (!IsA(var, Var)) + continue; /* Var belongs to foreign table? */ - if (!bms_is_member(var->varno, foreignrel->relids)) + if (!bms_is_member(var->varno, scanrel->relids)) continue; /* Is whole-row reference requested? */ @@ -1045,7 +1320,8 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, else columnList = list_append_unique(columnList, var); - if (IS_JOIN_REL(foreignrel)) + if (IS_JOIN_REL(foreignrel) || + (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel))) { MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; char *columnName; @@ -1057,7 +1333,11 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, #endif *column_name_list = lappend(*column_name_list, makeString(columnName)); - if (bms_is_member(var->varno, fpinfo->innerrel->relids)) + 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, @@ -1209,6 +1489,11 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, 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; + /* Increment the operator expression count */ glob_cxt->opexprcount++; @@ -1326,6 +1611,94 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, state = FDW_COLLATE_NONE; } break; +#ifdef META_DRIVER + 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 (!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; +#endif default: /* @@ -1390,10 +1763,11 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, */ bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, - bool is_join_cond) + bool is_join_cond, 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 @@ -1401,10 +1775,21 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, */ glob_cxt.root = root; glob_cxt.foreignrel = baserel; - glob_cxt.relids = baserel->relids; + + /* + * 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.varcount = 0; glob_cxt.opexprcount = 0; glob_cxt.is_join_cond = is_join_cond; + 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)) @@ -1633,4 +2018,44 @@ mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, 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; +} #endif diff --git a/mongo_query.h b/mongo_query.h index daeb0e3..a5adf11 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -63,8 +63,39 @@ enum mongoFdwScanPrivateIndex /* Expressions to execute remotely */ mongoFdwPrivateRemoteExprList, + /* Relation Type (BASE/JOIN/UPPER/UPPER_JOIN) */ + mongoFdwPrivateRelType, + /* Join information */ + /* + * 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, + + /* 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 @@ -77,29 +108,14 @@ enum mongoFdwScanPrivateIndex * means those are part of target and restriction columns. */ mongoFdwPrivateColNameList, - mongoFdwPrivateColIsInnerList, - /* List of join clauses to form a pipeline */ - mongoFdwPrivateJoinClauseList, - - /* - * 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, - /* Inner and Outer relation names */ mongoFdwPrivateJoinInnerOuterRelName, + /* List of join clauses to form a pipeline */ + mongoFdwPrivateJoinClauseList, + /* Join-type */ mongoFdwPrivateJoinType }; From fd37f1b8f27c705a0cfcf956a806ddd12713ff06 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 14:19:22 +0530 Subject: [PATCH 186/239] Add test coverage for the aggregate push-down. Add new test file aggregate_pushdown.sql for testing aggregate push-down feature. Add multiple alternate output files to adjust with different outputs across server versions. FDW-137, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, tested by Kashif Zeeshan. --- Makefile | 2 +- Makefile.meta | 2 +- expected/aggregate_pushdown.out | 1148 ++++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 1148 ++++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 1153 +++++++++++++++++++++++++++++ sql/aggregate_pushdown.sql | 267 +++++++ 6 files changed, 3718 insertions(+), 2 deletions(-) create mode 100644 expected/aggregate_pushdown.out create mode 100644 expected/aggregate_pushdown_1.out create mode 100644 expected/aggregate_pushdown_2.out create mode 100644 sql/aggregate_pushdown.sql diff --git a/Makefile b/Makefile index 3812c49..005dcce 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o depa 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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown aggregate_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/Makefile.meta b/Makefile.meta index da7cead..8bcc3f3 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o depa 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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown aggregate_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out new file mode 100644 index 0000000..e24caa0 --- /dev/null +++ b/expected/aggregate_pushdown.out @@ -0,0 +1,1148 @@ +\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 'edb' user with 'edb' 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 +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, 2; + 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(*)), (sum(fdw137_t1.c1)) + -> 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, 2; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c1, (sum(c1)), (count(*)) + Sort Key: fdw137_t1.c1 + -> Foreign Scan + Output: c1, (sum(c1)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c8, (min(c2)) + Sort Key: fdw137_t1.c8 + -> Foreign Scan + Output: c8, (min(c2)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c2, (sum(c1)), c1 + Sort Key: fdw137_t1.c2 + -> 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; + 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) + +-- 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; + QUERY PLAN +-------------------------------------------------------------- + Sort + Output: c1, (sum((c1 + 2))) + Sort Key: fdw137_t1.c1 + -> HashAggregate + 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 +(10 rows) + +SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (max(fdw137_t1.c4)) + -> HashAggregate + Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + max +------ + 400 + 600 + 700 + 800 + 900 + 1300 + +(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) + +-- 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 > 10 ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: (sum(t1.c8)), (avg(t2.c1)) + Sort Key: (sum(t1.c8)) + -> 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 > 10 ORDER BY 1; + sum | avg +-----+------------------ + 280 | 25.4545454545455 +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.c1, (count(*)), t2.c4 + Sort Key: t1.c1, t2.c4 + -> 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)) +(6 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, 3; + c1 | count | c4 +----+-------+------ + 10 | 1 | 700 + 10 | 1 | 900 + 10 | 1 | + 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Sort Key: t1.c8 + -> Foreign Scan + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) +(7 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; + sum | c8 | avg +-----+----+----- + 100 | 20 | 20 + 180 | 30 | 30 + 0 | 60 | 60 +(3 rows) + +-- 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)) + -> HashAggregate + Output: q.b, count(fdw137_t1.c1), sum(q.a) + Group 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 + -> Foreign Scan + Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) +(20 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; +-- 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 TABLE fprt1; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out new file mode 100644 index 0000000..6901b49 --- /dev/null +++ b/expected/aggregate_pushdown_1.out @@ -0,0 +1,1148 @@ +\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 'edb' user with 'edb' 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 +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, 2; + 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(*)), (sum(fdw137_t1.c1)) + -> 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, 2; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c1, (sum(c1)), (count(*)) + Sort Key: fdw137_t1.c1 + -> Foreign Scan + Output: c1, (sum(c1)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c8, (min(c2)) + Sort Key: fdw137_t1.c8 + -> Foreign Scan + Output: c8, (min(c2)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c2, (sum(c1)), c1 + Sort Key: fdw137_t1.c2 + -> 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; + 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) + +-- 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; + QUERY PLAN +-------------------------------------------------------------- + Sort + Output: c1, (sum((c1 + 2))) + Sort Key: fdw137_t1.c1 + -> HashAggregate + 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 +(10 rows) + +SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (max(fdw137_t1.c4)) + -> HashAggregate + Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + max +------ + 400 + 600 + 700 + 800 + 900 + 1300 + +(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) + +-- 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 > 10 ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: (sum(t1.c8)), (avg(t2.c1)) + Sort Key: (sum(t1.c8)) + -> 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 > 10 ORDER BY 1; + sum | avg +-----+------------------ + 280 | 25.4545454545455 +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.c1, (count(*)), t2.c4 + Sort Key: t1.c1, t2.c4 + -> 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)) +(6 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, 3; + c1 | count | c4 +----+-------+------ + 10 | 1 | 700 + 10 | 1 | 900 + 10 | 1 | + 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Sort Key: t1.c8 + -> Foreign Scan + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) +(7 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; + sum | c8 | avg +-----+----+----- + 100 | 20 | 20 + 180 | 30 | 30 + 0 | 60 | 60 +(3 rows) + +-- 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)) + -> HashAggregate + Output: q.b, count(fdw137_t1.c1), sum(q.a) + Group 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 + -> Foreign Scan + Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) +(20 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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) + Sort Key: (sum(ftprt1_p1.c1)) + -> Append + -> Foreign Scan + Output: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) + -> Foreign Scan + Output: ftprt1_p2.c1, (sum(ftprt1_p2.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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) + Sort Key: (sum(ftprt1_p1.c2)) + -> Append + -> Foreign Scan + Output: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) + -> Foreign Scan + Output: ftprt1_p2.c1, (sum(ftprt1_p2.c2)), (min(ftprt1_p2.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; +-- 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 TABLE fprt1; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out new file mode 100644 index 0000000..74a0bbb --- /dev/null +++ b/expected/aggregate_pushdown_2.out @@ -0,0 +1,1153 @@ +\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 'edb' user with 'edb' 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 +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, 2; + 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(*)), (sum(fdw137_t1.c1)) + -> 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, 2; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c1, (sum(c1)), (count(*)) + Sort Key: fdw137_t1.c1 + -> Foreign Scan + Output: c1, (sum(c1)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c8, (min(c2)) + Sort Key: fdw137_t1.c8 + -> Foreign Scan + Output: c8, (min(c2)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 rows) + +SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; + 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; + QUERY PLAN +--------------------------------------------------------------------------------- + Sort + Output: c2, (sum(c1)), c1 + Sort Key: fdw137_t1.c2 + -> 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; + 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) + +-- 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; + QUERY PLAN +-------------------------------------------------------------- + Sort + Output: c1, (sum((c1 + 2))) + Sort Key: fdw137_t1.c1 + -> HashAggregate + 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 +(10 rows) + +SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (max(fdw137_t1.c4)) + -> HashAggregate + Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + max +------ + 400 + 600 + 700 + 800 + 900 + 1300 + +(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) + +-- 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 > 10 ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: (sum(t1.c8)), (avg(t2.c1)) + Sort Key: (sum(t1.c8)) + -> 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 > 10 ORDER BY 1; + sum | avg +-----+------------------ + 280 | 25.4545454545455 +(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, 3; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Sort + Output: t1.c1, (count(*)), t2.c4 + Sort Key: t1.c1, t2.c4 + -> 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)) +(6 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, 3; + c1 | count | c4 +----+-------+------ + 10 | 1 | 700 + 10 | 1 | 900 + 10 | 1 | + 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Sort Key: t1.c8 + -> Foreign Scan + Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) + Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) +(7 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; + sum | c8 | avg +-----+----+----- + 100 | 20 | 20 + 180 | 30 | 30 + 0 | 60 | 60 +(3 rows) + +-- 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)) + -> HashAggregate + Output: q.b, count(fdw137_t1.c1), sum(q.a) + Group 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 + -> Foreign Scan + Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) +(20 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; +ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" +-- 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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) + Sort Key: (sum(ftprt1_p1.c1)) + -> HashAggregate + Output: ftprt1_p1.c1, sum(ftprt1_p1.c1) + Group Key: ftprt1_p1.c1 + -> Append + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1 + Foreign Namespace: mongo_fdw_regress.test2 +(13 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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) + Sort Key: (sum(ftprt1_p1.c2)) + -> HashAggregate + Output: ftprt1_p1.c1, sum(ftprt1_p1.c2), min(ftprt1_p1.c2), count(*) + Group Key: ftprt1_p1.c1 + Filter: (avg(ftprt1_p1.c2) < '22'::numeric) + -> Append + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1, ftprt1_p1.c2 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1, ftprt1_p2.c2 + Foreign Namespace: mongo_fdw_regress.test2 +(14 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 + -> HashAggregate + Output: t1.c1, count(((t1.*)::fprt1)) + Group Key: t1.c1 + Filter: (avg(t1.c2) < '22'::numeric) + -> Append + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1, t1.*, t1.c2 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1, t1_1.*, t1_1.c2 + Foreign Namespace: mongo_fdw_regress.test2 +(14 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; +ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" +-- 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 TABLE fprt1; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql new file mode 100644 index 0000000..d99e7c3 --- /dev/null +++ b/sql/aggregate_pushdown.sql @@ -0,0 +1,267 @@ +\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 'edb' user with 'edb' 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 +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, 2; +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, 2; + +-- 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; +SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; +SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; + +-- 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; +SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1; + +-- 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; +SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; + +-- Aggregate with unshippable GROUP BY clause are not pushed +EXPLAIN (VERBOSE, COSTS OFF) +SELECT max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; +SELECT max(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; + + +-- 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 > 10 ORDER BY 1; +SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 ORDER BY 1; +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, 3; +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, 3; +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; +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; + +-- 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; + +-- 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 TABLE fprt1; +DROP USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; From 90ea7dcca36bf5e0713b028b639846803ad38a5d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 14:19:22 +0530 Subject: [PATCH 187/239] Add enable_aggregate_pushdown option to control aggregate push-down. Considering the complexity of generating the aggregation pipeline, add an option to control the push-down to avoid any issues. This option can be set at the server level or the table level. If any of the tables involved in the join has this option set 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. Default is true. FDW-137, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, tested by Kashif Zeeshan. --- README.md | 7 ++ expected/aggregate_pushdown.out | 149 ++++++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 149 ++++++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 149 ++++++++++++++++++++++++++++++ mongo_fdw.c | 25 ++++- mongo_fdw.h | 6 +- option.c | 8 +- sql/aggregate_pushdown.sql | 51 ++++++++++ 8 files changed, 538 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8044136..fa34f76 100644 --- a/README.md +++ b/README.md @@ -274,6 +274,11 @@ The following options are only supported with meta driver: 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. Default is `true`. + * `enable_aggregate_pushdown`: 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. Default is `true`. The following parameters can be set on a MongoDB foreign table object: @@ -283,6 +288,8 @@ The following parameters can be set on a MongoDB foreign table object: the foreign table name used in the relevant `CREATE` command. * `enable_join_pushdown`: Similar to the server-level option, but can be configured at table level as well. Default is `true`. + * `enable_aggregate_pushdown`: Similar to the server-level option, but + can be configured at table level as well. Default is `true`. The following parameters can be supplied while creating user mapping: diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index e24caa0..5978943 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -1133,6 +1133,155 @@ SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8 + Sort Key: t1.c8 + -> 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)) +(6 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 6901b49..fe097ec 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -1133,6 +1133,155 @@ SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8 + Sort Key: t1.c8 + -> 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)) +(6 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 74a0bbb..9381f6c 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -1138,6 +1138,155 @@ SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1; SET enable_partitionwise_aggregate TO OFF; ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" +-- 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; + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8 + Sort Key: t1.c8 + -> 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)) +(6 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/mongo_fdw.c b/mongo_fdw.c index 801391d..f7b9b79 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -3322,18 +3322,35 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) PathTarget *grouping_target = root->upper_targets[UPPERREL_GROUP_AGG]; #endif MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) grouped_rel->fdw_private; - MongoFdwRelationInfo *ofpinfo; + MongoFdwRelationInfo *ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; + MongoFdwRelationInfo *ofpinfo_o; + MongoFdwRelationInfo *ofpinfo_i; ListCell *lc; int i; List *tlist = NIL; + bool is_join = false; + + /* + * If the underlying scan relation is the join relation then find the + * fpinfo of each relation involved in the join. + */ + if (IS_JOIN_REL(fpinfo->outerrel)) + { + ofpinfo_o = (MongoFdwRelationInfo *) ofpinfo->outerrel->fdw_private; + ofpinfo_i = (MongoFdwRelationInfo *) ofpinfo->innerrel->fdw_private; + is_join = true; + } + + /* If aggregate pushdown is not enabled, honor it. */ + if ((!is_join && !ofpinfo->options->enable_aggregate_pushdown) || + ((is_join && !ofpinfo_o->options->enable_aggregate_pushdown) || + (is_join && !ofpinfo_i->options->enable_aggregate_pushdown))) + return false; /* Grouping Sets are not pushable */ if (query->groupingSets) return false; - /* Get the fpinfo of the underlying scan relation. */ - ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; - /* * If underneath input relation has any local conditions, those conditions * are required to be applied before performing aggregation. Hence the diff --git a/mongo_fdw.h b/mongo_fdw.h index f129494..31c4858 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -157,6 +157,7 @@ #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" #endif /* Default values for option parameters */ @@ -202,7 +203,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 19; +static const uint32 ValidOptionCount = 21; #else static const uint32 ValidOptionCount = 7; #endif @@ -225,6 +226,7 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_CRL_FILE, ForeignServerRelationId}, {OPTION_NAME_WEAK_CERT, ForeignServerRelationId}, {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignServerRelationId}, + {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignServerRelationId}, #endif /* Foreign table options */ @@ -232,6 +234,7 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_COLLECTION, ForeignTableRelationId}, #ifdef META_DRIVER {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignTableRelationId}, + {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignTableRelationId}, #endif /* User mapping options */ @@ -265,6 +268,7 @@ typedef struct MongoFdwOptions char *crl_file; bool weak_cert_validation; bool enable_join_pushdown; + bool enable_aggregate_pushdown; #endif } MongoFdwOptions; diff --git a/option.c b/option.c index 9fc6168..36d2fd6 100644 --- a/option.c +++ b/option.c @@ -109,7 +109,8 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) #ifdef META_DRIVER || 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_SSL) == 0 || + strcmp(optionName, OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN) == 0 #endif ) { @@ -186,6 +187,7 @@ mongo_get_options(Oid foreignTableId) options->ssl = false; options->weak_cert_validation = false; options->enable_join_pushdown = true; + options->enable_aggregate_pushdown = true; #endif /* Loop through the options */ @@ -227,6 +229,10 @@ mongo_get_options(Oid foreignTableId) 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 /* This is for continuation */ #endif diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index d99e7c3..30ed550 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -252,6 +252,57 @@ 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; + +-- 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; + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; From 206f0b5c1579bece62ded0ac90711295fdd60080 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 18:16:00 +0530 Subject: [PATCH 188/239] Add deprecation notice for the legacy driver in README and autogen.sh. The legacy driver is too old, and MongoDB also stopped supporting it. Given that all the new feature needs a meta driver, having a legacy driver leads to complexity. Add a warning for the same in autogen.sh if someone tries building it, and also, put a note in the README. FDW-471, Vaibhav Dalvi, reviewed by Suraj Kharage. --- README.md | 9 +++++++-- autogen.sh | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fa34f76..b57cdd9 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ 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. -Build with [MongoDB][1]'s legacy branch driver +Build with [MongoDB][1]'s legacy branch driver (Deprecated in mongo_fdw 5.4.0) * autogen.sh --with-legacy Build [MongoDB][1]'s master branch driver @@ -71,6 +71,11 @@ meta driver accordingly. For more details on installation of mongo-c driver, you can refer [here][5]. #### Legacy driver + +Deprecation Notice: +The legacy driver support has been deprecated in mongo_fdw 5.4.0 and is +expected to be removed entirely in a future release. + * Checkout, extract legacy branch ```sh @@ -119,7 +124,7 @@ For meta, cp Makefile.meta Makefile -For legacy, +For legacy (Deprecated in mongo_fdw 5.4.0), cp Makefile.legacy Makefile diff --git a/autogen.sh b/autogen.sh index 9c90288..d09ec32 100755 --- a/autogen.sh +++ b/autogen.sh @@ -112,6 +112,7 @@ function create_config cleanup if [ "--with-legacy" = $1 ]; then + echo "Warning: The legacy driver support has been deprecated in mongo_fdw 5.4.0 and is expected to be removed entirely in a future release." checkout_json_lib && checkout_legacy_branch && install_json_lib && From c8b6cce0dbece6d376ab1f1712c25e8c305e5e2e Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 18 May 2022 18:52:42 +0530 Subject: [PATCH 189/239] Run pgindent. FDW-450, Vaibhav Dalvi. --- connection.c | 8 +- deparse.c | 23 +++--- mongo_fdw.c | 178 ++++++++++++++++++++++--------------------- mongo_fdw.h | 32 ++++---- mongo_query.c | 105 ++++++++++++------------- mongo_query.h | 10 +-- mongo_wrapper.c | 4 +- mongo_wrapper.h | 100 ++++++++++++------------ mongo_wrapper_meta.c | 4 +- option.c | 6 +- 10 files changed, 242 insertions(+), 228 deletions(-) diff --git a/connection.c b/connection.c index 92168d4..09160f1 100644 --- a/connection.c +++ b/connection.c @@ -44,7 +44,7 @@ typedef struct ConnCacheEntry 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 */ + uint32 mapping_hashvalue; /* hash value of user mapping OID */ } ConnCacheEntry; /* @@ -141,11 +141,11 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, if (entry->conn != NULL) { bson_error_t error; - bool retval; - bson_t *command; + bool retval; + bson_t *command; /* Ping the database using "ping" command */ - command = BCON_NEW("ping", BCON_INT32 (1)); + command = BCON_NEW("ping", BCON_INT32(1)); retval = mongoc_client_command_simple(entry->conn, opt->svr_database, command, NULL, NULL, &error); if (!retval) diff --git a/deparse.c b/deparse.c index 5591119..6c7e622 100644 --- a/deparse.c +++ b/deparse.c @@ -89,12 +89,12 @@ mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info) mongo_check_qual(((RelabelType *) node)->arg, qual_info); break; case T_BoolExpr: - mongo_check_qual((Expr *)((BoolExpr *) node)->args, qual_info); + mongo_check_qual((Expr *) ((BoolExpr *) node)->args, qual_info); break; case T_Aggref: { ListCell *lc; - char *func_name = get_func_name(((Aggref *) node)->aggfnoid); + char *func_name = get_func_name(((Aggref *) node)->aggfnoid); /* Save aggregation operation name */ qual_info->aggTypeList = lappend(qual_info->aggTypeList, @@ -118,7 +118,7 @@ mongo_check_qual(Expr *node, MongoRelQualInfo *qual_info) * For aggregation over the column, add required information * into the column information lists. */ - if (((Aggref *)node)->aggstar) + if (((Aggref *) node)->aggstar) { qual_info->colNameList = lappend(qual_info->colNameList, makeString("*")); @@ -225,7 +225,7 @@ mongo_check_var(Var *column, MongoRelQualInfo *qual_info) if (!(bms_is_member(column->varno, qual_info->foreignRel->relids) && column->varlevelsup == 0)) - return; /* Var does not belong to foreign table */ + return; /* Var does not belong to foreign table */ Assert(!IS_SPECIAL_VARNO(column->varno)); @@ -247,7 +247,7 @@ mongo_check_var(Var *column, MongoRelQualInfo *qual_info) key.varno = column->varno; key.varattno = column->varattno; - hash_search(qual_info->exprColHash, (void *)&key, HASH_ENTER, &found); + hash_search(qual_info->exprColHash, (void *) &key, HASH_ENTER, &found); /* * Add aggregated column in the aggColList even if it's already available @@ -390,7 +390,7 @@ mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context) /* Reset to zero to be used for nested arrays */ context->arrayIndex = reset_index; - /* Save join expression type boolean "TRUE" */ + /* Save join expression type boolean "TRUE" */ context->isBoolExpr = true; foreach(lc, node->args) @@ -436,9 +436,9 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) char *mongo_operator; int saved_array_index; int reset_index = 0; - int and_index = 0; - BSON and_op; - BSON and_obj; + int and_index = 0; + BSON and_op; + BSON and_obj; /* Retrieve information about the operator from the system catalog. */ tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno)); @@ -501,7 +501,8 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) bsonAppendFinishObject(child_doc, &expr); /* - * Add equality check for null values for columns involved in join-clauses. + * Add equality check for null values for columns involved in + * join-clauses. */ foreach(arg, node->args) { @@ -574,7 +575,7 @@ mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context) static void mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context) { - BSON ne_expr; + BSON ne_expr; bool found = false; ColInfoHashKey key; ColInfoHashEntry *columnInfo; diff --git a/mongo_fdw.c b/mongo_fdw.c index f7b9b79..f56ee5e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -64,6 +64,7 @@ PG_MODULE_MAGIC; #define CODE_VERSION 50300 extern PGDLLEXPORT void _PG_init(void); + PG_FUNCTION_INFO_V1(mongo_fdw_handler); PG_FUNCTION_INFO_V1(mongo_fdw_version); @@ -172,12 +173,12 @@ 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 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); #ifdef META_DRIVER static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, @@ -206,9 +207,8 @@ static const char *escape_json_string(const char *string); static void bson_to_json_string(StringInfo output, BSON_ITERATOR iter, bool isArray); #endif -static void -mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, - Oid foreigntableid); +static void mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, + Cost *total_cost, Oid foreigntableid); /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -319,9 +319,9 @@ mongoGetForeignRelSize(PlannerInfo *root, MongoFdwRelationInfo *fpinfo; MongoFdwOptions *options; ListCell *lc; - char *relname; - char *database; - char *refname; + char *relname; + char *database; + char *refname; /* * We use MongoFdwRelationInfo to pass various information to subsequent @@ -353,8 +353,8 @@ mongoGetForeignRelSize(PlannerInfo *root, options = mongo_get_options(foreigntableid); /* - * Retrieve exact document count for remote collection if asked, otherwise, - * use default estimate in planning. + * Retrieve exact document count for remote collection if asked, + * otherwise, use default estimate in planning. */ if (options->use_remote_estimate) { @@ -381,7 +381,7 @@ mongoGetForeignRelSize(PlannerInfo *root, } relname = options->collectionName; - database = options->svr_database; + database = options->svr_database; fpinfo->base_relname = relname; /* @@ -425,12 +425,12 @@ mongoGetForeignPaths(PlannerInfo *root, options = mongo_get_options(foreigntableid); /* - * Retrieve exact document count for remote collection if asked, otherwise, - * use default estimate in planning. + * 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); + double documentCount = foreign_table_document_count(foreigntableid); if (documentCount > 0.0) { @@ -459,10 +459,10 @@ mongoGetForeignPaths(PlannerInfo *root, 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. + * 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); @@ -477,7 +477,7 @@ mongoGetForeignPaths(PlannerInfo *root, */ cpuCostPerDoc = cpu_tuple_cost; cpuCostPerRow = (cpu_tuple_cost * MONGO_TUPLE_COST_MULTIPLIER) + tupleFilterCost; - totalCpuCost = (cpuCostPerDoc * documentCount) +(cpuCostPerRow * inputRowCount); + totalCpuCost = (cpuCostPerDoc * documentCount) + (cpuCostPerRow * inputRowCount); connectionCost = MONGO_CONNECTION_COST_MULTIPLIER * seq_page_cost; startupCost = baserel->baserestrictcost.startup + connectionCost; @@ -549,7 +549,7 @@ mongoGetForeignPlan(PlannerInfo *root, scan_relid = foreignrel->relid; else { - /* Join/Upper relation - set scan_relid to 0. */ + /* Join/Upper relation - set scan_relid to 0. */ scan_relid = 0; Assert(!restrictionClauses); @@ -603,12 +603,13 @@ mongoGetForeignPlan(PlannerInfo *root, } /* - * 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. + * 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) { @@ -646,8 +647,8 @@ mongoGetForeignPlan(PlannerInfo *root, if (IS_JOIN_REL(foreignrel)) { /* - * For join relations, the planner needs a targetlist, which represents - * the output of the ForeignScan node. + * 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); @@ -675,9 +676,9 @@ mongoGetForeignPlan(PlannerInfo *root, /* * 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.) + * 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) || @@ -703,8 +704,8 @@ mongoGetForeignPlan(PlannerInfo *root, { /* * scan_var_list should have expressions and not TargetEntry nodes. - * However, grouped_tlist created has TLEs, and thus retrieve them into - * scan_var_list. + * 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, @@ -738,9 +739,10 @@ mongoGetForeignPlan(PlannerInfo *root, mongofdwreltype = BASE_REL; #ifdef META_DRIVER + /* - * Prepare separate lists of information. This information would be useful - * at the time of execution to prepare the MongoDB query. + * 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)) { @@ -790,8 +792,9 @@ mongoGetForeignPlan(PlannerInfo *root, qual_info->isHavingList = NIL; /* - * Extract required data of columns involved in join clauses and append - * it into the various lists required to pass it to the executor. + * 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. @@ -857,6 +860,7 @@ mongoGetForeignPlan(PlannerInfo *root, fdw_private = lappend(fdw_private, makeInteger(mongofdwreltype)); #ifdef META_DRIVER + /* * Unlike postgres_fdw, remote query formation is done in the execution * state. There is NO way to get the correct information required to form @@ -1003,8 +1007,8 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) UserMapping *user; ForeignTable *table; int rtindex; - List *colNameList = NIL; - List *colIsInnerList = NIL; + List *colNameList = NIL; + List *colIsInnerList = NIL; /* If Explain with no Analyze, do nothing */ if (eflags & EXEC_FLAG_EXPLAIN_ONLY) @@ -1014,8 +1018,9 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) /* * 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. + * 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; @@ -1034,8 +1039,8 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) options = mongo_get_options(rte->relid); /* - * Get connection to the foreign server. Connection manager will establish - * new connection if necessary. + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -1087,7 +1092,7 @@ mongoIterateForeignScan(ForeignScanState *node) char *collectionName; /* - * We construct the query document to have MongoDB filter its rows. We + * 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 @@ -1706,7 +1711,7 @@ foreign_table_document_count(Oid foreignTableId) MongoFdwOptions *options; MONGO_CONN *mongoConnection; const BSON *emptyQuery = NULL; - double documentCount; + double documentCount; Oid userid = GetUserId(); ForeignServer *server; UserMapping *user; @@ -1749,8 +1754,8 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, HTAB *columnMappingHash; HASHCTL hashInfo; uint32 attnum = 0; - Index listIndex = 0; - Index aggIndex = 0; + Index listIndex = 0; + Index aggIndex = 0; memset(&hashInfo, 0, sizeof(hashInfo)); hashInfo.keysize = NAMEDATALEN; @@ -1778,12 +1783,12 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, 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 + * 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. + * 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. @@ -1803,13 +1808,13 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, /* * 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 + * "_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. + * 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) { @@ -1829,9 +1834,9 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, 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. + * 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); } @@ -1872,7 +1877,7 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, if ((relType == UPPER_REL || relType == UPPER_JOIN_REL) && !strncmp(hashKey, "AGG_RESULT_KEY", 5)) { - Aggref *agg = (Aggref *) lfirst(columnCell); + Aggref *agg = (Aggref *) lfirst(columnCell); columnMapping->columnTypeId = agg->aggtype; columnMapping->columnTypeMod = agg->aggcollid; @@ -1990,7 +1995,7 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, bool is_agg = false; if (!strncmp(bsonKey, "AGG_RESULT_KEY", 5) && bsonType == BSON_TYPE_INT32) - is_agg = true; + is_agg = true; columnMapping = NULL; if (bsonDocumentKey != NULL) @@ -2112,6 +2117,7 @@ column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) #endif break; case NAMEOID: + /* * We currently error out on data types other than object * identifier. MongoDB supports more data types for the _id field @@ -2146,6 +2152,7 @@ column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) 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. @@ -2647,7 +2654,7 @@ mongoAnalyzeForeignTable(Relation relation, int32 *attributeWidths; Oid foreignTableId; int32 documentWidth; - double documentCount; + double documentCount; double foreignTableSize; foreignTableId = RelationGetRelid(relation); @@ -2758,8 +2765,8 @@ mongo_acquire_sample_rows(Relation relation, options = mongo_get_options(foreignTableId); /* - * Get connection to the foreign server. Connection manager will establish - * new connection if necessary. + * Get connection to the foreign server. Connection manager will + * establish new connection if necessary. */ mongoConnection = mongo_get_connection(server, user, options); @@ -2966,8 +2973,8 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, ForeignPath *joinpath; Cost startup_cost; Cost total_cost; - Path *epq_path = NULL; /* Path to create plan to be executed when - * EvalPlanQual gets triggered. */ + Path *epq_path = NULL; /* Path to create plan to be executed when + * EvalPlanQual gets triggered. */ /* * Skip if this join combination has been considered already. @@ -3031,15 +3038,15 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, */ #if PG_VERSION_NUM >= 120000 joinpath = create_foreign_join_path(root, - joinrel, - NULL, - joinrel->rows, - startup_cost, - total_cost, - NIL, /* no pathkeys */ - joinrel->lateral_relids, - epq_path, - NULL); /* no fdw_private */ + joinrel, + NULL, + joinrel->rows, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + joinrel->lateral_relids, + epq_path, + NULL); /* no fdw_private */ #else joinpath = create_foreignscan_path(root, joinrel, @@ -3108,8 +3115,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, /* * 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. + * 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; @@ -3183,8 +3190,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, * 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 + * 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); @@ -3469,7 +3476,8 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) #else if (root->hasHavingQual && query->havingQual) { - ListCell *lc; + ListCell *lc; + foreach(lc, (List *) query->havingQual) #endif { @@ -3710,7 +3718,7 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, /* Add generated path into grouped_rel by add_path(). */ add_path(grouped_rel, (Path *) grouppath); } -#endif /* End of META_DRIVER */ +#endif /* End of META_DRIVER */ /* * mongoEstimateCosts diff --git a/mongo_fdw.h b/mongo_fdw.h index 31c4858..5e95800 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -175,9 +175,9 @@ /* Macro for list API backporting. */ #if PG_VERSION_NUM < 130000 - #define mongo_list_concat(l1, l2) list_concat(l1, list_copy(l2)) +#define mongo_list_concat(l1, l2) list_concat(l1, list_copy(l2)) #else - #define mongo_list_concat(l1, l2) list_concat((l1), (l2)) +#define mongo_list_concat(l1, l2) list_concat((l1), (l2)) #endif /* Macro for hard-coded aggregation result key */ @@ -267,8 +267,8 @@ typedef struct MongoFdwOptions char *ca_dir; char *crl_file; bool weak_cert_validation; - bool enable_join_pushdown; - bool enable_aggregate_pushdown; + bool enable_join_pushdown; + bool enable_aggregate_pushdown; #endif } MongoFdwOptions; @@ -294,11 +294,11 @@ typedef struct MongoFdwModifyState BSON *queryDocument; /* Bson Document */ MongoFdwOptions *options; - AttrNumber rowidAttno; /* attnum of resjunk rowid column */ + AttrNumber rowidAttno; /* attnum of resjunk rowid column */ /* Join/Upper relation information */ - uint32 relType; /* relation type. Base, Join, Upper, or - * Upper on join */ + uint32 relType; /* relation type. Base, Join, Upper, or Upper + * on join */ char *outerRelName; /* Outer relation name */ } MongoFdwModifyState; @@ -352,10 +352,10 @@ typedef struct MongoFdwRelationInfo RelOptInfo *innerrel; JoinType jointype; List *joinclauses; - char *inner_relname; + char *inner_relname; char *outer_relname; - MongoFdwOptions *options; /* Options applicable for this relation */ + MongoFdwOptions *options; /* Options applicable for this relation */ /* Grouping information */ List *grouped_tlist; @@ -427,15 +427,15 @@ 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; + 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? */ + 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; @@ -443,8 +443,8 @@ typedef struct MongoRelQualInfo typedef struct ColumnHashKey { - int varno; - int varattno; + int varno; + int varattno; } ColumnHashKey; /* @@ -477,7 +477,7 @@ extern void mongo_release_connection(MONGO_CONN *conn); 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 ); + List **colIsInnerList); extern bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, bool is_join_cond, bool is_having_cond); diff --git a/mongo_query.c b/mongo_query.c index c6dede3..7236f62 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -239,7 +239,7 @@ mongo_query_document(ForeignScanState *scanStateNode) #ifdef META_DRIVER MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; BSON root_pipeline; - int root_index = 0; + int root_index = 0; List *joinclauses; List *colname_list = NIL; List *isouter_list = NIL; @@ -310,9 +310,9 @@ mongo_query_document(ForeignScanState *scanStateNode) relationId = 0; /* - * We distinguish between equality expressions and others since we need - * to insert the latter (<, >, <=, >=, <>) as separate sub-documents - * into the BSON query object. + * We distinguish between equality expressions and others since we + * need to insert the latter (<, >, <=, >=, <>) as separate + * sub-documents into the BSON query object. */ equalityOperatorList = equality_operator_list(opExpressionList); comparisonOperatorList = list_difference(opExpressionList, @@ -464,7 +464,7 @@ mongo_query_document(ForeignScanState *scanStateNode) } #ifdef META_DRIVER - if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) + if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { BSON inner_pipeline; BSON lookup_object; @@ -491,12 +491,12 @@ mongo_query_document(ForeignScanState *scanStateNode) bsonAppendStartObject(&lookup, "let", &let_exprs); forboth(cell1, colname_list, cell2, isouter_list) { - char *colname = strVal(lfirst(cell1)); - bool is_outer = lfirst_int(cell2); + 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 + * 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. */ @@ -505,16 +505,16 @@ mongo_query_document(ForeignScanState *scanStateNode) /* * 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. + * must begin with only a lowercase ASCII letter or a + * non-ASCII character. */ - char *varname = psprintf("v_%s", colname); - char *field = psprintf("$%s", colname); + char *varname = psprintf("v_%s", colname); + char *field = psprintf("$%s", colname); bsonAppendUTF8(&let_exprs, varname, field); } } - bsonAppendFinishObject(&lookup, &let_exprs); /* End "let" */ + bsonAppendFinishObject(&lookup, &let_exprs); /* End "let" */ /* Form inner pipeline required in $lookup stage to execute $match */ bsonAppendStartArray(inner_pipeline_doc, "pipeline", &inner_pipeline); @@ -525,14 +525,14 @@ mongo_query_document(ForeignScanState *scanStateNode) context.colInfoHash = columnInfoHash; context.isBoolExpr = false; - /* Form equivalent join qual clauses in MongoDB */ + /* Form equivalent join qual clauses in MongoDB */ mongo_prepare_inner_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"), + bson_append_array(&lookup, "pipeline", (int) strlen("pipeline"), &inner_pipeline); bsonAppendUTF8(&lookup, "as", "Join_Result"); @@ -564,7 +564,7 @@ mongo_query_document(ForeignScanState *scanStateNode) } else { - BSON match_stage; + BSON match_stage; /* $match stage. This is to add a filter for the WHERE clause */ bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), @@ -576,10 +576,10 @@ mongo_query_document(ForeignScanState *scanStateNode) /* 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; + List *func_list; + List *agg_col_list; + List *groupby_col_list; + List *having_expr; BSON groupby_expr; BSON group_stage; BSON group_expr; @@ -587,8 +587,8 @@ mongo_query_document(ForeignScanState *scanStateNode) ListCell *cell1; ListCell *cell2; ListCell *cell3; - List *is_having_list; - Index aggIndex = 0; + List *is_having_list; + Index aggIndex = 0; func_list = list_nth(PrivateList, mongoFdwPrivateAggType); agg_col_list = list_nth(PrivateList, mongoFdwPrivateAggColList); @@ -603,9 +603,9 @@ mongo_query_document(ForeignScanState *scanStateNode) /* * 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. + * 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) { @@ -637,7 +637,7 @@ mongo_query_document(ForeignScanState *scanStateNode) columnInfo->colName)); } } - bsonAppendFinishObject(&group, &groupby_expr); /* End "_id" */ + bsonAppendFinishObject(&group, &groupby_expr); /* End "_id" */ } else { @@ -653,7 +653,7 @@ mongo_query_document(ForeignScanState *scanStateNode) bool found = false; char *func_name = strVal(lfirst(cell1)); Var *column = (Var *) lfirst(cell2); - bool is_having_agg = lfirst_int(cell3); + bool is_having_agg = lfirst_int(cell3); if (is_having_agg) bsonAppendStartObject(&group, "v_having", &group_expr); @@ -670,6 +670,7 @@ mongo_query_document(ForeignScanState *scanStateNode) (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 @@ -696,8 +697,8 @@ mongo_query_document(ForeignScanState *scanStateNode) else { /* - * The COUNT(*) in PostgreSQL is equivalent to {$sum: 1} in the - * MongoDB. + * The COUNT(*) in PostgreSQL is equivalent to {$sum: 1} in + * the MongoDB. */ bsonAppendInt32(&group_expr, psprintf("$%s", "sum"), 1); } @@ -711,7 +712,7 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Add HAVING operation */ if (having_expr) { - BSON match_stage; + BSON match_stage; BSON *filter = bsonCreate(); List *equalityOperatorList; List *comparisonOperatorList; @@ -1225,6 +1226,7 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, } break; default: + /* * We currently error out on other data types. Some types such as * byte arrays are easy to add, but they need testing. @@ -1277,7 +1279,7 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, { Var *var = (Var *) lfirst(lc); RangeTblEntry *rte = planner_rt_fetch(var->varno, root); - int is_innerrel = false; + int is_innerrel = false; /* * Add aggregation target also in the needed column list. This would @@ -1440,9 +1442,9 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, 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. + * 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; @@ -1635,10 +1637,10 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, 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)) + strcmp(func_name, "max") == 0 || + strcmp(func_name, "sum") == 0 || + strcmp(func_name, "avg") == 0 || + strcmp(func_name, "count") == 0)) return false; /* @@ -1683,8 +1685,8 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* * 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.) + * for now rather than immediately returning false, since th e + * parent node might not care.) */ collation = agg->aggcollid; if (collation == InvalidOid) @@ -1779,7 +1781,8 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, /* * 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. + * meaningful by the core code. For other relations, use their own + * relids. */ if (IS_UPPER_REL(baserel)) glob_cxt.relids = fpinfo->outerrel->relids; @@ -1891,7 +1894,7 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, ListCell *l4; memset(&hashInfo, 0, sizeof(hashInfo)); - hashInfo.keysize = sizeof(ColInfoHashKey ); + hashInfo.keysize = sizeof(ColInfoHashKey); hashInfo.entrysize = sizeof(ColInfoHashEntry); hashInfo.hcxt = CurrentMemoryContext; @@ -1909,15 +1912,15 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, { ColInfoHashEntry *columnInfo; char *columnName = strVal(lfirst(l1)); - int columnNum = lfirst_int(l2); - int varNo = lfirst_int(l3); + 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, + (void *) &key, HASH_ENTER, NULL); Assert(columnInfo != NULL); @@ -1963,11 +1966,11 @@ mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, pipeline_cxt *context) { BSON *and_query_doc = bsonCreate(); - BSON match_object; - BSON match_stage; - BSON expr; - BSON and_op; - int inner_pipeline_index = 0; + BSON match_object; + BSON match_stage; + BSON expr; + BSON and_op; + int inner_pipeline_index = 0; bsonAppendStartObject(inner_pipeline, psprintf("%d", inner_pipeline_index++), @@ -1983,7 +1986,7 @@ mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, mongo_append_joinclauses_to_inner_pipeline(joinclause, &and_op, context); /* Append $and array to $expr */ - bson_append_array(&expr, "$and", (int) strlen ("$and"), &and_op); + bson_append_array(&expr, "$and", (int) strlen("$and"), &and_op); bsonAppendFinishArray(and_query_doc, &and_op); bsonAppendFinishObject(&match_stage, &expr); diff --git a/mongo_query.h b/mongo_query.h index a5adf11..d48323f 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -22,10 +22,10 @@ */ 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? */ + 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? */ } pipeline_cxt; /* @@ -40,7 +40,7 @@ typedef struct ColInfoHashKey } ColInfoHashKey; typedef struct ColInfoEntry { - ColInfoHashKey key; /* Hash key */ + ColInfoHashKey key; /* Hash key */ char *colName; bool isOuter; } ColInfoHashEntry; diff --git a/mongo_wrapper.c b/mongo_wrapper.c index e3e2a5e..90f0e49 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -186,7 +186,7 @@ bsonIterInt32(BSON_ITERATOR *it) { case BSON_DOUBLE: { - double val = bson_iterator_double_raw(it); + double val = bson_iterator_double_raw(it); if (val < PG_INT32_MIN || val > PG_INT32_MAX) ereport(ERROR, @@ -198,7 +198,7 @@ bsonIterInt32(BSON_ITERATOR *it) } case BSON_LONG: { - int64 val = bson_iterator_long_raw(it); + int64 val = bson_iterator_long_raw(it); if (val < PG_INT32_MIN || val > PG_INT32_MAX) ereport(ERROR, diff --git a/mongo_wrapper.h b/mongo_wrapper.h index d978527..ba00b3f 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -31,47 +31,48 @@ MONGO_CONN *mongoConnect(MongoFdwOptions *opt); #else MONGO_CONN *mongoConnect(MongoFdwOptions *opt); #endif -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); +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); +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); +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); +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); #ifdef META_DRIVER const char *bsonIterBinData(BSON_ITERATOR *it, uint32_t *len); #else const char *bsonIterBinData(BSON_ITERATOR *it); -int bsonIterBinLen(BSON_ITERATOR *it); +int bsonIterBinLen(BSON_ITERATOR *it); #endif #ifdef META_DRIVER const bson_oid_t *bsonIterOid(BSON_ITERATOR *it); #else bson_oid_t *bsonIterOid(BSON_ITERATOR *it); #endif -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]); +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); @@ -81,33 +82,34 @@ const bson_value_t *bsonIterValue(BSON_ITERATOR *i); const char *bsonIterValue(BSON_ITERATOR *i); #endif -void bsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer); +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 bsonFinish(BSON *b); -bool jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v); +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 bsonFinish(BSON *b); +bool jsonToBsonAppendElement(BSON *bb, const char *k, + struct json_object *v); json_object *jsonTokenerPrase(char *s); -char *bsonAsJson(const BSON *bsonDocument); +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); +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 */ +#endif /* MONGO_QUERY_H */ diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 41e96b7..0598235 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -354,7 +354,7 @@ bsonIterInt32(BSON_ITERATOR *it) return (int32) bson_iter_bool(it); case BSON_TYPE_DOUBLE: { - double val = bson_iter_double(it); + double val = bson_iter_double(it); if (val < PG_INT32_MIN || val > PG_INT32_MAX) ereport(ERROR, @@ -380,7 +380,7 @@ bsonIterInt32(BSON_ITERATOR *it) return bson_iter_int32(it); default: return 0; - } + } } int64_t diff --git a/option.c b/option.c index 36d2fd6..d2e58d1 100644 --- a/option.c +++ b/option.c @@ -112,7 +112,7 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) strcmp(optionName, OPTION_NAME_SSL) == 0 || strcmp(optionName, OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN) == 0 #endif - ) + ) { /* These accept only boolean values */ (void) defGetBoolean(optionDef); @@ -233,7 +233,7 @@ mongo_get_options(Oid foreignTableId) OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN) == 0) options->enable_aggregate_pushdown = defGetBoolean(def); - else /* This is for continuation */ + else /* This is for continuation */ #endif if (strcmp(def->defname, OPTION_NAME_ADDRESS) == 0) @@ -269,7 +269,7 @@ mongo_get_options(Oid foreignTableId) options->svr_database = pstrdup(DEFAULT_DATABASE_NAME); if (!options->collectionName) - options->collectionName= get_rel_name(foreignTableId); + options->collectionName = get_rel_name(foreignTableId); return options; } From 3e2860b7fee982cf862602fa8d89edb945b51108 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 19 May 2022 14:44:46 +0530 Subject: [PATCH 190/239] Stamp 5.4.0. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index 9612068..12c3c5e 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50300 + 50400 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 0794b76..9d8f36b 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50300 + 50400 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index f56ee5e..9499951 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -59,9 +59,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.3.0 so number will be 50300 + * our version is 5.4.0 so number will be 50400 */ -#define CODE_VERSION 50300 +#define CODE_VERSION 50400 extern PGDLLEXPORT void _PG_init(void); From 668124c6c2abb4b76fb1d230f8d39990b194c2df Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 19 Jul 2022 10:57:15 +0530 Subject: [PATCH 191/239] Fix typos in autogen.sh and README.md files. Reported on GitHub through pull request #153 by Danny Hermes (dhermes). FDW-536, Danny Hermes, reviewed and further changes by Vaibhav Dalvi. --- README.md | 2 +- autogen.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b57cdd9..14a2851 100644 --- a/README.md +++ b/README.md @@ -197,7 +197,7 @@ statements for the foreign tables using the mongo_fdw. ### Connection Pooling The latest version comes with a connection pooler that utilizes the -same mango database connection for all the queries in the same session. +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. diff --git a/autogen.sh b/autogen.sh index d09ec32..8278c23 100755 --- a/autogen.sh +++ b/autogen.sh @@ -30,7 +30,7 @@ if ! [ -x "$(command -v cmake3)" ]; then fi ### -# Pull the latest version of Monggo C Driver's master branch +# Pull the latest version of Mongo C Driver's master branch # function checkout_mongo_driver { @@ -42,7 +42,7 @@ function checkout_mongo_driver } ### -# Pull the legacy branch from the Mongo C Driver's +# Pull the legacy branch of the Mongo C Driver # function checkout_legacy_branch { From 2cec9e44f2ca9c981778a0e1f2abfdc2f7f9e039 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 19 Jul 2022 10:57:15 +0530 Subject: [PATCH 192/239] Fix server crash caused due to missed Param node handling. While preparing a query filter in mongo_query_document(), we have missed considering the Param node in a comparison expression. And thus, was not preparing the query correctly, leading to the server crash. We do have this handling for the equality operator, though. Fix the same. Reported on GitHub through issue #160 by pludov. FDW-529, Vaibhav Dalvi, reviewed and further revised by Jeevan Chalke. --- expected/select.out | 50 +++++++++++++++++++++++++++++++++++++++++++ expected/select_1.out | 50 +++++++++++++++++++++++++++++++++++++++++++ mongo_query.c | 18 +++++++++++++--- sql/select.sql | 24 +++++++++++++++++++++ 4 files changed, 139 insertions(+), 3 deletions(-) diff --git a/expected/select.out b/expected/select.out index 12c3c5e..ddc3d93 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1336,6 +1336,55 @@ SELECT a FROM f_test_tbl6 ORDER BY 1; 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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1346,6 +1395,7 @@ 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; diff --git a/expected/select_1.out b/expected/select_1.out index 9d8f36b..668fc32 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1300,6 +1300,55 @@ SELECT a FROM f_test_tbl6 ORDER BY 1; 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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -1310,6 +1359,7 @@ 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; diff --git a/mongo_query.c b/mongo_query.c index 7236f62..4df900e 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -433,18 +433,30 @@ mongo_query_document(ForeignScanState *scanStateNode) char *mongoOperatorName; List *argumentList; Const *constant; + Param *paramNode; columnOperator = (OpExpr *) lfirst(columnOperatorCell); argumentList = columnOperator->args; constant = (Const *) find_argument_of_type(argumentList, T_Const); + paramNode = (Param *) find_argument_of_type(argumentList, + T_Param); operatorName = get_opname(columnOperator->opno); mongoOperatorName = mongo_operator_name(operatorName); + #ifdef META_DRIVER - append_constant_value(&childDocument, mongoOperatorName, - constant); + if (constant != NULL) + append_constant_value(&childDocument, mongoOperatorName, + constant); + else + append_param_value(&childDocument, mongoOperatorName, paramNode, + scanStateNode); #else - append_constant_value(filter, mongoOperatorName, constant); + if (constant != NULL) + append_constant_value(filter, mongoOperatorName, constant); + else + append_param_value(filter, mongoOperatorName, paramNode, + scanStateNode); #endif } bsonAppendFinishObject(filter, &childDocument); diff --git a/sql/select.sql b/sql/select.sql index 290f61e..cc75ab5 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -334,6 +334,29 @@ 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(); + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DROP TABLE l_test_tbl1; @@ -344,6 +367,7 @@ 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; From a5b12ba20f6390469c3f6d35a458cc9cf39d1cae Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 6 Sep 2022 15:32:42 +0530 Subject: [PATCH 193/239] Improve the WHERE clause pushdown. With these improvements, we now support recursive operator expressions, boolean expressions, Relabel types, and Var's on both sides of an operator. FDW-526, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, tested by Ajay Pal. --- deparse.c | 60 +++- expected/aggregate_pushdown.out | 46 +-- expected/aggregate_pushdown_1.out | 46 +-- expected/aggregate_pushdown_2.out | 46 +-- expected/join_pushdown.out | 68 +--- expected/join_pushdown_1.out | 68 +--- expected/join_pushdown_2.out | 68 +--- expected/join_pushdown_3.out | 28 +- expected/pushdown.out | 123 +++++-- expected/pushdown_1.out | 580 ++++++++++++++++++++++++++++++ mongo_fdw.c | 126 ++++--- mongo_fdw.h | 3 +- mongo_query.c | 381 ++++++++------------ mongo_query.h | 9 +- sql/aggregate_pushdown.sql | 4 +- sql/join_pushdown.sql | 2 +- sql/pushdown.sql | 37 +- 17 files changed, 1110 insertions(+), 585 deletions(-) create mode 100644 expected/pushdown_1.out diff --git a/deparse.c b/deparse.c index 6c7e622..9bb3f3c 100644 --- a/deparse.c +++ b/deparse.c @@ -346,6 +346,13 @@ mongo_append_expr(Expr *node, BSON *child_doc, pipeline_cxt *context) 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)); @@ -419,7 +426,7 @@ mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context) * * 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-clauses. E.g. add the following syntax: + * involved in JOIN and WHERE clauses. E.g. add the following syntax: * * {"$ne": [ "$$v_age", null ]}, * {"$ne": [ "$old", null ]} @@ -440,6 +447,9 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) 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)) @@ -494,6 +504,9 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) mongo_append_expr(lfirst(arg), &child1, context); } + /* Decreament operator expression count */ + context->opExprCount--; + bsonAppendFinishArray(&expr, &child1); if (context->isBoolExpr) bsonAppendFinishObject(&and_op, &expr); @@ -501,27 +514,35 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) bsonAppendFinishObject(child_doc, &expr); /* - * Add equality check for null values for columns involved in - * join-clauses. + * Add equality check for null values for columns involved in JOIN and + * WHERE clauses. */ - foreach(arg, node->args) + if (context->opExprCount == 0) { - if (!IsA(lfirst(arg), Var)) - continue; + List *var_list; + ListCell *lc; - if (context->isBoolExpr) - bsonAppendStartObject(&and_op, psprintf("%d", and_index++), &expr); - else - bsonAppendStartObject(child_doc, + 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); - mongo_add_null_check(lfirst(arg), &expr, context); - - if (context->isBoolExpr) - bsonAppendFinishObject(&and_op, &expr); - else - bsonAppendFinishObject(child_doc, &expr); + if (context->isBoolExpr) + bsonAppendFinishObject(&and_op, &expr); + else + bsonAppendFinishObject(child_doc, &expr); + } } if (context->isBoolExpr == true) @@ -560,7 +581,7 @@ mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context) if (!found) return; - if (columnInfo->isOuter) + if (columnInfo->isOuter && context->isJoinClause) field = psprintf("$$v_%s", columnInfo->colName); else field = psprintf("$%s", columnInfo->colName); @@ -570,7 +591,8 @@ mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context) /* * mongo_add_null_check - * Eliminate null value rows of columns involved in the join clauses. + * 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) @@ -591,7 +613,7 @@ mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context) if (!found) return; - if (columnInfo->isOuter) + if (columnInfo->isOuter && context->isJoinClause) field = psprintf("$$v_%s", columnInfo->colName); else field = psprintf("$%s", columnInfo->colName); diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 5978943..493cc59 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -260,7 +260,7 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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 > 10 ORDER BY 1; +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; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Sort @@ -271,10 +271,10 @@ SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c 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 > 10 ORDER BY 1; +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; sum | avg -----+------------------ - 280 | 25.4545454545455 + 310 | 22.1428571428571 (1 row) EXPLAIN (VERBOSE, COSTS OFF) @@ -312,9 +312,8 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON Sort Key: t1.c8 -> Foreign Scan Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) - Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) -(7 rows) +(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; sum | c8 | avg @@ -730,29 +729,34 @@ SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 -- 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 --------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------- Sort Output: q.b, (count(fdw137_t1.c1)), (sum(q.a)) Sort Key: q.b, (count(fdw137_t1.c1)) - -> HashAggregate + -> GroupAggregate Output: q.b, count(fdw137_t1.c1), sum(q.a) Group Key: q.b - -> Hash Left Join + -> Sort 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 + 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 - -> Foreign Scan - Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint - Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) -(20 rows) + -> 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 diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index fe097ec..cc6915a 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -260,7 +260,7 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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 > 10 ORDER BY 1; +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; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Sort @@ -271,10 +271,10 @@ SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c 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 > 10 ORDER BY 1; +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; sum | avg -----+------------------ - 280 | 25.4545454545455 + 310 | 22.1428571428571 (1 row) EXPLAIN (VERBOSE, COSTS OFF) @@ -312,9 +312,8 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON Sort Key: t1.c8 -> Foreign Scan Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) - Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) -(7 rows) +(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; sum | c8 | avg @@ -730,29 +729,34 @@ SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 -- 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 --------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------- Sort Output: q.b, (count(fdw137_t1.c1)), (sum(q.a)) Sort Key: q.b, (count(fdw137_t1.c1)) - -> HashAggregate + -> GroupAggregate Output: q.b, count(fdw137_t1.c1), sum(q.a) Group Key: q.b - -> Hash Left Join + -> Sort 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 + 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 - -> Foreign Scan - Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint - Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) -(20 rows) + -> 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 diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 9381f6c..b88f90c 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -260,7 +260,7 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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 > 10 ORDER BY 1; +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; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ Sort @@ -271,10 +271,10 @@ SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c 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 > 10 ORDER BY 1; +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; sum | avg -----+------------------ - 280 | 25.4545454545455 + 310 | 22.1428571428571 (1 row) EXPLAIN (VERBOSE, COSTS OFF) @@ -312,9 +312,8 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON Sort Key: t1.c8 -> Foreign Scan Output: (sum(t2.c1)), t1.c8, (avg(t1.c8)) - Filter: (((avg(t1.c8)) * '1'::numeric) > '10'::numeric) Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) -(7 rows) +(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; sum | c8 | avg @@ -730,29 +729,34 @@ SELECT c8, sum FROM fdw137_t1 t1, lateral (SELECT sum(t2.c1) sum FROM fdw137_t2 -- 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 --------------------------------------------------------------------------------------------------------------------------------------------------------------- + QUERY PLAN +----------------------------------------------------------------------------------------------------------------------------------------------------------------- Sort Output: q.b, (count(fdw137_t1.c1)), (sum(q.a)) Sort Key: q.b, (count(fdw137_t1.c1)) - -> HashAggregate + -> GroupAggregate Output: q.b, count(fdw137_t1.c1), sum(q.a) Group Key: q.b - -> Hash Left Join + -> Sort 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 + 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 - -> Foreign Scan - Output: (min(13)), (avg(fdw137_t1_1.c1)), NULL::bigint - Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 fdw137_t1) INNER JOIN (mongo_fdw_regress.test_tbl2 fdw137_t2)) -(20 rows) + -> 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 diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 6cc7ed7..ee6f10c 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -1022,23 +1022,17 @@ WITH t (c1_1, c1_3, c2_1) AS ( 1000 | 30 (14 rows) --- This won't push-down because WHERE only pushes operator expression. +-- 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 --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Hash Join - Hash Cond: (e.c1 = d.c8) - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(10 rows) + -> 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; @@ -1431,43 +1425,19 @@ 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 -------------------------------------------------------------------- - Merge Append + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Output: t1.c1, t1.c2 Sort Key: t1.c1, t1.c2 - -> Merge Join - Output: t1_1.c1, t1_1.c2 - Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) - -> Sort + -> Append + -> Foreign Scan Output: t1_1.c1, t1_1.c2 - Sort Key: t1_1.c1, t1_1.c2 - -> Foreign Scan on public.ftprt1_p1 t1_1 - Output: t1_1.c1, t1_1.c2 - Filter: ((t1_1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort - Output: t2_1.c2, t2_1.c1 - Sort Key: t2_1.c2, t2_1.c1 - -> Foreign Scan on public.ftprt2_p1 t2_1 - Output: t2_1.c2, t2_1.c1 - Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Join - Output: t1_2.c1, t1_2.c2 - Merge Cond: ((t1_2.c1 = t2_2.c2) AND (t1_2.c2 = t2_2.c1)) - -> Sort + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Foreign Scan Output: t1_2.c1, t1_2.c2 - Sort Key: t1_2.c1, t1_2.c2 - -> Foreign Scan on public.ftprt1_p2 t1_2 - Output: t1_2.c1, t1_2.c2 - Filter: ((t1_2.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: t2_2.c2, t2_2.c1 - Sort Key: t2_2.c2, t2_2.c1 - -> Foreign Scan on public.ftprt2_p2 t2_2 - Output: t2_2.c2, t2_2.c1 - Foreign Namespace: mongo_fdw_regress.test4 -(34 rows) + 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 @@ -1503,14 +1473,12 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Sort Key: fprt1_1.c1 -> Foreign Scan on public.ftprt1_p1 fprt1_1 Output: fprt1_1.c1 - Filter: ((fprt1_1.c1 % 2) = 0) 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 - Filter: ((fprt2_1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 -> Merge Left Join Output: fprt1_2.c1, 't1_phv'::text, fprt2_2.c2, ('t2_phv'::text) @@ -1520,16 +1488,14 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Sort Key: fprt1_2.c1 -> Foreign Scan on public.ftprt1_p2 fprt1_2 Output: fprt1_2.c1 - Filter: ((fprt1_2.c1 % 2) = 0) 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 - Filter: ((fprt2_2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(40 rows) +(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 diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index ee11238..aafb8aa 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -1022,23 +1022,17 @@ WITH t (c1_1, c1_3, c2_1) AS ( 1000 | 30 (14 rows) --- This won't push-down because WHERE only pushes operator expression. +-- 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 --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Hash Join - Hash Cond: (e.c1 = d.c8) - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(10 rows) + -> 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; @@ -1431,43 +1425,19 @@ 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 -------------------------------------------------------------------- - Merge Append + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Output: t1.c1, t1.c2 Sort Key: t1.c1, t1.c2 - -> Merge Join - Output: t1.c1, t1.c2 - Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) - -> Sort + -> Append + -> Foreign Scan Output: t1.c1, t1.c2 - Sort Key: t1.c1, t1.c2 - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1, t1.c2 - Filter: ((t1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort - Output: t2.c2, t2.c1 - Sort Key: t2.c2, t2.c1 - -> Foreign Scan on public.ftprt2_p1 t2 - Output: t2.c2, t2.c1 - Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Join - Output: t1_1.c1, t1_1.c2 - Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) - -> Sort + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Foreign Scan Output: t1_1.c1, t1_1.c2 - Sort Key: t1_1.c1, t1_1.c2 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1, t1_1.c2 - Filter: ((t1_1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: t2_1.c2, t2_1.c1 - Sort Key: t2_1.c2, t2_1.c1 - -> Foreign Scan on public.ftprt2_p2 t2_1 - Output: t2_1.c2, t2_1.c1 - Foreign Namespace: mongo_fdw_regress.test4 -(34 rows) + 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 @@ -1498,28 +1468,24 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) -> Foreign Scan on public.ftprt1_p1 Output: ftprt1_p1.c1 - Filter: ((ftprt1_p1.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test1 -> Hash Output: ftprt2_p1.c2, ('t2_phv'::text) -> Foreign Scan on public.ftprt2_p1 Output: ftprt2_p1.c2, 't2_phv'::text - Filter: ((ftprt2_p1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 -> Hash Left Join Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) -> Foreign Scan on public.ftprt1_p2 Output: ftprt1_p2.c1 - Filter: ((ftprt1_p2.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test2 -> Hash Output: ftprt2_p2.c2, ('t2_phv'::text) -> Foreign Scan on public.ftprt2_p2 Output: ftprt2_p2.c2, 't2_phv'::text - Filter: ((ftprt2_p2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(30 rows) +(26 rows) SELECT t1.c1, t1.phv, t2.c2, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index 4ad1d16..cd435dd 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1025,23 +1025,17 @@ WITH t (c1_1, c1_3, c2_1) AS ( 1000 | 30 (14 rows) --- This won't push-down because WHERE only pushes operator expression. +-- 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 --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Hash Join - Hash Cond: (e.c1 = d.c8) - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(10 rows) + -> 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; @@ -1434,43 +1428,19 @@ 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 -------------------------------------------------------------------- - Merge Append + QUERY PLAN +------------------------------------------------------------------------------------------------------- + Sort + Output: t1.c1, t1.c2 Sort Key: t1.c1, t1.c2 - -> Merge Join - Output: t1.c1, t1.c2 - Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) - -> Sort + -> Append + -> Foreign Scan Output: t1.c1, t1.c2 - Sort Key: t1.c1, t1.c2 - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1, t1.c2 - Filter: ((t1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test1 - -> Sort - Output: t2.c2, t2.c1 - Sort Key: t2.c2, t2.c1 - -> Foreign Scan on public.ftprt2_p1 t2 - Output: t2.c2, t2.c1 - Foreign Namespace: mongo_fdw_regress.test3 - -> Merge Join - Output: t1_1.c1, t1_1.c2 - Merge Cond: ((t1_1.c1 = t2_1.c2) AND (t1_1.c2 = t2_1.c1)) - -> Sort + Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) + -> Foreign Scan Output: t1_1.c1, t1_1.c2 - Sort Key: t1_1.c1, t1_1.c2 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1, t1_1.c2 - Filter: ((t1_1.c1 % 2) = 0) - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: t2_1.c2, t2_1.c1 - Sort Key: t2_1.c2, t2_1.c1 - -> Foreign Scan on public.ftprt2_p2 t2_1 - Output: t2_1.c2, t2_1.c1 - Foreign Namespace: mongo_fdw_regress.test4 -(34 rows) + 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 @@ -1501,28 +1471,24 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) -> Foreign Scan on public.ftprt1_p1 Output: ftprt1_p1.c1 - Filter: ((ftprt1_p1.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test1 -> Hash Output: ftprt2_p1.c2, ('t2_phv'::text) -> Foreign Scan on public.ftprt2_p1 Output: ftprt2_p1.c2, 't2_phv'::text - Filter: ((ftprt2_p1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 -> Hash Left Join Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) -> Foreign Scan on public.ftprt1_p2 Output: ftprt1_p2.c1 - Filter: ((ftprt1_p2.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test2 -> Hash Output: ftprt2_p2.c2, ('t2_phv'::text) -> Foreign Scan on public.ftprt2_p2 Output: ftprt2_p2.c2, 't2_phv'::text - Filter: ((ftprt2_p2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(30 rows) +(26 rows) SELECT t1.c1, t1.phv, t2.c2, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index a122f12..614df54 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -1025,23 +1025,17 @@ WITH t (c1_1, c1_3, c2_1) AS ( 1000 | 30 (14 rows) --- This won't push-down because WHERE only pushes operator expression. +-- 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 --------------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------------------- Sort Sort Key: d.c1 - -> Hash Join - Hash Cond: (e.c1 = d.c8) - -> Foreign Scan on f_test_tbl2 e - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - -> Foreign Scan on f_test_tbl1 d - Filter: ((c5 = '02-22-1981'::date) OR (c5 = '12-17-1980'::date)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(10 rows) + -> 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; @@ -1466,11 +1460,9 @@ SELECT t1.c1, t1.c2 -> Append -> Foreign Scan on public.ftprt1_p1 t1 Output: t1.c1, t1.c2 - Filter: ((t1.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test1 -> Foreign Scan on public.ftprt1_p2 t1_1 Output: t1_1.c1, t1_1.c2 - Filter: ((t1_1.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test2 -> Sort Output: t2.c2, t2.c1 @@ -1482,7 +1474,7 @@ SELECT t1.c1, t1.c2 -> Foreign Scan on public.ftprt2_p2 t2_1 Output: t2_1.c2, t2_1.c1 Foreign Namespace: mongo_fdw_regress.test4 -(25 rows) +(23 rows) SELECT t1.c1, t1.c2 FROM fprt1 t1, LATERAL (SELECT t2.c1, t2.c2 FROM fprt2 t2 @@ -1516,11 +1508,9 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv -> Append -> Foreign Scan on public.ftprt1_p1 Output: ftprt1_p1.c1 - Filter: ((ftprt1_p1.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test1 -> Foreign Scan on public.ftprt1_p2 Output: ftprt1_p2.c1 - Filter: ((ftprt1_p2.c1 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test2 -> Sort Output: ftprt2_p1.c2, ('t2_phv'::text) @@ -1528,13 +1518,11 @@ SELECT t1.c1, t1.phv, t2.c2, t2.phv -> Append -> Foreign Scan on public.ftprt2_p1 Output: ftprt2_p1.c2, 't2_phv'::text - Filter: ((ftprt2_p1.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test3 -> Foreign Scan on public.ftprt2_p2 Output: ftprt2_p2.c2, 't2_phv'::text - Filter: ((ftprt2_p2.c2 % 2) = 0) Foreign Namespace: mongo_fdw_regress.test4 -(30 rows) +(26 rows) SELECT t1.c1, t1.phv, t2.c2, t2.phv FROM (SELECT 't1_phv' phv, * FROM fprt1 WHERE c1 % 2 = 0) t1 LEFT JOIN diff --git a/expected/pushdown.out b/expected/pushdown.out index c2bbfe0..f3eab9c 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -286,9 +286,8 @@ SELECT a FROM f_mongo_test Sort Key: f_mongo_test.a -> Foreign Scan on public.f_mongo_test Output: a - Filter: ((f_mongo_test.a % 2) = 1) Foreign Namespace: mongo_fdw_regress.mongo_test -(7 rows) +(6 rows) SELECT a FROM f_mongo_test WHERE a%2 = 1 @@ -376,29 +375,61 @@ SELECT c1, c2 FROM f_test_tbl1 100 | EMP1 (1 row) --- Should not push down if two columns of same table is +-- Should push down if two columns of same table are -- involved in single WHERE clause operator expression. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, c6 FROM f_test_tbl1 - WHERE c1 = c6 AND c1 = 1100 +SELECT c1, c4 FROM f_test_tbl1 + WHERE c1 > c4 ORDER BY c1; QUERY PLAN -------------------------------------------------------- - Foreign Scan on public.f_test_tbl1 - Output: c1, c6 - Filter: ((f_test_tbl1.c1)::numeric = f_test_tbl1.c6) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(4 rows) + Sort + Output: c1, c4 + Sort Key: f_test_tbl1.c1 + -> Foreign Scan on public.f_test_tbl1 + Output: c1, c4 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) -SELECT c1, c6 FROM f_test_tbl1 - WHERE c1 = c6 AND c1 = 1100 +SELECT c1, c4 FROM f_test_tbl1 + WHERE c1 > c4 ORDER BY c1; - c1 | c6 -------+------ - 1100 | 1100 -(1 row) + 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) --- Nested operator expression in WHERE clause. Shouldn't push down. +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) + +-- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 WHERE (c1 > 1000) > FALSE; @@ -406,9 +437,8 @@ SELECT c1, c2 FROM f_test_tbl1 -------------------------------------------------- Foreign Scan on public.f_test_tbl1 Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(4 rows) +(3 rows) SELECT c1, c2 FROM f_test_tbl1 WHERE (c1 > 1000) > FALSE; @@ -427,9 +457,8 @@ SELECT c1, c2 FROM f_test_tbl1 -------------------------------------------------- Foreign Scan on public.f_test_tbl1 Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) Foreign Namespace: mongo_fdw_regress.test_tbl1 -(4 rows) +(3 rows) SELECT c1, c2 FROM f_test_tbl1 WHERE (c1 > 1000) > 0::BOOLEAN; @@ -516,9 +545,8 @@ SELECT name, marks FROM f_test_tbl3 Sort Key: f_test_tbl3.name -> Foreign Scan on public.f_test_tbl3 Output: name, marks - Filter: f_test_tbl3.pass Foreign Namespace: mongo_fdw_regress.test_tbl3 -(7 rows) +(6 rows) SELECT name, marks FROM f_test_tbl3 WHERE pass = true @@ -528,8 +556,57 @@ SELECT name, marks FROM f_test_tbl3 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) + -- 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; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out new file mode 100644 index 0000000..f06553f --- /dev/null +++ b/expected/pushdown_1.out @@ -0,0 +1,580 @@ +\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 and +-- mongo_fdw_regress1 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_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; + 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.c6 = ANY ('{1600,2450}'::numeric[])) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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; + QUERY PLAN +-------------------------------------------------------- + Sort + Output: c1, c2, c6 + Sort Key: e.c1 + -> Foreign Scan on public.f_test_tbl1 e + Output: c1, c2, c6 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) + +SELECT c1, c2, c6 FROM f_test_tbl1 e + WHERE c6 > 3000 + ORDER BY c1; + 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; + 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 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) + +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 = 1500 + ORDER BY c1; + 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; + 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 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) + +SELECT c1, c2, c6, c8 FROM f_test_tbl1 e + WHERE c6 BETWEEN 1000 AND 4000 + ORDER BY c1; + 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; + QUERY PLAN +-------------------------------------------------------- + Sort + Output: c1, c2, c5 + Sort Key: e.c1 + -> Foreign Scan on public.f_test_tbl1 e + Output: c1, c2, c5 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) + +SELECT c1, c2, c5 FROM f_test_tbl1 e + WHERE c5 <= '1980-12-17' + ORDER BY c1; + 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; + QUERY PLAN +--------------------------------------------------------- + Sort + Output: a + Sort Key: f_mongo_test.a + -> Foreign Scan on public.f_mongo_test + Output: a + Filter: ((f_mongo_test.a % 2) = 1) + Foreign Namespace: mongo_fdw_regress.mongo_test +(7 rows) + +SELECT a FROM f_mongo_test + WHERE a%2 = 1 + ORDER BY a; + 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) + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 = 'EMP10'; + 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'; + c1 | c2 +------+------- + 1000 | EMP10 +(1 row) + +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE c2 < 'EMP10'; + 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'; + 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; + QUERY PLAN +-------------------------------------------------------- + Sort + Output: c1, c4 + Sort Key: f_test_tbl1.c1 + -> Foreign Scan on public.f_test_tbl1 + Output: c1, c4 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +SELECT c1, c4 FROM f_test_tbl1 + WHERE c1 > c4 + ORDER BY c1; + c1 | c4 +------+----- + 800 | 400 + 1000 | 600 + 1100 | 800 + 1200 | 600 + 1300 | 400 + 1400 | 700 +(6 rows) + +-- Nested operator expression in WHERE clause. Should pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(4 rows) + +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > FALSE; + 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; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(4 rows) + +SELECT c1, c2 FROM f_test_tbl1 + WHERE (c1 > 1000) > 0::BOOLEAN; + 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; + 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.pass + Foreign Namespace: mongo_fdw_regress.test_tbl3 +(7 rows) + +SELECT name, marks FROM f_test_tbl3 + WHERE pass = true + ORDER BY name; + 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 + Filter: ((f_test_tbl2.c2)::text = (f_test_tbl2.c3)::text) + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(4 rows) + +SELECT c1 FROM f_test_tbl2 WHERE c2 = c3; + c1 +---- +(0 rows) + +-- 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 USER MAPPING FOR public SERVER mongo_server; +DROP SERVER mongo_server; +DROP EXTENSION mongo_fdw; diff --git a/mongo_fdw.c b/mongo_fdw.c index 9499951..9ceb9fb 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -339,8 +339,12 @@ mongoGetForeignRelSize(PlannerInfo *root, { RestrictInfo *ri = (RestrictInfo *) lfirst(lc); +#ifndef META_DRIVER if (IsA(ri->clause, OpExpr) && - mongo_is_foreign_expr(root, baserel, ri->clause, false, false)) + mongo_is_foreign_expr(root, baserel, ri->clause, false)) +#else + if (mongo_is_foreign_expr(root, baserel, ri->clause, false)) +#endif fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri); else fpinfo->local_conds = lappend(fpinfo->local_conds, ri); @@ -626,8 +630,7 @@ mongoGetForeignPlan(PlannerInfo *root, 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, - false)) + mongo_is_foreign_expr(root, foreignrel, rinfo->clause, false)) remote_exprs = lappend(remote_exprs, rinfo->clause); else local_exprs = lappend(local_exprs, rinfo->clause); @@ -739,6 +742,24 @@ mongoGetForeignPlan(PlannerInfo *root, mongofdwreltype = BASE_REL; #ifdef META_DRIVER + /* + * 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->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 @@ -748,15 +769,6 @@ mongoGetForeignPlan(PlannerInfo *root, { ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; - /* - * 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; - /* * Save foreign relation and relid's of an outer relation involved in * the join depending on the relation type. @@ -775,21 +787,10 @@ mongoGetForeignPlan(PlannerInfo *root, } else { - /* For baserel */ Assert(mongofdwreltype == JOIN_REL); qual_info->foreignRel = foreignrel; qual_info->outerRelids = fpinfo->outerrel->relids; } - 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; /* * Extract required data of columns involved in join clauses and @@ -842,24 +843,27 @@ mongoGetForeignPlan(PlannerInfo *root, } else quals = remote_exprs; - - /* Destroy hash table used to get unique column info */ - hash_destroy(qual_info->exprColHash); } else -#endif + { quals = remote_exprs; - /* - * 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); + /* For baserel */ + qual_info->foreignRel = foreignrel; + qual_info->outerRelids = NULL; - /* Append relation type */ - fdw_private = lappend(fdw_private, makeInteger(mongofdwreltype)); + /* + * Extract required data of columns involved in WHERE clause of the + * simple relation. + */ + mongo_prepare_qual_info(quals, qual_info); + } -#ifdef META_DRIVER + /* Destroy hash table used to get unique column info */ + hash_destroy(qual_info->exprColHash); +#else + quals = remote_exprs; +#endif /* * Unlike postgres_fdw, remote query formation is done in the execution @@ -868,12 +872,25 @@ mongoGetForeignPlan(PlannerInfo *root, * 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)); + +#ifdef META_DRIVER + 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); if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) { - 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); + 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); @@ -885,20 +902,17 @@ mongoGetForeignPlan(PlannerInfo *root, fdw_private = lappend(fdw_private, is_inner_column_list); if (mongofdwreltype == JOIN_REL) - { - fdw_private = lappend(fdw_private, - list_make2(makeString(fpinfo->inner_relname), - makeString(fpinfo->outer_relname))); - fdw_private = lappend(fdw_private, fpinfo->joinclauses); - fdw_private = lappend(fdw_private, makeInteger(fpinfo->jointype)); - } + tfpinfo = fpinfo; else if (mongofdwreltype == UPPER_JOIN_REL) + tfpinfo = ofpinfo; + + if (tfpinfo) { fdw_private = lappend(fdw_private, - list_make2(makeString(ofpinfo->inner_relname), - makeString(ofpinfo->outer_relname))); - fdw_private = lappend(fdw_private, ofpinfo->joinclauses); - fdw_private = lappend(fdw_private, makeInteger(ofpinfo->jointype)); + 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)); } } #endif @@ -3171,7 +3185,6 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, bool is_remote_clause = mongo_is_foreign_expr(root, joinrel, rinfo->clause, - true, false); if (IS_OUTER_JOIN(jointype) && @@ -3389,7 +3402,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) * 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, false)) + if (!mongo_is_foreign_expr(root, grouped_rel, expr, false)) return false; /* Add column in group by column list */ @@ -3417,7 +3430,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) else { /* Check entire expression whether it is pushable or not */ - if (mongo_is_foreign_expr(root, grouped_rel, expr, false, false) && + if (mongo_is_foreign_expr(root, grouped_rel, expr, false) && !mongo_is_foreign_param(root, grouped_rel, expr)) { /* Pushable, add to tlist */ @@ -3436,7 +3449,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) * cannot push down aggregation to the foreign server. */ if (!mongo_is_foreign_expr(root, grouped_rel, (Expr *) aggvars, - false, false)) + false)) return false; /* @@ -3510,7 +3523,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) NULL); #endif - if (!mongo_is_foreign_expr(root, grouped_rel, expr, false, true)) + 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); @@ -3547,8 +3560,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) */ if (IsA(expr, Aggref)) { - if (!mongo_is_foreign_expr(root, grouped_rel, expr, true, - false)) + if (!mongo_is_foreign_expr(root, grouped_rel, expr, false)) return false; tlist = add_to_flat_tlist(tlist, list_make1(expr)); diff --git a/mongo_fdw.h b/mongo_fdw.h index 5e95800..60459f4 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -479,8 +479,7 @@ 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_join_cond, - bool is_having_cond); + Expr *expression, bool is_having_cond); extern bool mongo_is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr); diff --git a/mongo_query.c b/mongo_query.c index 4df900e..28713c2 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -55,11 +55,12 @@ typedef struct foreign_glob_cxt { PlannerInfo *root; /* global planner state */ RelOptInfo *foreignrel; /* the foreign relation we are planning for */ +#ifndef META_DRIVER unsigned short varcount; /* Var count */ unsigned short opexprcount; +#endif Relids relids; /* relids of base relations in the underlying * scan */ - bool is_join_cond; /* "true" for join relations */ bool is_having_cond; /* "true" for HAVING clause condition */ } foreign_glob_cxt; @@ -81,13 +82,12 @@ typedef struct foreign_loc_cxt } foreign_loc_cxt; /* Local functions forward declarations */ +#ifndef META_DRIVER static Expr *find_argument_of_type(List *argumentList, NodeTag argumentType); static List *equality_operator_list(List *operatorList); static List *unique_column_list(List *operatorList); static List *column_operator_list(Var *column, List *operatorList); -static void append_param_value(BSON *queryDocument, const char *keyName, - Param *paramNode, - ForeignScanState *scanStateNode); +#endif static bool foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, foreign_loc_cxt *outer_cxt); @@ -96,14 +96,13 @@ static List *prepare_var_list_for_baserel(Oid relid, Index varno, #ifdef META_DRIVER static HTAB *column_info_hash(List *colname_list, List *colnum_list, List *rti_list, List *isouter_list); -static void mongo_prepare_inner_pipeline(List *joinclause, - BSON *inner_pipeline, +static void mongo_prepare_pipeline(List *clause, BSON *inner_pipeline, pipeline_cxt *context); -static void mongo_append_joinclauses_to_inner_pipeline(List *joinclause, - BSON *child_doc, - pipeline_cxt *context); +static void mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc, + pipeline_cxt *context); #endif +#ifndef META_DRIVER /* * find_argument_of_type * Walks over the given argument list, looks for an argument with the @@ -132,6 +131,7 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) return foundArgument; } +#endif /* * mongo_query_document @@ -148,7 +148,8 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * t2(_id NAME, old INT, alias VARCHAR) * * SQL query: - * SELECT * FROM t1 LEFT JOIN t2 ON(t1.age = t2.old) WHERE name = 'xyz'; + * SELECT * FROM t1 LEFT JOIN t2 ON (t1.age = t2.old) + * WHERE (t1.age % 2) = 1; * * Equivalent MongoDB query: * @@ -176,7 +177,16 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * "as": "Join_Result" * } * }, - * { "$match": { "name" : "xyz" } }, + * { "$match" : + * { + * "$expr" : + * { "$and" : [ + * { "$eq" : [ { "$mod" : [ "$age", 2] }, 1]}, + * { "$ne" : [ "$age", null ] } + * ] + * } + * } + * } * { * "$unwind": * { @@ -197,12 +207,6 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * The outer $match stage (2nd element of root pipeline array) represents * remote_exprs, and $match inside $lookup stage represents the join clauses. * - * For the conditions in WHERE i.e. remote_exprs, this function can only - * transform simple comparison expressions and returns these transformed - * expressions in a BSON document. For example, simple expressions: - * "l_shipdate >= date '1994-01-01' AND l_shipdate < date '1995-01-01'" becomes - * "l_shipdate: { $gte: new Date(757382400000), $lt: new Date(788918400000) }". - * * For grouping target, add $group stage on the base relation or join relation. * The HAVING clause is nothing but a post $match stage. * @@ -239,17 +243,33 @@ mongo_query_document(ForeignScanState *scanStateNode) #ifdef META_DRIVER MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; BSON root_pipeline; + BSON match_stage; int root_index = 0; List *joinclauses; + List *colnum_list; List *colname_list = NIL; List *isouter_list = NIL; + List *rti_list; char *inner_relname; char *outer_relname; HTAB *columnInfoHash; int jointype; + int natts; - /* Prepare array of stages */ - bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); + /* 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); + + /* Length should be same for all lists of column information */ + natts = list_length(colname_list); + Assert(natts == list_length(colnum_list) && natts == list_length(rti_list) + && natts == list_length(isouter_list)); + + /* Store information in the hash-table */ + columnInfoHash = column_info_hash(colname_list, colnum_list, rti_list, + isouter_list); if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { @@ -265,29 +285,8 @@ mongo_query_document(ForeignScanState *scanStateNode) outer_relname = strVal(list_nth(innerouter_relname, 1)); } - if (fmstate->relType != BASE_REL) - { - List *colnum_list; - List *rti_list; - int natts; - - colname_list = list_nth(PrivateList, - mongoFdwPrivateJoinClauseColNameList); - colnum_list = list_nth(PrivateList, - mongoFdwPrivareJoinClauseColNumList); - rti_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseRtiList); - isouter_list = list_nth(PrivateList, - mongoFdwPrivateJoinClauseIsOuterList); - - /* Length should be same for all lists of column information */ - natts = list_length(colname_list); - Assert(natts == list_length(colnum_list) && - natts == list_length(rti_list) && - natts == list_length(isouter_list)); - - columnInfoHash = column_info_hash(colname_list, colnum_list, rti_list, - isouter_list); - } + /* Prepare array of stages */ + bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); #endif /* @@ -297,6 +296,21 @@ mongo_query_document(ForeignScanState *scanStateNode) */ if (opExpressionList) { +#ifdef META_DRIVER + pipeline_cxt context; + + context.colInfoHash = columnInfoHash; + context.isBoolExpr = false; + context.isJoinClause = false; + context.scanStateNode = scanStateNode; + + bsonAppendStartArray(filter, "pipeline", &match_stage); + + /* Form equivalent WHERE clauses in MongoDB */ + mongo_prepare_pipeline(opExpressionList, &match_stage, &context); + + bsonAppendFinishArray(filter, &match_stage); +#else Oid relationId; List *equalityOperatorList; List *comparisonOperatorList; @@ -335,33 +349,11 @@ mongo_query_document(ForeignScanState *scanStateNode) constant = (Const *) find_argument_of_type(argumentList, T_Const); paramNode = (Param *) find_argument_of_type(argumentList, T_Param); - if (relationId != 0) - { - columnId = column->varattno; + columnId = column->varattno; #if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(relationId, columnId); + columnName = get_relid_attribute_name(relationId, columnId); #else - columnName = get_attname(relationId, columnId, false); -#endif - } -#ifdef META_DRIVER - /* For join rel, use columnInfoHash to get column name */ - else - { - 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) - columnName = columnInfo->colName; - } + columnName = get_attname(relationId, columnId, false); #endif if (constant != NULL) @@ -399,25 +391,6 @@ mongo_query_document(ForeignScanState *scanStateNode) columnName = get_attname(relationId, columnId, false); #endif } -#ifdef META_DRIVER - /* For join rel, use columnInfoHash to get column name */ - else - { - 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) - columnName = columnInfo->colName; - } -#endif /* Find all expressions that correspond to the column */ columnOperatorList = column_operator_list(column, @@ -444,23 +417,15 @@ mongo_query_document(ForeignScanState *scanStateNode) operatorName = get_opname(columnOperator->opno); mongoOperatorName = mongo_operator_name(operatorName); -#ifdef META_DRIVER - if (constant != NULL) - append_constant_value(&childDocument, mongoOperatorName, - constant); - else - append_param_value(&childDocument, mongoOperatorName, paramNode, - scanStateNode); -#else if (constant != NULL) append_constant_value(filter, mongoOperatorName, constant); else append_param_value(filter, mongoOperatorName, paramNode, scanStateNode); -#endif } bsonAppendFinishObject(filter, &childDocument); } +#endif } if (!bsonFinish(filter)) { @@ -482,7 +447,6 @@ mongo_query_document(ForeignScanState *scanStateNode) BSON lookup_object; BSON lookup; BSON let_exprs; - BSON outer_match_stage; BSON unwind_stage; BSON unwind; BSON *inner_pipeline_doc = bsonCreate(); @@ -536,10 +500,11 @@ mongo_query_document(ForeignScanState *scanStateNode) context.colInfoHash = columnInfoHash; context.isBoolExpr = false; + context.isJoinClause = true; + context.scanStateNode = scanStateNode; /* Form equivalent join qual clauses in MongoDB */ - mongo_prepare_inner_pipeline(joinclauses, &inner_pipeline, - &context); + mongo_prepare_pipeline(joinclauses, &inner_pipeline, &context); bsonAppendFinishArray(inner_pipeline_doc, &inner_pipeline); } @@ -552,10 +517,8 @@ mongo_query_document(ForeignScanState *scanStateNode) bsonAppendFinishObject(&root_pipeline, &lookup_object); /* $match stage. This is to add a filter */ - bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), - &outer_match_stage); - bsonAppendBson(&outer_match_stage, "$match", filter); - bsonAppendFinishObject(&root_pipeline, &outer_match_stage); + if (opExpressionList) + bsonAppendBson(&root_pipeline, "$match", &match_stage); /* * $unwind stage. This deconstructs an array field from the input @@ -574,16 +537,8 @@ mongo_query_document(ForeignScanState *scanStateNode) fmstate->outerRelName = outer_relname; } - else - { - BSON match_stage; - - /* $match stage. This is to add a filter for the WHERE clause */ - bsonAppendStartObject(&root_pipeline, psprintf("%d", root_index++), - &match_stage); - bsonAppendBson(&match_stage, "$match", filter); - bsonAppendFinishObject(&root_pipeline, &match_stage); - } + else if (opExpressionList) + bsonAppendBson(&root_pipeline, "$match", &match_stage); /* Add $group stage for upper relation */ if (fmstate->relType == UPPER_JOIN_REL || fmstate->relType == UPPER_REL) @@ -725,77 +680,25 @@ mongo_query_document(ForeignScanState *scanStateNode) if (having_expr) { BSON match_stage; - BSON *filter = bsonCreate(); - List *equalityOperatorList; - List *comparisonOperatorList; - ListCell *equalityOperatorCell; - ListCell *comparisonoperatorCell; + 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); - equalityOperatorList = equality_operator_list(having_expr); - comparisonOperatorList = list_difference(having_expr, - equalityOperatorList); - /* Append equality expressions to the query */ - foreach(equalityOperatorCell, equalityOperatorList) - { - OpExpr *equalityOperator; - Const *constant; - List *argumentList; - - equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); - argumentList = equalityOperator->args; - constant = (Const *) find_argument_of_type(argumentList, - T_Const); - - if (constant != NULL) - append_constant_value(filter, "v_having", constant); - } - - foreach(comparisonoperatorCell, comparisonOperatorList) - { - BSON childDocument; - OpExpr *operator; - List *argumentList; - Const *constant; - char *operatorName; - char *mongoOperatorName; - - /* For comparison expressions, start a sub-document */ - bsonAppendStartObject(filter, "v_having", &childDocument); - - operator = (OpExpr *) lfirst(comparisonoperatorCell); - argumentList = operator->args; - constant = (Const *) find_argument_of_type(argumentList, - T_Const); - operatorName = get_opname(operator->opno); - mongoOperatorName = mongo_operator_name(operatorName); -#ifdef META_DRIVER - append_constant_value(&childDocument, mongoOperatorName, - constant); -#else - append_constant_value(filter, mongoOperatorName, constant); -#endif - bsonAppendFinishObject(filter, &childDocument); - } - - bsonAppendBson(&match_stage, "$match", filter); bsonAppendFinishObject(&root_pipeline, &match_stage); if (!bsonFinish(filter)) - { -#ifdef META_DRIVER ereport(ERROR, (errmsg("could not create document for query"), errhint("BSON flags: %d", queryDocument->flags))); -#else - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON error: %d", queryDocument->err))); -#endif - } } } @@ -854,6 +757,7 @@ mongo_operator_name(const char *operatorName) return (char *) mongoOperatorName; } +#ifndef META_DRIVER /* * equality_operator_list * Finds the equality (=) operators in the given list, and returns these @@ -929,8 +833,9 @@ column_operator_list(Var *column, List *operatorList) return columnOperatorList; } +#endif -static void +void append_param_value(BSON *queryDocument, const char *keyName, Param *paramNode, ForeignScanState *scanStateNode) { @@ -1372,15 +1277,11 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, * as being built-in), and that all collations used in the expression derive * from Vars of the foreign table. * - * For WHERE clauses, we only support simple binary operators that compare a - * column against a constant. If the expression is a tree, we don't recurse - * into it. - * - * For JOIN clauses, in addition to the above support, 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. + * 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, @@ -1404,15 +1305,14 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, { Var *var = (Var *) node; +#ifndef META_DRIVER /* Increment the Var count */ glob_cxt->varcount++; - +#endif /* * If the Var is from the foreign table, we consider its * collation (if any) safe to use. If it is from another - * table, we treat its collation the same way as we would a - * Param's collation, i.e. it's not safe for it to have a - * non-default collation. + * table, don't push it down. */ if (bms_is_member(var->varno, glob_cxt->relids) && var->varlevelsup == 0) @@ -1423,29 +1323,13 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, } else { - /* Var belongs to some other table */ - collation = var->varcollid; - if (var->varcollid != InvalidOid && - var->varcollid != DEFAULT_COLLATION_OID) - return false; - - if (collation == InvalidOid || - collation == DEFAULT_COLLATION_OID) - { - /* - * It's noncollatable, or it's safe to combine with a - * collatable foreign Var, so set state to NONE. - */ - state = FDW_COLLATE_NONE; - } - else - { - /* - * Do not fail right away, since the Var might appear - * in a collation-insensitive context. - */ - state = FDW_COLLATE_UNSAFE; - } + /* + * 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; @@ -1508,8 +1392,10 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, !glob_cxt->is_having_cond) return false; +#ifndef META_DRIVER /* Increment the operator expression count */ glob_cxt->opexprcount++; +#endif /* * We support =, <, >, <=, >=, <>, +, -, *, /, %, ^, |/, and @ @@ -1522,12 +1408,15 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* * Recurse to input subexpressions. * - * We support only =, <, >, <=, >= and <> operators for WHERE - * conditions of simple as well as join relation. + * 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) || - (!glob_cxt->is_join_cond && glob_cxt->opexprcount > 1)) + &inner_cxt) +#ifndef META_DRIVER + || (glob_cxt->opexprcount > 1) +#endif + ) return false; /* @@ -1596,8 +1485,11 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, foreach(lc, l) { if ((!foreign_expr_walker((Node *) lfirst(lc), - glob_cxt, &inner_cxt)) || - (!(glob_cxt->is_join_cond) && glob_cxt->varcount > 1)) + glob_cxt, &inner_cxt)) +#ifndef META_DRIVER + || (glob_cxt->varcount > 1) +#endif + ) return false; } @@ -1680,7 +1572,8 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, n = (Node *) tle->expr; } - if (!foreign_expr_walker(n, glob_cxt, &inner_cxt)) + if (!IsA(n, Var) || !foreign_expr_walker(n, glob_cxt, + &inner_cxt)) return false; } @@ -1777,7 +1670,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, */ bool mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, - bool is_join_cond, bool is_having_cond) + bool is_having_cond) { foreign_glob_cxt glob_cxt; foreign_loc_cxt loc_cxt; @@ -1801,9 +1694,10 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, else glob_cxt.relids = baserel->relids; +#ifndef META_DRIVER glob_cxt.varcount = 0; glob_cxt.opexprcount = 0; - glob_cxt.is_join_cond = is_join_cond; +#endif glob_cxt.is_having_cond = is_having_cond; loc_cxt.collation = InvalidOid; loc_cxt.state = FDW_COLLATE_NONE; @@ -1951,8 +1845,8 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, } /* - * mongo_prepare_inner_pipeline - * Form inner query pipeline syntax equivalent to postgresql join clauses. + * 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: @@ -1974,50 +1868,63 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, * ] */ static void -mongo_prepare_inner_pipeline(List *joinclause, BSON *inner_pipeline, - pipeline_cxt *context) +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; - int inner_pipeline_index = 0; - bsonAppendStartObject(inner_pipeline, - psprintf("%d", inner_pipeline_index++), - &match_object); - bsonAppendStartObject(&match_object, "$match", &match_stage); + 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 clause expression */ - mongo_append_joinclauses_to_inner_pipeline(joinclause, &and_op, context); + /* 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); - bsonAppendFinishObject(&match_object, &match_stage); - bsonAppendFinishObject(inner_pipeline, &match_object); + if (context->isJoinClause) + { + bsonAppendFinishObject(&match_object, &match_stage); + bsonAppendFinishObject(inner_pipeline, &match_object); + } + else + bsonAppendFinishObject(inner_pipeline, &match_stage); } /* - * mongo_append_joinclauses_to_inner_pipeline - * Append all join expressions to mongoDB's $and array. + * mongo_append_clauses_to_pipeline + * Append all JOIN/WHERE/HAVING clauses to mongoDB's $and array. */ static void -mongo_append_joinclauses_to_inner_pipeline(List *joinclause, BSON *child_doc, - pipeline_cxt *context) +mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc, + pipeline_cxt *context) { ListCell *lc; - /* loop through all join-clauses */ - foreach(lc, joinclause) + /* loop through all clauses */ + foreach(lc, clause) { Expr *expr = (Expr *) lfirst(lc); diff --git a/mongo_query.h b/mongo_query.h index d48323f..cde41a6 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -26,6 +26,10 @@ typedef struct pipeline_cxt 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; /* @@ -66,8 +70,6 @@ enum mongoFdwScanPrivateIndex /* Relation Type (BASE/JOIN/UPPER/UPPER_JOIN) */ mongoFdwPrivateRelType, - /* Join information */ - /* * List of column name, attribute number, range table index, and whether * this column is of outer relation or not. @@ -130,5 +132,8 @@ 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); #endif /* MONGO_QUERY_H */ diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index 30ed550..625f077 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -72,8 +72,8 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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 > 10 ORDER BY 1; -SELECT sum(t1.c8), avg(t2.c1) FROM fdw137_t1 t1 INNER JOIN fdw137_t2 t2 ON (t1.c8 = t2.c1) WHERE t1.c8 > 10 ORDER BY 1; +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; +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; 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, 3; 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, 3; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 418e93d..26a8c0c 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -255,7 +255,7 @@ WITH t (c1_1, c1_3, c2_1) AS ( 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; --- This won't push-down because WHERE only pushes operator expression. +-- 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; diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 47f841e..e011fb8 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -143,17 +143,25 @@ SELECT c1, c2 FROM f_test_tbl1 SELECT c1, c2 FROM f_test_tbl1 WHERE c2 < 'EMP10'; --- Should not push down if two columns of same table is +-- Should push down if two columns of same table are -- involved in single WHERE clause operator expression. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, c6 FROM f_test_tbl1 - WHERE c1 = c6 AND c1 = 1100 +SELECT c1, c4 FROM f_test_tbl1 + WHERE c1 > c4 ORDER BY c1; -SELECT c1, c6 FROM f_test_tbl1 - WHERE c1 = c6 AND c1 = 1100 +SELECT c1, c4 FROM f_test_tbl1 + WHERE c1 > c4 ORDER BY c1; --- Nested operator expression in WHERE clause. Shouldn't push down. +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; + +-- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 WHERE (c1 > 1000) > FALSE; @@ -193,8 +201,25 @@ SELECT name, marks FROM f_test_tbl3 WHERE pass = true ORDER BY name; +-- 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; + -- 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; From 637e4f695663de0b121151794eec85d42d461ee0 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 9 Sep 2022 10:52:29 +0530 Subject: [PATCH 194/239] Fix expected output changes for legacy code path. Commit a5b12ba20f6390469c3f6d35a458cc9cf39d1cae missed that. Vaibhav Dalvi. --- expected/pushdown_1.out | 43 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index f06553f..2d54c21 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -406,6 +406,32 @@ SELECT c1, c4 FROM f_test_tbl1 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 + Filter: ((f_test_tbl1.c1 < f_test_tbl1.c4) AND (f_test_tbl1.c7 < f_test_tbl1.c8)) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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) + -- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 @@ -568,6 +594,23 @@ SELECT c1 FROM f_test_tbl2 WHERE c2 = c3; ---- (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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; From e10547f93ad90e012c440d401a887215188d4437 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 13 Oct 2022 14:29:01 +0530 Subject: [PATCH 195/239] Push down ORDER BY to remote MongoDB servers. If possible, consider a path that adds the ORDER BY clause to the remote SQL 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 push ORDER BY with either ASC NULLS FIRST or DESC NULLS LAST to the remote server. Moreover, as MongoDB sorts only on fields, only column names in ORDER BY expressions are pushed down. FDW-134, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Ajay Pal, and a few cosmetic changes by Jeevan Chalke. --- README.md | 9 + mongo_fdw.c | 720 +++++++++++++++++++++++++++++++++++++++++++++++++- mongo_fdw.h | 6 + mongo_query.c | 79 +++++- mongo_query.h | 4 + 5 files changed, 808 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 14a2851..2b63ad0 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,15 @@ 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. + ### New MongoDB C Driver Support This enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But diff --git a/mongo_fdw.c b/mongo_fdw.c index 9ceb9fb..5503bc7 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -38,6 +38,7 @@ #if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" #endif +#include "optimizer/paths.h" #include "optimizer/tlist.h" #if PG_VERSION_NUM < 120000 #include "optimizer/var.h" @@ -63,6 +64,39 @@ PG_MODULE_MAGIC; */ #define CODE_VERSION 50400 +#ifdef META_DRIVER +/* + * 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. + */ +#define IS_PATHKEY_PUSHABLE(pathkey) \ + ((pathkey->pk_strategy == BTLessStrategyNumber && pathkey->pk_nulls_first) || \ + (pathkey->pk_strategy != BTLessStrategyNumber && !pathkey->pk_nulls_first)) + +/* 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 +#endif + +/* + * 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 + */ +enum FdwPathPrivateIndex +{ + /* has-final-sort flag (as an integer Value node) */ + FdwPathPrivateHasFinalSort, +}; + extern PGDLLEXPORT void _PG_init(void); PG_FUNCTION_INFO_V1(mongo_fdw_handler); @@ -210,6 +244,28 @@ static void bson_to_json_string(StringInfo output, BSON_ITERATOR iter, static void mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, Oid foreigntableid); +#ifdef META_DRIVER +static List *mongo_get_useful_ecs_for_relation(PlannerInfo *root, + RelOptInfo *rel); +static List *mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, + RelOptInfo *rel); +static void mongo_add_paths_with_pathkeys(PlannerInfo *root, + RelOptInfo *rel, + Path *epq_path, + Cost base_startup_cost, + Cost base_total_cost); +static Expr *mongo_find_em_expr_for_input_target(PlannerInfo *root, + EquivalenceClass *ec, + RelOptInfo *rel); +static Expr *mongo_find_em_expr_for_rel(PlannerInfo *root, + EquivalenceClass *ec, RelOptInfo *rel); +#if PG_VERSION_NUM >= 120000 +static void mongo_add_foreign_ordered_paths(PlannerInfo *root, + RelOptInfo *input_rel, + RelOptInfo *ordered_rel); +#endif +#endif + /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 static JsonSemAction nullSemAction = @@ -511,6 +567,11 @@ mongoGetForeignPaths(PlannerInfo *root, /* Add foreign path as the only possible path */ add_path(baserel, foreignPath); + +#ifdef META_DRIVER + /* Add paths with pathkeys */ + mongo_add_paths_with_pathkeys(root, baserel, NULL, startupCost, totalCost); +#endif } /* @@ -546,6 +607,16 @@ mongoGetForeignPlan(PlannerInfo *root, #ifdef META_DRIVER MongoRelQualInfo *qual_info; MongoFdwRelationInfo *ofpinfo; + List *pathKeyList = NIL; + List *isAscSortList = NIL; + bool has_final_sort = false; + + /* + * 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)); #endif /* Set scan relation id */ @@ -750,6 +821,7 @@ mongoGetForeignPlan(PlannerInfo *root, 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; @@ -858,11 +930,53 @@ mongoGetForeignPlan(PlannerInfo *root, */ mongo_prepare_qual_info(quals, qual_info); } +#else + quals = remote_exprs; +#endif + + /* + * Check the ORDER BY clause, and if we found any useful pathkeys, then + * store the required information. + */ +#ifdef META_DRIVER + foreach(lc, best_path->path.pathkeys) + { + PathKey *pathkey = lfirst(lc); + Expr *em_expr; + + if (has_final_sort) + { + /* + * By construction, foreignrel is the input relation to the final + * sort. + */ + em_expr = mongo_find_em_expr_for_input_target(root, + pathkey->pk_eclass, + foreignrel); + } + else + em_expr = mongo_find_em_expr_for_rel(root, pathkey->pk_eclass, + qual_info->foreignRel); + + Assert(em_expr != NULL); + /* Ignore binary-compatible relabeling */ + 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 (pathkey->pk_strategy == BTLessStrategyNumber) + 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); -#else - quals = remote_exprs; #endif /* @@ -887,6 +1001,9 @@ mongoGetForeignPlan(PlannerInfo *root, 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); + if (IS_JOIN_REL(foreignrel) || IS_UPPER_REL(foreignrel)) { MongoFdwRelationInfo *tfpinfo = NULL; @@ -3077,7 +3194,9 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, /* Add generated path into joinrel by add_path(). */ add_path(joinrel, (Path *) joinpath); - /* XXX Consider pathkeys for the join relation */ + /* Add paths with pathkeys */ + mongo_add_paths_with_pathkeys(root, joinrel, epq_path, startup_cost, + total_cost); /* XXX Consider parameterized paths for the join relation */ } @@ -3614,14 +3733,34 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, return; /* Ignore stages we don't support; and skip any duplicate calls. */ - if (stage != UPPERREL_GROUP_AGG || output_rel->fdw_private) +#if PG_VERSION_NUM >= 120000 + if ((stage != UPPERREL_GROUP_AGG && stage != UPPERREL_ORDERED) || +#else + if (stage != UPPERREL_GROUP_AGG || +#endif + output_rel->fdw_private) return; fpinfo = (MongoFdwRelationInfo *) palloc0(sizeof(MongoFdwRelationInfo)); fpinfo->pushdown_safe = false; + fpinfo->stage = stage; output_rel->fdw_private = fpinfo; -#if PG_VERSION_NUM >= 110000 +#if PG_VERSION_NUM >= 120000 + 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; + default: + elog(ERROR, "unexpected upper relation: %d", (int) stage); + break; + } +#elif PG_VERSION_NUM >= 110000 mongo_add_foreign_grouping_paths(root, input_rel, output_rel, (GroupPathExtraData *) extra); #else @@ -3754,3 +3893,574 @@ mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, *total_cost = baserel->rows + *startup_cost; } + +#ifdef META_DRIVER +/* + * 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); + EquivalenceClass *pathkey_ec = pathkey->pk_eclass; + Expr *em_expr; + + /* 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 (!(em_expr = mongo_find_em_expr_for_rel(root, pathkey_ec, rel))) + { + query_pathkeys_ok = false; + break; + } + + /* Ignore binary-compatible relabeling */ + while (em_expr && IsA(em_expr, RelabelType)) + em_expr = ((RelabelType *) em_expr)->arg; + + /* Only Vars are allowed per MongoDB. */ + if (!IsA(em_expr, Var)) + { + 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); + Expr *em_expr; + PathKey *pathkey; + + /* If redundant with what we did above, skip it. */ + if (cur_ec == query_ec) + continue; + + /* If no pushable expression for this rel, skip it. */ + if (!(em_expr = mongo_find_em_expr_for_rel(root, cur_ec, rel))) + continue; + + /* Ignore binary-compatible relabeling */ + 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; + + 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. + */ +static void +mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, + Path *epq_path, Cost base_startup_cost, + Cost base_total_cost) +{ + ListCell *lc; + List *useful_pathkeys_list = NIL; /* List of all pathkeys */ + + /* + * 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 PG_VERSION_NUM >= 120000 + if (IS_SIMPLE_REL(rel)) + 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)); + 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)); +#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)); +#endif + } +} + +/* + * mongo_find_em_expr_for_rel + * Find an equivalence class member expression, all of whose Vars, come + * from the indicated relation. + */ +static Expr * +mongo_find_em_expr_for_rel(PlannerInfo *root, EquivalenceClass *ec, + RelOptInfo *rel) +{ + ListCell *lc_em; + + foreach(lc_em, ec->ec_members) + { + EquivalenceMember *em = (EquivalenceMember *) lfirst(lc_em); + + 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->em_expr; + } + } + + /* 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. + */ +#if PG_VERSION_NUM >= 120000 +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; + + /* 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; + 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. + */ + sort_expr = mongo_find_em_expr_for_input_target(root, pathkey_ec, + input_rel); + if (!sort_expr) + return; + + /* Ignore binary-compatible relabeling */ + 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 = 15; + 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_make1(makeInteger(true)); + + /* Create foreign ordering path */ + 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); + + /* and add it to the ordered_rel */ + add_path(ordered_rel, (Path *) ordered_path); +} +#endif /* PG_VERSION_NUM >= 120000 */ + +/* + * mongo_find_em_expr_for_input_target + * Find an equivalence class member expression to be computed as a sort + * column in the given target. + */ +static Expr * +mongo_find_em_expr_for_input_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->em_expr; + } + + i++; + } + + return NULL; /* keep compiler quiet */ +} +#endif /* End of META_DRIVER */ diff --git a/mongo_fdw.h b/mongo_fdw.h index 60459f4..6e64cdd 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -347,6 +347,9 @@ typedef struct MongoFdwRelationInfo */ 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; @@ -360,6 +363,9 @@ typedef struct MongoFdwRelationInfo /* Grouping information */ List *grouped_tlist; List *groupbyColList; + + /* Upper relation information */ + UpperRelationKind stage; } MongoFdwRelationInfo; /* diff --git a/mongo_query.c b/mongo_query.c index 28713c2..bc805fb 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -149,8 +149,9 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * * SQL query: * SELECT * FROM t1 LEFT JOIN t2 ON (t1.age = t2.old) - * WHERE (t1.age % 2) = 1; - * + * WHERE (t1.age % 2) = 1 + * ORDER BY t1.age ASC NULLS FIRST; + * Equivalent MongoDB query: * * db.t1.aggregate([ @@ -193,7 +194,8 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * "path": "$Join_Result", * "preserveNullAndEmptyArrays": true * } - * } + * }, + * { "$sort": { "age" : 1 } } * ]) * * Any MongoDB query would have the following three main arrays: @@ -213,7 +215,8 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * Example: Consider above table t1: * * SQL query: - * SELECT name, SUM(age) FROM t1 GROUP BY name HAVING MIN(name) = 'xyz'; + * SELECT name, SUM(age) FROM t1 GROUP BY name HAVING MIN(name) = 'xyz' + * ORDER BY name DESC NULLS LAST; * * Equivalent MongoDB query: * @@ -229,7 +232,11 @@ find_argument_of_type(List *argumentList, NodeTag argumentType) * { * "$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) @@ -250,6 +257,8 @@ mongo_query_document(ForeignScanState *scanStateNode) List *colname_list = NIL; List *isouter_list = NIL; List *rti_list; + List *pathkey_list; + List *is_ascsort_list; char *inner_relname; char *outer_relname; HTAB *columnInfoHash; @@ -271,6 +280,10 @@ mongo_query_document(ForeignScanState *scanStateNode) 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); + if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { List *innerouter_relname; @@ -702,6 +715,62 @@ mongo_query_document(ForeignScanState *scanStateNode) } } + /* 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 */ + } + bsonAppendFinishArray(queryDocument, &root_pipeline); if (!bsonFinish(queryDocument)) @@ -1455,7 +1524,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* * RelabelType must not introduce a collation not derived from - * an input foreign Var (same logic as for a real function). + * an input foreign Var. */ collation = r->resultcollid; if (collation == InvalidOid) diff --git a/mongo_query.h b/mongo_query.h index cde41a6..1fbd2af 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -81,6 +81,10 @@ enum mongoFdwScanPrivateIndex mongoFdwPrivateJoinClauseRtiList, mongoFdwPrivateJoinClauseIsOuterList, + /* ORDER BY clause information */ + mongoFdwPrivatePathKeyList, + mongoFdwPrivateIsAscSortList, + /* Upper relation information */ /* Upper relation grouping operation name list */ From 8795246d499eed60942057ed983e7dbded696052 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 13 Oct 2022 14:29:01 +0530 Subject: [PATCH 196/239] Add test coverage for the ORDER BY push-down. FDW-134, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Ajay Pal. --- data/mongo_test_data.js | 8 + expected/aggregate_pushdown.out | 261 ++++-- expected/aggregate_pushdown_1.out | 261 ++++-- expected/aggregate_pushdown_2.out | 287 ++++-- expected/aggregate_pushdown_3.out | 1426 +++++++++++++++++++++++++++++ expected/join_pushdown.out | 248 +++-- expected/join_pushdown_1.out | 248 +++-- expected/join_pushdown_2.out | 248 +++-- expected/join_pushdown_3.out | 248 +++-- expected/pushdown.out | 255 ++++-- expected/pushdown_1.out | 211 +++-- expected/select.out | 29 +- expected/select_1.out | 6 +- sql/aggregate_pushdown.sql | 100 +- sql/join_pushdown.sql | 56 +- sql/pushdown.sql | 102 ++- sql/select.sql | 4 +- 17 files changed, 2966 insertions(+), 1032 deletions(-) create mode 100644 expected/aggregate_pushdown_3.out diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index b24548f..a09926b 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -104,3 +104,11 @@ 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)} +]); diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 493cc59..58edf60 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -22,22 +22,22 @@ 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 +-- 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, 2; +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(*)), (sum(fdw137_t1.c1)) + 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, 2; +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 @@ -48,18 +48,15 @@ SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1):: -- 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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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(*)) - Sort Key: fdw137_t1.c1 - -> Foreign Scan - Output: c1, (sum(c1)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -76,18 +73,15 @@ SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORD (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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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)) - Sort Key: fdw137_t1.c8 - -> Foreign Scan - Output: c8, (min(c2)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -95,18 +89,15 @@ SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) -- 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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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 - Sort Key: fdw137_t1.c2 - -> Foreign Scan - Output: c2, (sum(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -124,22 +115,19 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY -- 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; - QUERY PLAN --------------------------------------------------------------- - Sort - Output: c1, (sum((c1 + 2))) - Sort Key: fdw137_t1.c1 - -> HashAggregate - 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 -(10 rows) +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; +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 @@ -219,6 +207,35 @@ SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY 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; @@ -260,41 +277,38 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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; +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)) + 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; +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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------- - Sort +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 - Sort Key: t1.c1, t2.c4 - -> 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)) -(6 rows) + 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, 3; +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 - 10 | 1 | 20 | 2 | 400 20 | 1 | 800 20 | 1 | 900 @@ -304,18 +318,15 @@ SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c (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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ - Sort +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)) - Sort Key: t1.c8 - -> 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) + 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; +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 @@ -1247,16 +1258,13 @@ 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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ - Sort +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 - Sort Key: t1.c8 - -> 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)) -(6 rows) + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) +(3 rows) -- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON. -- Shouldn't push down join as well as aggregation. @@ -1286,6 +1294,94 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; @@ -1295,6 +1391,7 @@ 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; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index cc6915a..141f45c 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -22,22 +22,22 @@ 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 +-- 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, 2; +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(*)), (sum(fdw137_t1.c1)) + 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, 2; +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 @@ -48,18 +48,15 @@ SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1):: -- 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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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(*)) - Sort Key: fdw137_t1.c1 - -> Foreign Scan - Output: c1, (sum(c1)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -76,18 +73,15 @@ SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORD (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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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)) - Sort Key: fdw137_t1.c8 - -> Foreign Scan - Output: c8, (min(c2)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -95,18 +89,15 @@ SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) -- 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; - QUERY PLAN ---------------------------------------------------------------------------------- - Sort +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 - Sort Key: fdw137_t1.c2 - -> Foreign Scan - Output: c2, (sum(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 rows) + 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; +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 @@ -124,22 +115,19 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY -- 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; - QUERY PLAN --------------------------------------------------------------- - Sort - Output: c1, (sum((c1 + 2))) - Sort Key: fdw137_t1.c1 - -> HashAggregate - 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 -(10 rows) +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; +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 @@ -219,6 +207,35 @@ SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY 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; @@ -260,41 +277,38 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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; +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)) + 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; +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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------- - Sort +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 - Sort Key: t1.c1, t2.c4 - -> 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)) -(6 rows) + 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, 3; +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 - 10 | 1 | 20 | 2 | 400 20 | 1 | 800 20 | 1 | 900 @@ -304,18 +318,15 @@ SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c (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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ - Sort +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)) - Sort Key: t1.c8 - -> 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) + 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; +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 @@ -1247,16 +1258,13 @@ 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; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------ - Sort +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 - Sort Key: t1.c8 - -> 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)) -(6 rows) + Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) +(3 rows) -- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON. -- Shouldn't push down join as well as aggregation. @@ -1286,6 +1294,94 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; @@ -1295,6 +1391,7 @@ 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; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index b88f90c..756b0a4 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -22,22 +22,22 @@ 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 +-- 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, 2; +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(*)), (sum(fdw137_t1.c1)) + 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, 2; +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 @@ -48,18 +48,18 @@ SELECT count(*), sum(c1), avg(c1), min(c4), max(c1), sum(c1) * (random() <= 1):: -- 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; +SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 ASC NULLS FIRST; QUERY PLAN --------------------------------------------------------------------------------- Sort Output: c1, (sum(c1)), (count(*)) - Sort Key: fdw137_t1.c1 + Sort Key: fdw137_t1.c1 NULLS FIRST -> Foreign Scan Output: c1, (sum(c1)), (count(*)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) (6 rows) -SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; +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 @@ -76,18 +76,18 @@ SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORD (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; +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 --------------------------------------------------------------------------------- Sort Output: c8, (min(c2)) - Sort Key: fdw137_t1.c8 + Sort Key: fdw137_t1.c8 NULLS FIRST -> Foreign Scan Output: c8, (min(c2)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) (6 rows) -SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; +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 @@ -95,18 +95,18 @@ SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) -- 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; +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 + 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; +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 @@ -124,22 +124,19 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY -- 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; - QUERY PLAN --------------------------------------------------------------- - Sort - Output: c1, (sum((c1 + 2))) - Sort Key: fdw137_t1.c1 - -> HashAggregate - 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 -(10 rows) +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; +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 @@ -219,6 +216,35 @@ SELECT c1, sum(c1) FROM fdw137_t1 GROUP BY c1 HAVING min(c1 * 3) > 500 ORDER BY 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; @@ -260,62 +286,64 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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; +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)) + 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; +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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------------------------- - Sort - Output: t1.c1, (count(*)), t2.c4 - Sort Key: t1.c1, t2.c4 +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 +--------------------------------------------------------------------------------------------------------- + GroupAggregate + Output: t1.c1, count(*), t2.c4 + Group Key: t1.c1, t2.c4 -> 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)) + Output: t1.c1, t2.c4 + Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2) (6 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, 3; +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 | 700 10 | 1 | 900 10 | 1 | - 20 | 2 | 400 - 20 | 1 | 800 - 20 | 1 | 900 + 10 | 1 | 700 20 | 1 | 1300 - 30 | 5 | 600 + 20 | 1 | 900 + 20 | 1 | 400 + 20 | 1 | 800 + 20 | 1 | 400 + 30 | 3 | 600 30 | 1 | 900 -(9 rows) + 30 | 2 | 600 +(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; +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 + 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; +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 @@ -1032,7 +1060,6 @@ SELECT c2, least_agg(c1) FROM fdw137_t1 GROUP BY c2 ORDER BY c2; -- Test partition-wise aggregate SET enable_partitionwise_aggregate TO ON; -ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" -- 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) @@ -1042,22 +1069,19 @@ CREATE FOREIGN TABLE ftprt1_p2 PARTITION OF fprt1 FOR VALUES FROM (5) TO (8) -- Plan with partitionwise aggregates is enabled EXPLAIN (VERBOSE, COSTS OFF) SELECT c1, sum(c1) FROM fprt1 GROUP BY c1 ORDER BY 2; - QUERY PLAN ----------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------- Sort Output: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) Sort Key: (sum(ftprt1_p1.c1)) - -> HashAggregate - Output: ftprt1_p1.c1, sum(ftprt1_p1.c1) - Group Key: ftprt1_p1.c1 - -> Append - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Foreign Namespace: mongo_fdw_regress.test2 -(13 rows) + -> Append + -> Foreign Scan + Output: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) + -> Foreign Scan + Output: ftprt1_p2.c1, (sum(ftprt1_p2.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 @@ -1074,23 +1098,19 @@ 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; - QUERY PLAN ------------------------------------------------------------------------------- + QUERY PLAN +------------------------------------------------------------------------------------------ Sort Output: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) Sort Key: (sum(ftprt1_p1.c2)) - -> HashAggregate - Output: ftprt1_p1.c1, sum(ftprt1_p1.c2), min(ftprt1_p1.c2), count(*) - Group Key: ftprt1_p1.c1 - Filter: (avg(ftprt1_p1.c2) < '22'::numeric) - -> Append - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1, ftprt1_p1.c2 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1, ftprt1_p2.c2 - Foreign Namespace: mongo_fdw_regress.test2 -(14 rows) + -> Append + -> Foreign Scan + Output: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) + -> Foreign Scan + Output: ftprt1_p2.c1, (sum(ftprt1_p2.c2)), (min(ftprt1_p2.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 @@ -1114,18 +1134,22 @@ SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1; Sort Output: t1.c1, (count(((t1.*)::fprt1))) Sort Key: t1.c1 - -> HashAggregate - Output: t1.c1, count(((t1.*)::fprt1)) - Group Key: t1.c1 - Filter: (avg(t1.c2) < '22'::numeric) - -> Append + -> 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 -(14 rows) +(18 rows) SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1; c1 | count @@ -1141,7 +1165,6 @@ SELECT c1, count(t1) FROM fprt1 t1 GROUP BY c1 HAVING avg(c2) < 22 ORDER BY 1; (8 rows) SET enable_partitionwise_aggregate TO OFF; -ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" -- 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'); @@ -1252,12 +1275,12 @@ 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; +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 ----------------------------------------------------------------------------------------------------------------------- Sort Output: (sum(t2.c1)), t1.c8 - Sort Key: t1.c8 + Sort Key: t1.c8 NULLS FIRST -> 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)) @@ -1291,6 +1314,97 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 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 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + 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 + 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 + -> 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) +(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 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; @@ -1300,6 +1414,7 @@ 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; diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out new file mode 100644 index 0000000..164d931 --- /dev/null +++ b/expected/aggregate_pushdown_3.out @@ -0,0 +1,1426 @@ +\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 'edb' user with 'edb' 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 +--------------------------------------------------------------------------------- + Sort + Output: c1, (sum(c1)), (count(*)) + Sort Key: fdw137_t1.c1 NULLS FIRST + -> Foreign Scan + Output: c1, (sum(c1)), (count(*)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 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 +--------------------------------------------------------------------------------- + Sort + Output: c8, (min(c2)) + Sort Key: fdw137_t1.c8 NULLS FIRST + -> Foreign Scan + Output: c8, (min(c2)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) +(6 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 +--------------------------------------------------------------------------------- + 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) + +-- 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (max(fdw137_t1.c4)) + -> HashAggregate + Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + max +------ + 400 + 600 + 700 + 800 + 900 + 1300 + +(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 +--------------------------------------------------------------------------------------------------------- + GroupAggregate + Output: t1.c1, count(*), t2.c4 + Group Key: t1.c1, t2.c4 + -> Foreign Scan + Output: t1.c1, t2.c4 + Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2) +(6 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 | 900 + 10 | 1 | + 10 | 1 | 700 + 20 | 1 | 1300 + 20 | 1 | 900 + 20 | 1 | 400 + 20 | 1 | 800 + 20 | 1 | 400 + 30 | 3 | 600 + 30 | 1 | 900 + 30 | 2 | 600 +(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) + +-- 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; +ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" +-- 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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) + Sort Key: (sum(ftprt1_p1.c1)) + -> HashAggregate + Output: ftprt1_p1.c1, sum(ftprt1_p1.c1) + Group Key: ftprt1_p1.c1 + -> Append + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1 + Foreign Namespace: mongo_fdw_regress.test2 +(13 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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) + Sort Key: (sum(ftprt1_p1.c2)) + -> HashAggregate + Output: ftprt1_p1.c1, sum(ftprt1_p1.c2), min(ftprt1_p1.c2), count(*) + Group Key: ftprt1_p1.c1 + Filter: (avg(ftprt1_p1.c2) < '22'::numeric) + -> Append + -> Foreign Scan on public.ftprt1_p1 + Output: ftprt1_p1.c1, ftprt1_p1.c2 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 + Output: ftprt1_p2.c1, ftprt1_p2.c2 + Foreign Namespace: mongo_fdw_regress.test2 +(14 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 + -> HashAggregate + Output: t1.c1, count(((t1.*)::fprt1)) + Group Key: t1.c1 + Filter: (avg(t1.c2) < '22'::numeric) + -> Append + -> Foreign Scan on public.ftprt1_p1 t1 + Output: t1.c1, t1.*, t1.c2 + Foreign Namespace: mongo_fdw_regress.test1 + -> Foreign Scan on public.ftprt1_p2 t1_1 + Output: t1_1.c1, t1_1.*, t1_1.c2 + Foreign Namespace: mongo_fdw_regress.test2 +(14 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; +ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" +-- 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 +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (sum(t2.c1)), t1.c8 + Sort Key: t1.c8 NULLS FIRST + -> 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)) +(6 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 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + 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 + 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 + -> 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) +(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 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) + +-- 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; diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index ee6f10c..8b24ebb 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -36,19 +36,18 @@ CREATE TABLE l_test_tbl1 AS -- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -65,58 +64,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3 OFFSET 50; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- + 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 +------------------------------------------------------------------------------------------------------ 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) + -> Foreign Scan + 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 OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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 | | @@ -136,24 +131,22 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 | | | | @@ -165,25 +158,23 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (12 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 LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -204,58 +195,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -272,7 +259,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) EXPLAIN (COSTS OFF) @@ -353,17 +339,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 @@ -512,17 +496,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- (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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 rows) + 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, d.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 @@ -532,18 +514,16 @@ SELECT d.c1, e.c1 -- 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Filter: (abs(c8) = c1) - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(5 rows) + 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, d.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 @@ -554,11 +534,11 @@ 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, 3; + 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, e.c1 + 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 @@ -568,9 +548,10 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 (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, 3; + 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 @@ -587,7 +568,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) RESET enable_mergejoin; @@ -596,11 +576,11 @@ RESET enable_nestloop; 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, 3; + 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, l.c8 + 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 @@ -613,7 +593,7 @@ SELECT l.c1, l.c6, l.c8 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, 3; + 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 @@ -737,17 +717,15 @@ 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(4 rows) + 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, 3; + 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 | 700 | EMP7 | 2450.34 | 10 @@ -909,18 +887,16 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(5 rows) + 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, 3; + 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 diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index aafb8aa..23828f6 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -36,19 +36,18 @@ CREATE TABLE l_test_tbl1 AS -- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -65,58 +64,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3 OFFSET 50; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- + 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 +------------------------------------------------------------------------------------------------------ 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) + -> Foreign Scan + 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 OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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 | | @@ -136,24 +131,22 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 | | | | @@ -165,25 +158,23 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (12 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 LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -204,58 +195,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -272,7 +259,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) EXPLAIN (COSTS OFF) @@ -353,17 +339,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 @@ -512,17 +496,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- (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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 rows) + 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, d.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 @@ -532,18 +514,16 @@ SELECT d.c1, e.c1 -- 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Filter: (abs(c8) = c1) - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(5 rows) + 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, d.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 @@ -554,11 +534,11 @@ 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, 3; + 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, e.c1 + 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 @@ -568,9 +548,10 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 (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, 3; + 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 @@ -587,7 +568,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) RESET enable_mergejoin; @@ -596,11 +576,11 @@ RESET enable_nestloop; 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, 3; + 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, l.c8 + 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 @@ -613,7 +593,7 @@ SELECT l.c1, l.c6, l.c8 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, 3; + 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 @@ -737,17 +717,15 @@ 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(4 rows) + 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, 3; + 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 | 700 | EMP7 | 2450.34 | 10 @@ -909,18 +887,16 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(5 rows) + 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, 3; + 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 diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index cd435dd..a9b7d96 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -36,19 +36,18 @@ CREATE TABLE l_test_tbl1 AS -- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -65,58 +64,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3 OFFSET 50; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- + 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 +------------------------------------------------------------------------------------------------------ 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) + -> Foreign Scan + 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 OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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 | | @@ -136,24 +131,22 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 | | | | @@ -165,25 +158,23 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (12 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 LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -204,58 +195,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -272,7 +259,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) EXPLAIN (COSTS OFF) @@ -353,17 +339,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 @@ -512,17 +496,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- (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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 rows) + 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, d.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 @@ -532,18 +514,16 @@ SELECT d.c1, e.c1 -- 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Filter: (abs(c8) = c1) - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(5 rows) + 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, d.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 @@ -554,11 +534,11 @@ 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, 3; + 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, e.c1 + 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 @@ -568,9 +548,10 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 (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, 3; + 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 @@ -587,7 +568,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) RESET enable_mergejoin; @@ -596,11 +576,11 @@ RESET enable_nestloop; 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, 3; + 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, l.c8 + 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 @@ -613,7 +593,7 @@ SELECT l.c1, l.c6, l.c8 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, 3; + 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 @@ -737,17 +717,15 @@ 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(4 rows) + 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, 3; + 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 | 700 | EMP7 | 2450.34 | 10 @@ -909,18 +887,16 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(5 rows) + 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, 3; + 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 diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 614df54..7a22654 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -36,19 +36,18 @@ CREATE TABLE l_test_tbl1 AS -- 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -65,58 +64,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3 OFFSET 50; - QUERY PLAN ------------------------------------------------------------------------------------------------------------- + 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 +------------------------------------------------------------------------------------------------------ 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) + -> Foreign Scan + 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 OUTER JOIN f_test_tbl1 e ON (d.c1 = e.c8 AND e.c4 > d.c1 OR e.c2 < d.c3) ORDER BY 1, 3 OFFSET 50; + 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 | | @@ -136,24 +131,22 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 | | | | @@ -165,25 +158,23 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (12 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 LEFT OUTER JOIN f_test_tbl1 e ON d.c1 = 20 ORDER BY 1, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -204,58 +195,54 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- - 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 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | (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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - 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) -(4 rows) + 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, 3; + 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 @@ -272,7 +259,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) EXPLAIN (COSTS OFF) @@ -353,17 +339,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- 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, 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) + 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, 3; + 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 ----+----------------+------+-------+---------+---- 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 @@ -512,17 +496,15 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- (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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(4 rows) + 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, d.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 @@ -532,18 +514,16 @@ SELECT d.c1, e.c1 -- 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, d.c1; - QUERY PLAN -------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c3 - -> Foreign Scan - Filter: (abs(c8) = c1) - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) -(5 rows) + 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, d.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 @@ -554,11 +534,11 @@ 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, 3; + 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, e.c1 + 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 @@ -568,9 +548,10 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 (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, 3; + 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 @@ -587,7 +568,6 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 30 | SALES | 1200 | EMP12 | 950 | 30 40 | HR | | | | 50 | TESTING | | | | - | | | | | (17 rows) RESET enable_mergejoin; @@ -596,11 +576,11 @@ RESET enable_nestloop; 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, 3; + 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, l.c8 + 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 @@ -613,7 +593,7 @@ SELECT l.c1, l.c6, l.c8 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, 3; + 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 @@ -737,17 +717,15 @@ 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: e.c1 - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(4 rows) + 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, 3; + 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 | 700 | EMP7 | 2450.34 | 10 @@ -909,18 +887,16 @@ SELECT d.c1, d.c2, d.c5, e.c1, e.c2 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, 3; - QUERY PLAN ------------------------------------------------------------------------------------------------------- - Sort - Sort Key: d.c1, e.c1 - -> Foreign Scan - Filter: ((c1 = 10) OR (c8 = 30)) - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(5 rows) + 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, 3; + 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 diff --git a/expected/pushdown.out b/expected/pushdown.out index f3eab9c..59924b1 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -49,17 +49,14 @@ SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e WHERE c6 IN (1600, 2450) - ORDER BY c1; - QUERY PLAN ---------------------------------------------------------- - Sort + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +--------------------------------------------------- + Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 - Sort Key: e.c1 - -> 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 -(7 rows) + 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) @@ -72,20 +69,17 @@ SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6 - Sort Key: e.c1 - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 rows) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(3 rows) SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c6 -----+------+------ 900 | EMP9 | 5000 @@ -94,20 +88,17 @@ SELECT c1, c2, c6 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 = 1500 - ORDER BY c1; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY c1 DESC NULLS LAST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 - Sort Key: e.c1 - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6, c8 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 rows) + 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; + ORDER BY c1 DESC NULLS LAST; c1 | c2 | c6 | c8 ------+-------+------+---- 1000 | EMP10 | 1500 | 30 @@ -116,20 +107,17 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 BETWEEN 1000 AND 4000 - ORDER BY c1; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 - Sort Key: e.c1 - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6, c8 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 rows) + 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; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c6 | c8 ------+-------+---------+---- 200 | EMP2 | 1600 | 30 @@ -183,20 +171,17 @@ SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' - ORDER BY c1; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c5 - Sort Key: e.c1 - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c5 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 rows) + 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; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c5 ------+-------+------------ 100 | EMP1 | 1980-12-17 @@ -278,20 +263,17 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; - QUERY PLAN ---------------------------------------------------------- - Sort + ORDER BY a ASC NULLS FIRST; + QUERY PLAN +--------------------------------------------------- + Foreign Scan on public.f_mongo_test Output: a - Sort Key: f_mongo_test.a - -> Foreign Scan on public.f_mongo_test - Output: a - Foreign Namespace: mongo_fdw_regress.mongo_test -(6 rows) + Foreign Namespace: mongo_fdw_regress.mongo_test +(3 rows) SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; + ORDER BY a ASC NULLS FIRST; a --- 1 @@ -341,18 +323,24 @@ SELECT c1, c2, c5 FROM f_test_tbl1 e 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'; - QUERY PLAN --------------------------------------------------- - Foreign Scan on public.f_test_tbl1 - Output: c1, c2 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(3 rows) + 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'; + WHERE c2 = 'EMP10' + ORDER BY c2 COLLATE "en_US" DESC NULLS LAST; c1 | c2 ------+------- 1000 | EMP10 @@ -360,7 +348,8 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 < 'EMP10'; + WHERE c2 < 'EMP10' + ORDER BY c2 DESC NULLS LAST; QUERY PLAN -------------------------------------------------- Foreign Scan on public.f_test_tbl1 @@ -369,7 +358,8 @@ SELECT c1, c2 FROM f_test_tbl1 (3 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 < 'EMP10'; + WHERE c2 < 'EMP10' + ORDER BY c2 DESC NULLS LAST; c1 | c2 -----+------ 100 | EMP1 @@ -380,20 +370,17 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c4 FROM f_test_tbl1 WHERE c1 > c4 - ORDER BY c1; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl1 Output: c1, c4 - Sort Key: f_test_tbl1.c1 - -> Foreign Scan on public.f_test_tbl1 - Output: c1, c4 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 rows) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(3 rows) SELECT c1, c4 FROM f_test_tbl1 WHERE c1 > c4 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c4 ------+----- 800 | 400 @@ -432,7 +419,8 @@ SELECT c1, c4, c7, c8 FROM f_test_tbl1 -- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; + WHERE (c1 > 1000) > FALSE + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------- Foreign Scan on public.f_test_tbl1 @@ -441,7 +429,8 @@ SELECT c1, c2 FROM f_test_tbl1 (3 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; + WHERE (c1 > 1000) > FALSE + ORDER BY c1 ASC NULLS FIRST; c1 | c2 ------+------- 1100 | EMP11 @@ -452,7 +441,8 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > 0::BOOLEAN; + WHERE (c1 > 1000) > 0::BOOLEAN + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------- Foreign Scan on public.f_test_tbl1 @@ -461,7 +451,8 @@ SELECT c1, c2 FROM f_test_tbl1 (3 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > 0::BOOLEAN; + WHERE (c1 > 1000) > 0::BOOLEAN + ORDER BY c1 ASC NULLS FIRST; c1 | c2 ------+------- 1100 | EMP11 @@ -537,20 +528,17 @@ EXECUTE pre_stmt_f_mongo_test(2); EXPLAIN (VERBOSE, COSTS FALSE) SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; - QUERY PLAN --------------------------------------------------------- - Sort + ORDER BY name DESC NULLS LAST; + QUERY PLAN +-------------------------------------------------- + Foreign Scan on public.f_test_tbl3 Output: name, marks - Sort Key: f_test_tbl3.name - -> Foreign Scan on public.f_test_tbl3 - Output: name, marks - Foreign Namespace: mongo_fdw_regress.test_tbl3 -(6 rows) + Foreign Namespace: mongo_fdw_regress.test_tbl3 +(3 rows) SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; + ORDER BY name DESC NULLS LAST; name | marks ------+--------- vdd | {29,31} @@ -604,6 +592,84 @@ SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL; (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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -611,6 +677,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index 2d54c21..6eb389e 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -49,12 +49,12 @@ SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e WHERE c6 IN (1600, 2450) - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN --------------------------------------------------------- Sort Output: c1, c2, c6, c8 - Sort Key: e.c1 + Sort Key: e.c1 NULLS FIRST -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 Filter: (e.c6 = ANY ('{1600,2450}'::numeric[])) @@ -72,12 +72,12 @@ SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------------- Sort Output: c1, c2, c6 - Sort Key: e.c1 + Sort Key: e.c1 NULLS FIRST -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6 Foreign Namespace: mongo_fdw_regress.test_tbl1 @@ -85,7 +85,7 @@ SELECT c1, c2, c6 FROM f_test_tbl1 e SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c6 -----+------+------ 900 | EMP9 | 5000 @@ -94,12 +94,12 @@ SELECT c1, c2, c6 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 = 1500 - ORDER BY c1; + ORDER BY c1 DESC NULLS LAST; QUERY PLAN -------------------------------------------------------- Sort Output: c1, c2, c6, c8 - Sort Key: e.c1 + Sort Key: e.c1 DESC NULLS LAST -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 Foreign Namespace: mongo_fdw_regress.test_tbl1 @@ -107,7 +107,7 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 = 1500 - ORDER BY c1; + ORDER BY c1 DESC NULLS LAST; c1 | c2 | c6 | c8 ------+-------+------+---- 1000 | EMP10 | 1500 | 30 @@ -116,12 +116,12 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 BETWEEN 1000 AND 4000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------------- Sort Output: c1, c2, c6, c8 - Sort Key: e.c1 + Sort Key: e.c1 NULLS FIRST -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c6, c8 Foreign Namespace: mongo_fdw_regress.test_tbl1 @@ -129,7 +129,7 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 BETWEEN 1000 AND 4000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c6 | c8 ------+-------+---------+---- 200 | EMP2 | 1600 | 30 @@ -183,12 +183,12 @@ SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------------- Sort Output: c1, c2, c5 - Sort Key: e.c1 + Sort Key: e.c1 NULLS FIRST -> Foreign Scan on public.f_test_tbl1 e Output: c1, c2, c5 Foreign Namespace: mongo_fdw_regress.test_tbl1 @@ -196,7 +196,7 @@ SELECT c1, c2, c5 FROM f_test_tbl1 e SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c2 | c5 ------+-------+------------ 100 | EMP1 | 1980-12-17 @@ -278,12 +278,12 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; + ORDER BY a ASC NULLS FIRST; QUERY PLAN --------------------------------------------------------- Sort Output: a - Sort Key: f_mongo_test.a + Sort Key: f_mongo_test.a NULLS FIRST -> Foreign Scan on public.f_mongo_test Output: a Filter: ((f_mongo_test.a % 2) = 1) @@ -292,7 +292,7 @@ SELECT a FROM f_mongo_test SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; + ORDER BY a ASC NULLS FIRST; a --- 1 @@ -342,18 +342,24 @@ SELECT c1, c2, c5 FROM f_test_tbl1 e 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'; - QUERY PLAN --------------------------------------------------- - Foreign Scan on public.f_test_tbl1 - Output: c1, c2 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(3 rows) + 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'; + WHERE c2 = 'EMP10' + ORDER BY c2 COLLATE "en_US" DESC NULLS LAST; c1 | c2 ------+------- 1000 | EMP10 @@ -361,16 +367,21 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 < 'EMP10'; - QUERY PLAN --------------------------------------------------- - Foreign Scan on public.f_test_tbl1 + WHERE c2 < 'EMP10' + ORDER BY c2 DESC NULLS LAST; + QUERY PLAN +-------------------------------------------------------- + Sort Output: c1, c2 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(3 rows) + Sort Key: f_test_tbl1.c2 DESC NULLS LAST + -> Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(6 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 < 'EMP10'; + WHERE c2 < 'EMP10' + ORDER BY c2 DESC NULLS LAST; c1 | c2 -----+------ 100 | EMP1 @@ -381,12 +392,12 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c4 FROM f_test_tbl1 WHERE c1 > c4 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; QUERY PLAN -------------------------------------------------------- Sort Output: c1, c4 - Sort Key: f_test_tbl1.c1 + Sort Key: f_test_tbl1.c1 NULLS FIRST -> Foreign Scan on public.f_test_tbl1 Output: c1, c4 Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) @@ -395,7 +406,7 @@ SELECT c1, c4 FROM f_test_tbl1 SELECT c1, c4 FROM f_test_tbl1 WHERE c1 > c4 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; c1 | c4 ------+----- 800 | 400 @@ -435,17 +446,22 @@ SELECT c1, c4, c7, c8 FROM f_test_tbl1 -- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; - QUERY PLAN --------------------------------------------------- - Foreign Scan on public.f_test_tbl1 + WHERE (c1 > 1000) > FALSE + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------------- + Sort Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(4 rows) + Sort Key: f_test_tbl1.c1 NULLS FIRST + -> Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; + WHERE (c1 > 1000) > FALSE + ORDER BY c1 ASC NULLS FIRST; c1 | c2 ------+------- 1100 | EMP11 @@ -456,17 +472,22 @@ SELECT c1, c2 FROM f_test_tbl1 EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > 0::BOOLEAN; - QUERY PLAN --------------------------------------------------- - Foreign Scan on public.f_test_tbl1 + WHERE (c1 > 1000) > 0::BOOLEAN + ORDER BY c1 ASC NULLS FIRST; + QUERY PLAN +-------------------------------------------------------- + Sort Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(4 rows) + Sort Key: f_test_tbl1.c1 NULLS FIRST + -> Foreign Scan on public.f_test_tbl1 + Output: c1, c2 + Filter: ((f_test_tbl1.c1 > 1000) > false) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > 0::BOOLEAN; + WHERE (c1 > 1000) > 0::BOOLEAN + ORDER BY c1 ASC NULLS FIRST; c1 | c2 ------+------- 1100 | EMP11 @@ -542,12 +563,12 @@ EXECUTE pre_stmt_f_mongo_test(2); EXPLAIN (VERBOSE, COSTS FALSE) SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; + ORDER BY name DESC NULLS LAST; QUERY PLAN -------------------------------------------------------- Sort Output: name, marks - Sort Key: f_test_tbl3.name + Sort Key: f_test_tbl3.name DESC NULLS LAST -> Foreign Scan on public.f_test_tbl3 Output: name, marks Filter: f_test_tbl3.pass @@ -556,7 +577,7 @@ SELECT name, marks FROM f_test_tbl3 SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; + ORDER BY name DESC NULLS LAST; name | marks ------+--------- vdd | {29,31} @@ -611,6 +632,87 @@ SELECT c1 FROM f_test_tbl2 WHERE c2 IS NULL; (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 +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + 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 + -> 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; + _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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -618,6 +720,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/expected/select.out b/expected/select.out index ddc3d93..f5a7ba6 100644 --- a/expected/select.out +++ b/expected/select.out @@ -1101,25 +1101,22 @@ SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6 -- 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; - QUERY PLAN ---------------------------------------------------------------- - Sort + 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) - Sort Key: t1.a - -> 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 -(12 rows) + -> 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; + 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 diff --git a/expected/select_1.out b/expected/select_1.out index 668fc32..8c121af 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -1065,12 +1065,12 @@ SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6 -- 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; + 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 --------------------------------------------------------------- Sort Output: t1.a, t1.b, t2.a, (t1.a) - Sort Key: t1.a + Sort Key: t1.a NULLS FIRST -> Nested Loop Left Join Output: t1.a, t1.b, t2.a, (t1.a) -> Foreign Scan on public.f_mongo_test t1 @@ -1083,7 +1083,7 @@ SELECT t1.a, t1.b, t3.a, t1_a FROM f_mongo_test t1 LEFT JOIN LATERAL ( (12 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; + 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 diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index 625f077..a9d04bd 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -28,28 +28,28 @@ INSERT INTO fdw137_t2 VALUES (0); CREATE TABLE fdw137_local AS SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM fdw137_t1; --- Simple aggregates +-- 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, 2; -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, 2; +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; -SELECT c1, sum(c1), count(*) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; +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; -SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) = 20 ORDER BY c8; +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; -SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 1; +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; -- 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; -SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1; +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) @@ -59,6 +59,11 @@ 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) @@ -72,14 +77,14 @@ SELECT count(*) FROM (SELECT c3, count(c1) FROM fdw137_t1 GROUP BY c3 HAVING (av -- 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; -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; +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, 3; -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, 3; +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; -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; +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; -- Aggregate is not pushed down as aggregation contains random() EXPLAIN (VERBOSE, COSTS OFF) @@ -294,7 +299,7 @@ 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; +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; -- Check when enable_join_pushdown is OFF and enable_aggregate_pushdown is ON. -- Shouldn't push down join as well as aggregation. @@ -303,6 +308,64 @@ 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; + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; @@ -312,6 +375,7 @@ 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; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 26a8c0c..b03769c 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -44,42 +44,42 @@ CREATE TABLE l_test_tbl1 AS -- 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, 3; + 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, 3; + 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, 3; + 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, 3; + 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, 3 OFFSET 50; + 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, 3 OFFSET 50; + 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, 3; + 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, 3; + 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; -- 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, 3; + 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, 3; + 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, 3; + 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, 3; + 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, 3; + 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, 3; + 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; @@ -93,9 +93,9 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- 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, 3; + 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, 3; + 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) @@ -124,25 +124,25 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 -- (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, d.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, d.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, d.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, d.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 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, 3; + 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, 3; + 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; @@ -150,10 +150,10 @@ RESET enable_nestloop; 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, 3; + 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, 3; + 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 @@ -186,9 +186,9 @@ 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, 3; + 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, 3; + 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; @@ -226,9 +226,9 @@ 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, 3; + 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, 3; + 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) diff --git a/sql/pushdown.sql b/sql/pushdown.sql index e011fb8..8e5c89d 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -38,7 +38,7 @@ SELECT c1, c2, c3, c4, c5, c6, c7, c8 FROM f_test_tbl1 ORDER BY c1; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e WHERE c6 IN (1600, 2450) - ORDER BY c1; + 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; @@ -46,26 +46,26 @@ SELECT c1, c2, c6 AS "salary", c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; SELECT c1, c2, c6 FROM f_test_tbl1 e WHERE c6 > 3000 - ORDER BY c1; + 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; + ORDER BY c1 DESC NULLS LAST; SELECT c1, c2, c6, c8 FROM f_test_tbl1 e WHERE c6 = 1500 - ORDER BY c1; + 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; + 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; + ORDER BY c1 ASC NULLS FIRST; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e @@ -78,10 +78,10 @@ SELECT c1, c2, c4, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; SELECT c1, c2, c5 FROM f_test_tbl1 e WHERE c5 <= '1980-12-17' - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2, c6, c8 FROM f_test_tbl1 e @@ -110,10 +110,10 @@ SELECT c1, c2, c6, c8 FROM f_test_tbl1 e EXPLAIN (VERBOSE, COSTS FALSE) SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; + ORDER BY a ASC NULLS FIRST; SELECT a FROM f_mongo_test WHERE a%2 = 1 - ORDER BY a; + ORDER BY a ASC NULLS FIRST; EXPLAIN (VERBOSE, COSTS FALSE) SELECT a, b FROM f_mongo_test @@ -131,27 +131,32 @@ 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'; + WHERE c2 = 'EMP10' + ORDER BY c2 COLLATE "en_US" DESC NULLS LAST; SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 = 'EMP10'; + 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'; + WHERE c2 < 'EMP10' + ORDER BY c2 DESC NULLS LAST; SELECT c1, c2 FROM f_test_tbl1 - WHERE c2 < 'EMP10'; + 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; + ORDER BY c1 ASC NULLS FIRST; SELECT c1, c4 FROM f_test_tbl1 WHERE c1 > c4 - ORDER BY c1; + ORDER BY c1 ASC NULLS FIRST; EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c4, c7, c8 FROM f_test_tbl1 @@ -164,14 +169,18 @@ SELECT c1, c4, c7, c8 FROM f_test_tbl1 -- Nested operator expression in WHERE clause. Should pushdown. EXPLAIN (VERBOSE, COSTS FALSE) SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; + WHERE (c1 > 1000) > FALSE + ORDER BY c1 ASC NULLS FIRST; SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > FALSE; + 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; + WHERE (c1 > 1000) > 0::BOOLEAN + ORDER BY c1 ASC NULLS FIRST; SELECT c1, c2 FROM f_test_tbl1 - WHERE (c1 > 1000) > 0::BOOLEAN; + 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) @@ -196,10 +205,10 @@ EXECUTE pre_stmt_f_mongo_test(2); EXPLAIN (VERBOSE, COSTS FALSE) SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; + ORDER BY name DESC NULLS LAST; SELECT name, marks FROM f_test_tbl3 WHERE pass = true - ORDER BY name; + ORDER BY name DESC NULLS LAST; -- INSERT NULL values and check behaviour. INSERT INTO f_test_tbl2 VALUES ('0', NULL, NULL, NULL); @@ -217,6 +226,52 @@ 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -224,6 +279,7 @@ 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 USER MAPPING FOR public SERVER mongo_server; DROP SERVER mongo_server; DROP EXTENSION mongo_fdw; diff --git a/sql/select.sql b/sql/select.sql index cc75ab5..5629613 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -267,9 +267,9 @@ SELECT (c6::float + (c1 * length(c3::text))) AS "c1 + c6", c1, c6 -- 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; + 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; + 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; From 2107388a3976584300535f1038fc8c76bbe3839a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 13 Oct 2022 14:29:01 +0530 Subject: [PATCH 197/239] Add enable_order_by_pushdown GUC to control ORDER BY push-down. Sometimes getting a sorted result from MongoDB server is slower than performing a sort locally. To have that flexibility add a GUC named mongo_fdw.enable_order_by_pushdown to control the ORDER BY push-down. However, it is set by super users only. The default is true. FDW-134, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Ajay Pal. --- README.md | 6 ++++ expected/aggregate_pushdown.out | 54 +++++++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 54 +++++++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 54 +++++++++++++++++++++++++++++++ expected/aggregate_pushdown_3.out | 54 +++++++++++++++++++++++++++++++ expected/join_pushdown.out | 54 +++++++++++++++++++++++++++++++ expected/join_pushdown_1.out | 54 +++++++++++++++++++++++++++++++ expected/join_pushdown_2.out | 54 +++++++++++++++++++++++++++++++ expected/join_pushdown_3.out | 54 +++++++++++++++++++++++++++++++ expected/pushdown.out | 30 +++++++++++++++++ expected/pushdown_1.out | 31 ++++++++++++++++++ mongo_fdw.c | 28 ++++++++++++++++ sql/aggregate_pushdown.sql | 12 +++++++ sql/join_pushdown.sql | 16 +++++++++ sql/pushdown.sql | 11 +++++++ 15 files changed, 566 insertions(+) diff --git a/README.md b/README.md index 2b63ad0..c4a6ea7 100644 --- a/README.md +++ b/README.md @@ -310,6 +310,12 @@ The following parameters can be supplied while creating user mapping: * `username`: Username to use when connecting to MongoDB. * `password`: Password to authenticate to the MongoDB server. +GUC variables: + + * `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`. + 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 diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 58edf60..9281049 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -113,6 +113,37 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 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; @@ -334,6 +365,29 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON 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; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 141f45c..2a13465 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -113,6 +113,37 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 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; @@ -334,6 +365,29 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON 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; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 756b0a4..9f4955b 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -122,6 +122,37 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 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; @@ -351,6 +382,29 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON 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; diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out index 164d931..9d511ca 100644 --- a/expected/aggregate_pushdown_3.out +++ b/expected/aggregate_pushdown_3.out @@ -122,6 +122,37 @@ SELECT c2, sum(c1) FROM fdw137_t1 GROUP BY c1, c2 HAVING min(c1) > 500 ORDER BY 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; @@ -351,6 +382,29 @@ SELECT sum(t2.c1), t1.c8, avg(t1.c8) FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON 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; diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 8b24ebb..2ade800 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -160,6 +160,38 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 @@ -529,6 +561,28 @@ SELECT d.c1, e.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. diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index 23828f6..e314dcb 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -160,6 +160,38 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 @@ -529,6 +561,28 @@ SELECT d.c1, e.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. diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index a9b7d96..a362230 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -160,6 +160,38 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 @@ -529,6 +561,28 @@ SELECT d.c1, e.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. diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 7a22654..af797df 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -160,6 +160,38 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 @@ -529,6 +561,28 @@ SELECT d.c1, e.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. diff --git a/expected/pushdown.out b/expected/pushdown.out index 59924b1..20e6574 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -416,6 +416,36 @@ SELECT c1, c4, c7, c8 FROM f_test_tbl1 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 diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index 6eb389e..e3c80d3 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -443,6 +443,37 @@ SELECT c1, c4, c7, c8 FROM f_test_tbl1 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 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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 diff --git a/mongo_fdw.c b/mongo_fdw.c index 5503bc7..5f8ec67 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -45,6 +45,7 @@ #endif #include "parser/parsetree.h" #include "storage/ipc.h" +#include "utils/guc.h" #include "utils/jsonb.h" #if PG_VERSION_NUM < 130000 #include "utils/jsonapi.h" @@ -83,6 +84,9 @@ PG_MODULE_MAGIC; * the underlying scan. Hence for now, cost sorts same as underlying scans. */ #define DEFAULT_MONGO_SORT_MULTIPLIER 1 + +/* GUC variables. */ +static bool enable_order_by_pushdown = true; #endif /* @@ -289,6 +293,22 @@ void _PG_init(void) { #ifdef META_DRIVER + /* + * Sometimes getting a sorted result from MongoDB server is slower than + * performing a sort locally. To have that flexibility add a GUC named + * mongo_fdw.enable_order_by_pushdown to control the ORDER BY push-down. + */ + 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); + /* Initialize MongoDB C driver */ mongoc_init(); #endif @@ -4156,6 +4176,10 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, ListCell *lc; List *useful_pathkeys_list = NIL; /* List of all pathkeys */ + /* If orderby pushdown is not enabled, honor it. */ + if (!enable_order_by_pushdown) + return; + /* * Check the query pathkeys length. Don't push when exceeding the limit * set by MongoDB. @@ -4284,6 +4308,10 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, ForeignPath *ordered_path; ListCell *lc; + /* If orderby pushdown is not enabled, honor it. */ + if (!enable_order_by_pushdown) + return; + /* Shouldn't get here unless the query has ORDER BY */ Assert(parse->sortClause); diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index a9d04bd..132cbd2 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -45,6 +45,12 @@ SELECT c8, min(c2) FROM fdw137_t1 WHERE c3 = 'ADMIN' GROUP BY c8 HAVING min(c8) 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) @@ -85,6 +91,12 @@ SELECT t1.c1, count(*), t2.c4 FROM fdw137_t2 t1 INNER JOIN fdw137_t1 t2 ON (t1.c 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) diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index b03769c..6c4db0a 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -62,6 +62,14 @@ 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 @@ -134,6 +142,14 @@ 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; diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 8e5c89d..9171b9a 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -166,6 +166,17 @@ 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 From 58bce890672fde1b1c6733a7d72714e9865e28cc Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 13 Oct 2022 14:29:01 +0530 Subject: [PATCH 198/239] Push down LIMIT/OFFSET to remote MongoDB servers. Wherever applicable, perform LIMIT and OFFSET operations on the remote server. This reduces network traffic between local PostgreSQL and remote MongoDB servers. Due to limitations on the MongoDB side, we only support Const nodes of type bigint, which allows us to have only 64-bit integer values. FDW-131, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan, and a few cosmetic changes by Jeevan Chalke. --- README.md | 5 + mongo_fdw.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++- mongo_query.c | 44 ++++++++ mongo_query.h | 5 + 4 files changed, 340 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c4a6ea7..5cd117f 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,11 @@ 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. + ### New MongoDB C Driver Support This enhancement is to add a new [MongoDB][1]' C driver. The current implementation is based on the legacy driver of MongoDB. But diff --git a/mongo_fdw.c b/mongo_fdw.c index 5f8ec67..efbe583 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -94,11 +94,14 @@ static bool enable_order_by_pushdown = true; * 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); @@ -239,6 +242,12 @@ static void mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel); #endif +#if PG_VERSION_NUM >= 120000 +static void mongo_add_foreign_final_paths(PlannerInfo *root, + RelOptInfo *input_rel, + RelOptInfo *final_rel, + FinalPathExtraData *extra); +#endif #endif #ifndef META_DRIVER static const char *escape_json_string(const char *string); @@ -630,13 +639,21 @@ mongoGetForeignPlan(PlannerInfo *root, 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)); + } + #endif /* Set scan relation id */ @@ -997,6 +1014,39 @@ mongoGetForeignPlan(PlannerInfo *root, /* 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); + } + } #endif /* @@ -1023,6 +1073,9 @@ mongoGetForeignPlan(PlannerInfo *root, 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)) { @@ -3728,8 +3781,6 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) * mongoGetForeignUpperPaths * Add paths for post-join operations like aggregation, grouping etc. if * corresponding operations are safe to push down. - * - * Right now, we only support aggregate, grouping and having clause pushdown. */ #if PG_VERSION_NUM >= 110000 static void @@ -3754,7 +3805,8 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, /* Ignore stages we don't support; and skip any duplicate calls. */ #if PG_VERSION_NUM >= 120000 - if ((stage != UPPERREL_GROUP_AGG && stage != UPPERREL_ORDERED) || + if ((stage != UPPERREL_GROUP_AGG && stage != UPPERREL_ORDERED && + stage != UPPERREL_FINAL) || #else if (stage != UPPERREL_GROUP_AGG || #endif @@ -3776,6 +3828,10 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, 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; @@ -4405,7 +4461,7 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, * 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_make1(makeInteger(true)); + fdw_private = list_make2(makeInteger(true), makeInteger(false)); /* Create foreign ordering path */ ordered_path = create_foreign_upper_path(root, @@ -4421,6 +4477,232 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, /* 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 + */ + 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 */ + NULL); /* no fdw_private */ + + /* 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 + */ + 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); + + /* and add it to the final_rel */ + add_path(final_rel, (Path *) final_path); +} #endif /* PG_VERSION_NUM >= 120000 */ /* diff --git a/mongo_query.c b/mongo_query.c index bc805fb..1c9bc51 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -264,6 +264,7 @@ mongo_query_document(ForeignScanState *scanStateNode) HTAB *columnInfoHash; int jointype; int natts; + bool has_limit; /* Retrieve data passed by planning phase */ colname_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseColNameList); @@ -284,6 +285,9 @@ mongo_query_document(ForeignScanState *scanStateNode) 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; @@ -771,6 +775,46 @@ mongo_query_document(ForeignScanState *scanStateNode) 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); if (!bsonFinish(queryDocument)) diff --git a/mongo_query.h b/mongo_query.h index 1fbd2af..67ba0af 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -85,6 +85,11 @@ enum mongoFdwScanPrivateIndex mongoFdwPrivatePathKeyList, mongoFdwPrivateIsAscSortList, + /* LIMIT/OFFSET clause information */ + mongoFdwPrivateHasLimitClause, + mongoFdwPrivateLimitCountList, + mongoFdwPrivateLimitOffsetList, + /* Upper relation information */ /* Upper relation grouping operation name list */ From 066075952b007bffbe7d18a7e33cdb5c31733efc Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 13 Oct 2022 14:29:01 +0530 Subject: [PATCH 199/239] Add test coverage for the LIMIT/OFFSET push-down. FDW-131, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan --- Makefile | 2 +- Makefile.meta | 2 +- expected/aggregate_pushdown.out | 149 ++++++++++ expected/aggregate_pushdown_1.out | 149 ++++++++++ expected/aggregate_pushdown_2.out | 175 ++++++++++++ expected/aggregate_pushdown_3.out | 175 ++++++++++++ expected/join_pushdown.out | 204 ++++++++++++-- expected/join_pushdown_1.out | 204 ++++++++++++-- expected/join_pushdown_2.out | 179 ++++++++++++ expected/join_pushdown_3.out | 179 ++++++++++++ expected/limit_offset_pushdown.out | 379 +++++++++++++++++++++++++ expected/limit_offset_pushdown_1.out | 397 +++++++++++++++++++++++++++ sql/aggregate_pushdown.sql | 44 +++ sql/join_pushdown.sql | 60 ++++ sql/limit_offset_pushdown.sql | 121 ++++++++ 15 files changed, 2381 insertions(+), 38 deletions(-) create mode 100644 expected/limit_offset_pushdown.out create mode 100644 expected/limit_offset_pushdown_1.out create mode 100644 sql/limit_offset_pushdown.sql diff --git a/Makefile b/Makefile index 005dcce..3bbe05e 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o depa 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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown aggregate_pushdown limit_offset_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/Makefile.meta b/Makefile.meta index 8bcc3f3..e98d28c 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -26,7 +26,7 @@ OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o depa 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 +REGRESS = server_options connection_validation dml select pushdown join_pushdown aggregate_pushdown limit_offset_pushdown REGRESS_OPTS = --load-extension=$(EXTENSION) # diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 9281049..0dd754f 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -1436,6 +1436,155 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY 132 | 132 (3 rows) +-- FDW-131: Limit and offset pushdown with Aggregate pushdown. +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + min | c1 +-----+---- + 10 | 10 + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 + | +(6 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + min | c1 +-----+---- + 10 | 10 +(1 row) + +-- Limit 0, Offset 0 with aggregates. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + min | c1 +-----+---- +(0 rows) + +-- Limit NULL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit ALL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit with -ve value. Shouldn't pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> GroupAggregate + Output: c1, max(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; + QUERY PLAN +--------------------------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> Foreign Scan + Output: c1, (max(c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(5 rows) + +-- Should throw an error. +SELECT c1, max(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)) + -> GroupAggregate + Output: c1, avg(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 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 -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 2a13465..10c8016 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -1436,6 +1436,155 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY 132 | 132 (3 rows) +-- FDW-131: Limit and offset pushdown with Aggregate pushdown. +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + min | c1 +-----+---- + 10 | 10 + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 + | +(6 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + min | c1 +-----+---- + 10 | 10 +(1 row) + +-- Limit 0, Offset 0 with aggregates. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + min | c1 +-----+---- +(0 rows) + +-- Limit NULL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit ALL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit with -ve value. Shouldn't pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> GroupAggregate + Output: c1, max(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; + QUERY PLAN +--------------------------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> Foreign Scan + Output: c1, (max(c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(5 rows) + +-- Should throw an error. +SELECT c1, max(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)) + -> GroupAggregate + Output: c1, avg(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 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 -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 9f4955b..73bc84f 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -1459,6 +1459,181 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY 132 | 132 (3 rows) +-- FDW-131: Limit and offset pushdown with Aggregate pushdown. +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + min | c1 +-----+---- + 10 | 10 + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 + | +(6 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> GroupAggregate + Output: min(c1), c1 + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + min | c1 +-----+---- + 10 | 10 +(1 row) + +-- Limit 0, Offset 0 with aggregates. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> GroupAggregate + Output: min(c1), c1 + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + min | c1 +-----+---- +(0 rows) + +-- Limit NULL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> Sort + Output: (min(c1)), c1 + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit ALL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> Sort + Output: (min(c1)), c1 + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit with -ve value. Shouldn't pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> GroupAggregate + Output: c1, max(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> Sort + Output: c1, (max(c1)) + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: c1, (max(c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +-- Should throw an error. +SELECT c1, max(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)) + -> GroupAggregate + Output: c1, avg(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 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) + -> Sort + Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(12 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 -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out index 9d511ca..712db82 100644 --- a/expected/aggregate_pushdown_3.out +++ b/expected/aggregate_pushdown_3.out @@ -1464,6 +1464,181 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY 132 | 132 (3 rows) +-- FDW-131: Limit and offset pushdown with Aggregate pushdown. +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + min | c1 +-----+---- + 10 | 10 + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 + | +(6 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> GroupAggregate + Output: min(c1), c1 + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + min | c1 +-----+---- + 10 | 10 +(1 row) + +-- Limit 0, Offset 0 with aggregates. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> GroupAggregate + Output: min(c1), c1 + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + min | c1 +-----+---- +(0 rows) + +-- Limit NULL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> Sort + Output: (min(c1)), c1 + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit ALL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: (min(c1)), c1 + -> Sort + Output: (min(c1)), c1 + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit with -ve value. Shouldn't pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> GroupAggregate + Output: c1, max(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; + QUERY PLAN +--------------------------------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> Sort + Output: c1, (max(c1)) + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: c1, (max(c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(8 rows) + +-- Should throw an error. +SELECT c1, max(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)) + -> GroupAggregate + Output: c1, avg(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 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) + -> Sort + Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) + Sort Key: fdw137_t2.c1 NULLS FIRST + -> Foreign Scan + Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(12 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 -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 2ade800..7567ef4 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -100,12 +100,11 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 ------------------------------------------------------------------------------------------------------- - Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(3 rows) + 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; @@ -672,17 +671,16 @@ 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 ----------------------------------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------- Sort Sort Key: l.c8 InitPlan 1 (returns $0) - -> Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2) + -> 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) -(8 rows) +(7 rows) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -696,17 +694,16 @@ 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 ------------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------- Sort Sort Key: l.c8 InitPlan 1 (returns $0) - -> Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) INNER JOIN (mongo_fdw_regress.test_tbl2 f2) + -> 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) -(8 rows) +(7 rows) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -1352,6 +1349,177 @@ SELECT e.c1, d.c2 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 | 10 + 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 | 10 + 100 | 20 + 100 | 30 +(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 diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index e314dcb..87a9e0e 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -100,12 +100,11 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 ------------------------------------------------------------------------------------------------------- - Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl2 d) LEFT JOIN (mongo_fdw_regress.test_tbl1 e) -(3 rows) + 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; @@ -672,17 +671,16 @@ 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 ----------------------------------------------------------------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------------------------------------------------------- Sort Sort Key: l.c8 InitPlan 1 (returns $0) - -> Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) LEFT JOIN (mongo_fdw_regress.test_tbl2 f2) + -> 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) -(8 rows) +(7 rows) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -696,17 +694,16 @@ 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 ------------------------------------------------------------------------------------------------------------------ + QUERY PLAN +----------------------------------------------------------------------------------------------------------- Sort Sort Key: l.c8 InitPlan 1 (returns $0) - -> Limit - -> Foreign Scan - Foreign Namespace: (mongo_fdw_regress.test_tbl1 f1) INNER JOIN (mongo_fdw_regress.test_tbl2 f2) + -> 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) -(8 rows) +(7 rows) SELECT l.c1, l.c6, l.c8 FROM l_test_tbl1 l @@ -1352,6 +1349,177 @@ SELECT e.c1, d.c2 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 | 10 + 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 | 10 + 100 | 20 + 100 | 30 +(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 diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index a362230..9605e7d 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1355,6 +1355,185 @@ SELECT e.c1, d.c2 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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 | 30 + 100 | 40 +(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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 | 10 + 100 | 20 + 100 | 30 +(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 diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index af797df..0c3c59c 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -1355,6 +1355,185 @@ SELECT e.c1, d.c2 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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 | 30 + 100 | 40 +(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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 +--------------------------------------------------------------------------------------------------------- + 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) + +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 | 10 + 100 | 20 + 100 | 30 +(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; ERROR: unrecognized configuration parameter "enable_partitionwise_join" diff --git a/expected/limit_offset_pushdown.out b/expected/limit_offset_pushdown.out new file mode 100644 index 0000000..b797352 --- /dev/null +++ b/expected/limit_offset_pushdown.out @@ -0,0 +1,379 @@ +\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 'edb' user with 'edb' 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; diff --git a/expected/limit_offset_pushdown_1.out b/expected/limit_offset_pushdown_1.out new file mode 100644 index 0000000..b4d361b --- /dev/null +++ b/expected/limit_offset_pushdown_1.out @@ -0,0 +1,397 @@ +\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 'edb' user with 'edb' 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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 +-------------------------------------------------------- + Limit + Output: c1, c2, c3 + -> Foreign Scan on public.fdw131_t1 + Output: c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(5 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; diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index 132cbd2..2fc622c 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -378,6 +378,50 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY 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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; +SELECT min(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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; +SELECT min(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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; +SELECT min(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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; +SELECT min(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +-- Should throw an error. +SELECT c1, max(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)); + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 6c4db0a..05cb494 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -339,6 +339,66 @@ SELECT e.c1, d.c2 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; diff --git a/sql/limit_offset_pushdown.sql b/sql/limit_offset_pushdown.sql new file mode 100644 index 0000000..9071483 --- /dev/null +++ b/sql/limit_offset_pushdown.sql @@ -0,0 +1,121 @@ +\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 'edb' user with 'edb' 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; From f1a6b447bd31d28f52eefabd81e66dc86de0ba68 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 17 Oct 2022 09:30:44 +0530 Subject: [PATCH 200/239] Save the status of the enable_aggregate_pushdown option for later checking. In the case of nested join queries, the existing logic may not correctly propagate the status of the enable_aggregate_pushdown option to the stage where we are accessing the aggregation path. Fix that by saving the status of enable_aggregate_pushdown in MongoFdwRelationInfo, which is updated for every output rel at various stages. It can then be accessed at the time of grouping operation. FDW-560, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan. --- expected/aggregate_pushdown.out | 36 +++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 36 +++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 36 +++++++++++++++++++++++++++ expected/aggregate_pushdown_3.out | 36 +++++++++++++++++++++++++++ mongo_fdw.c | 41 ++++++++++++++++--------------- mongo_fdw.h | 6 +++++ sql/aggregate_pushdown.sql | 6 +++++ 7 files changed, 177 insertions(+), 20 deletions(-) diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 0dd754f..3feec30 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -1320,6 +1320,42 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 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'); diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 10c8016..e700678 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -1320,6 +1320,42 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 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'); diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 73bc84f..ce95727 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -1340,6 +1340,42 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) (6 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'); diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out index 712db82..5326b8e 100644 --- a/expected/aggregate_pushdown_3.out +++ b/expected/aggregate_pushdown_3.out @@ -1345,6 +1345,42 @@ SELECT sum(t2.c1), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2 Foreign Namespace: Aggregate on ((mongo_fdw_regress.test_tbl1 t1) LEFT JOIN (mongo_fdw_regress.test_tbl2 t2)) (6 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'); diff --git a/mongo_fdw.c b/mongo_fdw.c index efbe583..ec26592 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -490,6 +490,15 @@ mongoGetForeignRelSize(PlannerInfo *root, /* Also store the options in fpinfo for further use */ fpinfo->options = options; + +#ifdef META_DRIVER + /* + * 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; +#endif } /* @@ -3472,6 +3481,10 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, /* 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 string describing this join relation to be used in EXPLAIN * output of the corresponding ForeignScan. @@ -3535,29 +3548,9 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) #endif MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) grouped_rel->fdw_private; MongoFdwRelationInfo *ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; - MongoFdwRelationInfo *ofpinfo_o; - MongoFdwRelationInfo *ofpinfo_i; ListCell *lc; int i; List *tlist = NIL; - bool is_join = false; - - /* - * If the underlying scan relation is the join relation then find the - * fpinfo of each relation involved in the join. - */ - if (IS_JOIN_REL(fpinfo->outerrel)) - { - ofpinfo_o = (MongoFdwRelationInfo *) ofpinfo->outerrel->fdw_private; - ofpinfo_i = (MongoFdwRelationInfo *) ofpinfo->innerrel->fdw_private; - is_join = true; - } - - /* If aggregate pushdown is not enabled, honor it. */ - if ((!is_join && !ofpinfo->options->enable_aggregate_pushdown) || - ((is_join && !ofpinfo_o->options->enable_aggregate_pushdown) || - (is_join && !ofpinfo_i->options->enable_aggregate_pushdown))) - return false; /* Grouping Sets are not pushable */ if (query->groupingSets) @@ -3877,6 +3870,14 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, /* 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 (!fpinfo->is_agg_scanrel_pushable) + return; + /* Assess if it is safe to push down aggregation and grouping. */ #if PG_VERSION_NUM >= 110000 if (!mongo_foreign_grouping_ok(root, grouped_rel, extra->havingQual)) diff --git a/mongo_fdw.h b/mongo_fdw.h index 6e64cdd..3b89079 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -366,6 +366,12 @@ typedef struct MongoFdwRelationInfo /* Upper relation information */ UpperRelationKind stage; + + /* + * True if the underlying scan relation involved in aggregation is + * pushable. + */ + bool is_agg_scanrel_pushable; } MongoFdwRelationInfo; /* diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index 2fc622c..9c8574a 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -313,6 +313,12 @@ 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'); From 234c8cff4c52bb1c7e1a60b8529de11ace666dc1 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 29 Nov 2022 19:38:37 +0530 Subject: [PATCH 201/239] Check the shippability of sort clauses properly. We were pushing the ORDER BY clauses to the remote side without verifying whether the sort operator was safe to ship. It resulted in a wrong output when the sort operator isn't default for the sort expression's type. Fix the same. FDW-564, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. --- deparse.c | 70 +++++++++++++++++++++ expected/pushdown.out | 62 +++++++++++++++++++ expected/pushdown_1.out | 58 +++++++++++++++++ mongo_fdw.c | 134 ++++++++++++++++++++++++++++------------ mongo_fdw.h | 8 +++ sql/pushdown.sql | 43 +++++++++++++ 6 files changed, 336 insertions(+), 39 deletions(-) diff --git a/deparse.c b/deparse.c index 9bb3f3c..7ab3c0d 100644 --- a/deparse.c +++ b/deparse.c @@ -29,6 +29,7 @@ #include "mongo.h" #endif #include "mongo_query.h" +#include "nodes/nodeFuncs.h" #if PG_VERSION_NUM < 120000 #include "nodes/relation.h" #include "optimizer/var.h" @@ -623,3 +624,72 @@ mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context) 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) +{ +#if PG_VERSION_NUM >= 120000 + return (oid < FirstGenbkiObjectId); +#else + return (oid < FirstBootstrapObjectId); +#endif +} diff --git a/expected/pushdown.out b/expected/pushdown.out index 20e6574..dcd27ab 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -700,6 +700,62 @@ SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY 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 ORDER BY 1 USING OPERATOR(public.<^); + QUERY PLAN +----------------------------------------------------------------- + Sort + Output: ($0) + Sort Key: ($0) USING <^ + InitPlan 1 (returns $0) + -> Limit + Output: f_mongo_test.a + -> Foreign Scan on public.f_mongo_test + Output: f_mongo_test.a + Filter: (f_mongo_test.a IS NOT NULL) + Foreign Namespace: mongo_fdw_regress.mongo_test + -> Result + Output: $0 +(12 rows) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -708,6 +764,12 @@ 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; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index e3c80d3..aa11550 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -744,6 +744,58 @@ SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY 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 ORDER BY 1 USING OPERATOR(public.<^); + QUERY PLAN +--------------------------------------------------------------- + Sort + Output: (min(a)) + Sort Key: (min(f_mongo_test.a)) USING <^ + -> Aggregate + Output: min(a) + -> Foreign Scan on public.f_mongo_test + Output: _id, a, b + Foreign Namespace: mongo_fdw_regress.mongo_test +(8 rows) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -752,6 +804,12 @@ 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index ec26592..6ab6dff 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -55,6 +55,7 @@ #include "utils/rel.h" #include "utils/selfuncs.h" #include "utils/syscache.h" +#include "utils/typcache.h" /* Declarations for dynamic loading */ PG_MODULE_MAGIC; @@ -267,11 +268,9 @@ static void mongo_add_paths_with_pathkeys(PlannerInfo *root, Path *epq_path, Cost base_startup_cost, Cost base_total_cost); -static Expr *mongo_find_em_expr_for_input_target(PlannerInfo *root, - EquivalenceClass *ec, - RelOptInfo *rel); -static Expr *mongo_find_em_expr_for_rel(PlannerInfo *root, - EquivalenceClass *ec, RelOptInfo *rel); +static EquivalenceMember *mongo_find_em_for_rel_target(PlannerInfo *root, + EquivalenceClass *ec, + RelOptInfo *rel); #if PG_VERSION_NUM >= 120000 static void mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, @@ -987,6 +986,7 @@ mongoGetForeignPlan(PlannerInfo *root, #ifdef META_DRIVER foreach(lc, best_path->path.pathkeys) { + EquivalenceMember *em; PathKey *pathkey = lfirst(lc); Expr *em_expr; @@ -996,16 +996,23 @@ mongoGetForeignPlan(PlannerInfo *root, * By construction, foreignrel is the input relation to the final * sort. */ - em_expr = mongo_find_em_expr_for_input_target(root, - pathkey->pk_eclass, - foreignrel); + em = mongo_find_em_for_rel_target(root, pathkey->pk_eclass, + foreignrel); } else - em_expr = mongo_find_em_expr_for_rel(root, pathkey->pk_eclass, - qual_info->foreignRel); + 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"); - Assert(em_expr != NULL); /* Ignore binary-compatible relabeling */ + em_expr = em->em_expr; while (IsA(em_expr, RelabelType)) em_expr = ((RelabelType *) em_expr)->arg; @@ -4121,8 +4128,6 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) foreach(lc, root->query_pathkeys) { PathKey *pathkey = (PathKey *) lfirst(lc); - EquivalenceClass *pathkey_ec = pathkey->pk_eclass; - Expr *em_expr; /* Only ASC NULLS FIRST and DESC NULLS LAST can be pushed down */ if (!IS_PATHKEY_PUSHABLE(pathkey)) @@ -4138,18 +4143,7 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) * end up resorting the entire data set. So, unless we can push * down all of the query pathkeys, forget it. */ - if (!(em_expr = mongo_find_em_expr_for_rel(root, pathkey_ec, rel))) - { - query_pathkeys_ok = false; - break; - } - - /* Ignore binary-compatible relabeling */ - while (em_expr && IsA(em_expr, RelabelType)) - em_expr = ((RelabelType *) em_expr)->arg; - - /* Only Vars are allowed per MongoDB. */ - if (!IsA(em_expr, Var)) + if (!mongo_is_foreign_pathkey(root, rel, pathkey)) { query_pathkeys_ok = false; break; @@ -4184,6 +4178,7 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) foreach(lc, useful_eclass_list) { EquivalenceClass *cur_ec = lfirst(lc); + EquivalenceMember *em; Expr *em_expr; PathKey *pathkey; @@ -4191,11 +4186,16 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) 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_expr = mongo_find_em_expr_for_rel(root, cur_ec, rel))) + 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; @@ -4211,6 +4211,10 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) 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)); } @@ -4312,13 +4316,12 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, } /* - * mongo_find_em_expr_for_rel + * mongo_find_em_for_rel * Find an equivalence class member expression, all of whose Vars, come * from the indicated relation. */ -static Expr * -mongo_find_em_expr_for_rel(PlannerInfo *root, EquivalenceClass *ec, - RelOptInfo *rel) +EquivalenceMember * +mongo_find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel) { ListCell *lc_em; @@ -4326,6 +4329,10 @@ mongo_find_em_expr_for_rel(PlannerInfo *root, EquivalenceClass *ec, { 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)) @@ -4335,7 +4342,7 @@ mongo_find_em_expr_for_rel(PlannerInfo *root, EquivalenceClass *ec, * taken entirely from this relation, we'll be content to choose * any one of those. */ - return em->em_expr; + return em; } } @@ -4419,6 +4426,7 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, { PathKey *pathkey = (PathKey *) lfirst(lc); EquivalenceClass *pathkey_ec = pathkey->pk_eclass; + EquivalenceMember *em = NULL; Expr *sort_expr; /* @@ -4436,12 +4444,14 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, * shippable EM that is computed in input_rel's reltarget, else we * can't push down the sort. */ - sort_expr = mongo_find_em_expr_for_input_target(root, pathkey_ec, - input_rel); - if (!sort_expr) + 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; @@ -4707,13 +4717,13 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, #endif /* PG_VERSION_NUM >= 120000 */ /* - * mongo_find_em_expr_for_input_target + * mongo_find_em_for_rel_target * Find an equivalence class member expression to be computed as a sort * column in the given target. */ -static Expr * -mongo_find_em_expr_for_input_target(PlannerInfo *root, EquivalenceClass *ec, - RelOptInfo *rel) +static EquivalenceMember * +mongo_find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, + RelOptInfo *rel) { PathTarget *target = rel->reltarget; ListCell *lc1; @@ -4766,7 +4776,7 @@ mongo_find_em_expr_for_input_target(PlannerInfo *root, EquivalenceClass *ec, * 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->em_expr; + return em; } i++; @@ -4774,4 +4784,50 @@ mongo_find_em_expr_for_input_target(PlannerInfo *root, EquivalenceClass *ec, 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; + + oprid = get_opfamily_member(pathkey->pk_opfamily, + em->em_datatype, + em->em_datatype, + pathkey->pk_strategy); + if (!OidIsValid(oprid)) + elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", + pathkey->pk_strategy, em->em_datatype, em->em_datatype, + 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; +} #endif /* End of META_DRIVER */ diff --git a/mongo_fdw.h b/mongo_fdw.h index 3b89079..3bd080d 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -502,5 +502,13 @@ 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 */ diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 9171b9a..480881c 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -283,6 +283,43 @@ SELECT _id, a01, a31, a32, a33, a34, a35 FROM f_test_large ORDER BY 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 ORDER BY 1 USING OPERATOR(public.<^); + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; @@ -291,6 +328,12 @@ 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; From d3f0e6c6e0ef96f129295688243018dd795d0c6f Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 1 Dec 2022 19:52:57 +0530 Subject: [PATCH 202/239] Fix compilation warning. Commit 234c8cff4c52bb1c7e1a60b8529de11ace666dc1 misplaced the header file into the wrong .c file. Fix the same. Vaibhav Dalvi. --- deparse.c | 1 - mongo_fdw.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/deparse.c b/deparse.c index 7ab3c0d..f1deb2e 100644 --- a/deparse.c +++ b/deparse.c @@ -29,7 +29,6 @@ #include "mongo.h" #endif #include "mongo_query.h" -#include "nodes/nodeFuncs.h" #if PG_VERSION_NUM < 120000 #include "nodes/relation.h" #include "optimizer/var.h" diff --git a/mongo_fdw.c b/mongo_fdw.c index 6ab6dff..6100c4a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -32,6 +32,7 @@ #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 From bcb22ac5362ccf8a46228c4399a976701a6328c3 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 1 Dec 2022 19:56:29 +0530 Subject: [PATCH 203/239] Run pgindent. Vaibhav Dalvi. --- deparse.c | 8 ++++---- mongo_fdw.c | 24 +++++++++++++----------- mongo_query.c | 15 ++++++++------- mongo_query.h | 8 ++++---- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/deparse.c b/deparse.c index f1deb2e..26a5c79 100644 --- a/deparse.c +++ b/deparse.c @@ -534,8 +534,8 @@ mongo_append_op_expr(OpExpr *node, BSON *child_doc, pipeline_cxt *context) &expr); else bsonAppendStartObject(child_doc, - psprintf("%d", context->arrayIndex++), - &expr); + psprintf("%d", context->arrayIndex++), + &expr); mongo_add_null_check(var, &expr, context); if (context->isBoolExpr) @@ -638,8 +638,8 @@ mongo_is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, Expr *em_expr; /* - * mongo_is_foreign_expr would detect volatile expressions as well, - * but checking ec_has_volatile here saves some cycles. + * 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 6100c4a..1336846 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -492,10 +492,11 @@ mongoGetForeignRelSize(PlannerInfo *root, fpinfo->options = options; #ifdef META_DRIVER + /* * 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. + * 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; #endif @@ -650,7 +651,7 @@ mongoGetForeignPlan(PlannerInfo *root, bool has_final_sort = false; bool has_limit = false; int64 limit_value; - int64 offset_value; + int64 offset_value; /* * Get FDW private data created by mongoGetForeignUpperPaths(), if any. @@ -859,6 +860,7 @@ mongoGetForeignPlan(PlannerInfo *root, mongofdwreltype = BASE_REL; #ifdef META_DRIVER + /* * We use MongoRelQualInfo to pass various information related to joining * quals and grouping target to fdw_private which is used to form @@ -4520,8 +4522,8 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, /* * 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. + * FOR UPDATE/SHARE clause and there is no LIMIT, don't need to add + * Foreign final path. */ if (parse->rowMarks || !extra->limit_needed) return; @@ -4668,7 +4670,7 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, if (!((Const *) node)->constisnull && (DatumGetInt64(((Const *) node)->constvalue) < 0)) - return; + return; } if (parse->limitOffset) { @@ -4680,7 +4682,7 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, if (!((Const *) node)->constisnull && (DatumGetInt64(((Const *) node)->constvalue) < 0)) - return; + return; } /* Safe to push down */ @@ -4810,13 +4812,13 @@ mongo_is_default_sort_operator(EquivalenceMember *em, PathKey *pathkey) pathkey->pk_strategy); if (!OidIsValid(oprid)) elog(ERROR, "missing operator %d(%u,%u) in opfamily %u", - pathkey->pk_strategy, em->em_datatype, em->em_datatype, - pathkey->pk_opfamily); + pathkey->pk_strategy, em->em_datatype, em->em_datatype, + 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))) + (strncmp(oprname, ">", NAMEDATALEN) == 0))) return false; /* @@ -4824,7 +4826,7 @@ mongo_is_default_sort_operator(EquivalenceMember *em, PathKey *pathkey) * 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), + 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; diff --git a/mongo_query.c b/mongo_query.c index 1c9bc51..28b2e80 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -97,7 +97,7 @@ static List *prepare_var_list_for_baserel(Oid relid, Index varno, 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); + pipeline_cxt *context); static void mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc, pipeline_cxt *context); #endif @@ -772,14 +772,14 @@ mongo_query_document(ForeignScanState *scanStateNode) } } bsonAppendFinishObject(&sort_stage, &sort); - bsonAppendFinishObject(&root_pipeline, &sort_stage); /* End sort */ + bsonAppendFinishObject(&root_pipeline, &sort_stage); /* End sort */ } /* Add LIMIT/SKIP stage */ if (has_limit) { int64 limit_value; - int64 offset_value; + int64 offset_value; /* * Add skip stage for OFFSET clause. However, don't add the same if @@ -1422,6 +1422,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* Increment the Var count */ glob_cxt->varcount++; #endif + /* * If the Var is from the foreign table, we consider its * collation (if any) safe to use. If it is from another @@ -1521,15 +1522,15 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, /* * Recurse to input subexpressions. * - * We support same operators as joinclause for WHERE conditions - * of simple as well as join relation. + * 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) #ifndef META_DRIVER || (glob_cxt->opexprcount > 1) #endif - ) + ) return false; /* @@ -1602,7 +1603,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, #ifndef META_DRIVER || (glob_cxt->varcount > 1) #endif - ) + ) return false; } diff --git a/mongo_query.h b/mongo_query.h index 67ba0af..0bf0b0a 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -26,10 +26,10 @@ typedef struct pipeline_cxt 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 */ + 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; /* From c250cbddd3c12dd1009195e798e85a12f850f69d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 14 Dec 2022 12:40:03 +0530 Subject: [PATCH 204/239] Stamp 5.5.0. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index f5a7ba6..967d790 100644 --- a/expected/select.out +++ b/expected/select.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50400 + 50500 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 8c121af..ef1712e 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -15,7 +15,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50400 + 50500 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 1336846..6164702 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -63,9 +63,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.4.0 so number will be 50400 + * our version is 5.5.0 so number will be 50500 */ -#define CODE_VERSION 50400 +#define CODE_VERSION 50500 #ifdef META_DRIVER /* From 830ba4a11e7f0451768eec442752dfc8e9cce5f2 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 28 Mar 2023 15:24:57 +0530 Subject: [PATCH 205/239] Add enable_join_pushdown guc to control join push-down at the session level. Along with the enable_join_pushdown server/table level option, add the same named guc to control join push-down at the session level. This allows the user to control the push-down per the query/data pattern. Default is true. FDW-558, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Ajay Pal. --- README.md | 8 +++- expected/join_pushdown.out | 79 ++++++++++++++++++++++++++++++++++++ expected/join_pushdown_1.out | 79 ++++++++++++++++++++++++++++++++++++ expected/join_pushdown_2.out | 79 ++++++++++++++++++++++++++++++++++++ expected/join_pushdown_3.out | 79 ++++++++++++++++++++++++++++++++++++ mongo_fdw.c | 34 ++++++++++++---- sql/join_pushdown.sql | 26 ++++++++++++ 7 files changed, 374 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5cd117f..0039d0e 100644 --- a/README.md +++ b/README.md @@ -317,9 +317,13 @@ The following parameters can be supplied while creating user mapping: 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_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`. + operation to the foreign server, instead of fetching rows from the + foreign server and performing the sort locally. Default is `true`. As an example, the following commands demonstrate loading the `mongo_fdw` wrapper, creating a server, and then creating a foreign diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 7567ef4..da06e05 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -1881,6 +1881,85 @@ SELECT 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) + 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; diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index 87a9e0e..5e4967c 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -1851,6 +1851,85 @@ SELECT 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) + 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index 9605e7d..4a412e5 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1865,6 +1865,85 @@ SELECT 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) + 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 0c3c59c..cfb8f4d 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -1900,6 +1900,85 @@ SELECT 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) + 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 6164702..1f8edf4 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -88,6 +88,7 @@ PG_MODULE_MAGIC; #define DEFAULT_MONGO_SORT_MULTIPLIER 1 /* GUC variables. */ +static bool enable_join_pushdown = true; static bool enable_order_by_pushdown = true; #endif @@ -303,10 +304,21 @@ _PG_init(void) { #ifdef META_DRIVER /* - * Sometimes getting a sorted result from MongoDB server is slower than - * performing a sort locally. To have that flexibility add a GUC named - * mongo_fdw.enable_order_by_pushdown to control the ORDER BY push-down. + * 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, @@ -3198,6 +3210,8 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, 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. @@ -3205,6 +3219,15 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, 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 @@ -3321,11 +3344,6 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, 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)) - return false; - /* Recursive joins can't be pushed down */ if (IS_JOIN_REL(outerrel) || IS_JOIN_REL(innerrel)) return false; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 05cb494..709e533 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -504,6 +504,32 @@ 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; + 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; From 429e405e5aa5d43a01535451da184d6a5757fdd9 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 28 Mar 2023 15:24:57 +0530 Subject: [PATCH 206/239] Remove support for v10. Standard Support for both EDB Postgres Advanced Server 10 and PostgreSQL 10 is already ended. Thus, adjust Makefile so that we restrict compilation of mongo_fdw code against v10. Update the README accordingly. Also, clean up the code for the same. FDW-588, Vaibhav Dalvi, reviewed by Sravan Velagandula. --- Makefile | 4 +- Makefile.legacy | 4 +- Makefile.meta | 4 +- README.md | 2 +- deparse.c | 4 - expected/aggregate_pushdown_3.out | 1691 ------------------------ expected/join_pushdown_3.out | 2003 ----------------------------- mongo_fdw.c | 143 +- mongo_query.c | 18 - 9 files changed, 10 insertions(+), 3863 deletions(-) delete mode 100644 expected/aggregate_pushdown_3.out delete mode 100644 expected/join_pushdown_3.out diff --git a/Makefile b/Makefile index 3bbe05e..f2775c7 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) - $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) + $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 7dcd448..4d8db93 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) - $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) + $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index e98d28c..0ec3e4d 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 10 11 12 13 14 15)) - $(error PostgreSQL 10, 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) + $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) endif diff --git a/README.md b/README.md index 0039d0e..e3ef0e3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 10, 11, 12, 13, 14, and 15. +Postgres Advanced Server 11, 12, 13, 14, and 15. Installation ------------ diff --git a/deparse.c b/deparse.c index 26a5c79..59fdba9 100644 --- a/deparse.c +++ b/deparse.c @@ -273,11 +273,7 @@ mongo_check_var(Var *column, MongoRelQualInfo *qual_info) /* Get RangeTblEntry from array in PlannerInfo. */ rte = planner_rt_fetch(column->varno, qual_info->root); -#if PG_VERSION_NUM >= 110000 colname = get_attname(rte->relid, column->varattno, false); -#else - colname = get_relid_attribute_name(rte->relid, column->varattno); -#endif /* Is relation inner or outer? */ if (bms_is_member(column->varno, qual_info->outerRelids)) diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out deleted file mode 100644 index 5326b8e..0000000 --- a/expected/aggregate_pushdown_3.out +++ /dev/null @@ -1,1691 +0,0 @@ -\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 'edb' user with 'edb' 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 ---------------------------------------------------------------------------------- - Sort - Output: c1, (sum(c1)), (count(*)) - Sort Key: fdw137_t1.c1 NULLS FIRST - -> Foreign Scan - Output: c1, (sum(c1)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 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 ---------------------------------------------------------------------------------- - Sort - Output: c8, (min(c2)) - Sort Key: fdw137_t1.c8 NULLS FIRST - -> Foreign Scan - Output: c8, (min(c2)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 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 ---------------------------------------------------------------------------------- - 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) - --- 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Sort - Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) - Sort Key: (max(fdw137_t1.c4)) - -> HashAggregate - Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - max ------- - 400 - 600 - 700 - 800 - 900 - 1300 - -(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 ---------------------------------------------------------------------------------------------------------- - GroupAggregate - Output: t1.c1, count(*), t2.c4 - Group Key: t1.c1, t2.c4 - -> Foreign Scan - Output: t1.c1, t2.c4 - Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2) -(6 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 | 900 - 10 | 1 | - 10 | 1 | 700 - 20 | 1 | 1300 - 20 | 1 | 900 - 20 | 1 | 400 - 20 | 1 | 800 - 20 | 1 | 400 - 30 | 3 | 600 - 30 | 1 | 900 - 30 | 2 | 600 -(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) - --- 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; -ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" --- 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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) - Sort Key: (sum(ftprt1_p1.c1)) - -> HashAggregate - Output: ftprt1_p1.c1, sum(ftprt1_p1.c1) - Group Key: ftprt1_p1.c1 - -> Append - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Foreign Namespace: mongo_fdw_regress.test2 -(13 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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) - Sort Key: (sum(ftprt1_p1.c2)) - -> HashAggregate - Output: ftprt1_p1.c1, sum(ftprt1_p1.c2), min(ftprt1_p1.c2), count(*) - Group Key: ftprt1_p1.c1 - Filter: (avg(ftprt1_p1.c2) < '22'::numeric) - -> Append - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1, ftprt1_p1.c2 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1, ftprt1_p2.c2 - Foreign Namespace: mongo_fdw_regress.test2 -(14 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 - -> HashAggregate - Output: t1.c1, count(((t1.*)::fprt1)) - Group Key: t1.c1 - Filter: (avg(t1.c2) < '22'::numeric) - -> Append - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1, t1.*, t1.c2 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1, t1_1.*, t1_1.c2 - Foreign Namespace: mongo_fdw_regress.test2 -(14 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; -ERROR: unrecognized configuration parameter "enable_partitionwise_aggregate" --- 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 ------------------------------------------------------------------------------------------------------------------------ - Sort - Output: (sum(t2.c1)), t1.c8 - Sort Key: t1.c8 NULLS FIRST - -> 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)) -(6 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 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 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 - 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 - -> 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) -(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 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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; - min | c1 ------+---- - 10 | 10 - 20 | 20 - 30 | 30 - 40 | 40 - 50 | 50 - | -(6 rows) - -EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - QUERY PLAN --------------------------------------------------------------- - Limit - Output: (min(c1)), c1 - -> GroupAggregate - Output: min(c1), c1 - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) - -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - min | c1 ------+---- - 10 | 10 -(1 row) - --- Limit 0, Offset 0 with aggregates. -EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - QUERY PLAN --------------------------------------------------------------- - Limit - Output: (min(c1)), c1 - -> GroupAggregate - Output: min(c1), c1 - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) - -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - min | c1 ------+---- -(0 rows) - --- Limit NULL -EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - QUERY PLAN ---------------------------------------------------------------------------------------- - Limit - Output: (min(c1)), c1 - -> Sort - Output: (min(c1)), c1 - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: (min(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 rows) - -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - min | c1 ------+---- - 20 | 20 - 30 | 30 - 40 | 40 - 50 | 50 -(4 rows) - --- Limit ALL -EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - QUERY PLAN ---------------------------------------------------------------------------------------- - Limit - Output: (min(c1)), c1 - -> Sort - Output: (min(c1)), c1 - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: (min(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 rows) - -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - min | c1 ------+---- - 20 | 20 - 30 | 30 - 40 | 40 - 50 | 50 -(4 rows) - --- Limit with -ve value. Shouldn't pushdown. -EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; - QUERY PLAN --------------------------------------------------------------- - Limit - Output: c1, (max(c1)) - -> GroupAggregate - Output: c1, max(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) - --- Should throw an error. -SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; - QUERY PLAN ---------------------------------------------------------------------------------------- - Limit - Output: c1, (max(c1)) - -> Sort - Output: c1, (max(c1)) - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: c1, (max(c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 rows) - --- Should throw an error. -SELECT c1, max(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)) - -> GroupAggregate - Output: c1, avg(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 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) - -> Sort - Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(12 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 --- 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out deleted file mode 100644 index cfb8f4d..0000000 --- a/expected/join_pushdown_3.out +++ /dev/null @@ -1,2003 +0,0 @@ -\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 'edb' user with 'edb' 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'); -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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 ------------------------------------------------------------------------------------------------------- - Limit - -> Foreign Scan - 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 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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - | | 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 | | -(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) - -> Limit - -> 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) -(8 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) - -> Limit - -> 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) -(8 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 | 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 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: t.c1_1, t.c2_1, t.c1_3 - Sort Key: t.c1_3, t.c1_1 - CTE t - -> Foreign Scan - Output: d.c1, d.c3, e.c1 - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) - -> CTE Scan on t - Output: t.c1_1, t.c2_1, t.c1_3 -(9 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. -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) - --- Don't pushdown when full document retrieval is involved. -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) - --- 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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 | 30 - 100 | 40 -(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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 | 10 - 100 | 20 - 100 | 30 -(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; -ERROR: unrecognized configuration parameter "enable_partitionwise_join" --- 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 - -> Hash Join - Output: t1.c1, t2.c2 - Hash Cond: (t1.c1 = t2.c2) - -> Append - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1 - Foreign Namespace: mongo_fdw_regress.test2 - -> Hash - Output: t2.c2 - -> Append - -> Foreign Scan on public.ftprt2_p1 t2 - Output: t2.c2 - Foreign Namespace: mongo_fdw_regress.test3 - -> Foreign Scan on public.ftprt2_p2 t2_1 - Output: t2_1.c2 - Foreign Namespace: mongo_fdw_regress.test4 -(22 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 - -> Hash Join - Output: t1.c1, t2.c2, t3.c2 - Hash Cond: (t1.c1 = t3.c1) - -> Hash Join - Output: t1.c1, t2.c2 - Hash Cond: (t1.c1 = t2.c2) - -> Append - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1 - Foreign Namespace: mongo_fdw_regress.test2 - -> Hash - Output: t2.c2 - -> Append - -> Foreign Scan on public.ftprt2_p1 t2 - Output: t2.c2 - Foreign Namespace: mongo_fdw_regress.test3 - -> Foreign Scan on public.ftprt2_p2 t2_1 - Output: t2_1.c2 - Foreign Namespace: mongo_fdw_regress.test4 - -> Hash - Output: t3.c2, t3.c1 - -> Append - -> Foreign Scan on public.ftprt1_p1 t3 - Output: t3.c2, t3.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 t3_1 - Output: t3_1.c2, t3_1.c1 - Foreign Namespace: mongo_fdw_regress.test2 -(34 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 ----------------------------------------------------------------- - Merge Join - Output: t1.c1, t1.c2 - Merge Cond: ((t1.c1 = t2.c2) AND (t1.c2 = t2.c1)) - -> Sort - Output: t1.c1, t1.c2 - Sort Key: t1.c1, t1.c2 - -> Append - -> Foreign Scan on public.ftprt1_p1 t1 - Output: t1.c1, t1.c2 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 t1_1 - Output: t1_1.c1, t1_1.c2 - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: t2.c2, t2.c1 - Sort Key: t2.c2, t2.c1 - -> Append - -> Foreign Scan on public.ftprt2_p1 t2 - Output: t2.c2, t2.c1 - Foreign Namespace: mongo_fdw_regress.test3 - -> Foreign Scan on public.ftprt2_p2 t2_1 - Output: t2_1.c2, t2_1.c1 - Foreign Namespace: mongo_fdw_regress.test4 -(23 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 --- Table alias in foreign scan is different for v12, v11 and v10. -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 ------------------------------------------------------------------------------- - Sort - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 - -> Merge Left Join - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Merge Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) - -> Sort - Output: ftprt1_p1.c1 - Sort Key: ftprt1_p1.c1 - -> Append - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Foreign Namespace: mongo_fdw_regress.test2 - -> Sort - Output: ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt2_p1.c2 - -> Append - -> Foreign Scan on public.ftprt2_p1 - Output: ftprt2_p1.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test3 - -> Foreign Scan on public.ftprt2_p2 - Output: ftprt2_p2.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test4 -(26 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; -ERROR: unrecognized configuration parameter "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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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) - -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; -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 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 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 1f8edf4..78208df 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -172,30 +172,21 @@ static void mongoExplainForeignModify(ModifyTableState *mtstate, static bool mongoAnalyzeForeignTable(Relation relation, AcquireSampleRowsFunc *func, BlockNumber *totalpages); -#if PG_VERSION_NUM >= 110000 static void mongoBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo); static void mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo); -#endif #ifdef META_DRIVER static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, JoinPathExtraData *extra); -#if PG_VERSION_NUM >= 110000 static void mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra); -#else -static void mongoGetForeignUpperPaths(PlannerInfo *root, - UpperRelationKind stage, - RelOptInfo *input_rel, - RelOptInfo *output_rel); -#endif #endif /* @@ -230,7 +221,6 @@ static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *innerrel, JoinPathExtraData *extra); static void mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info); -#if PG_VERSION_NUM >= 110000 static bool mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual); @@ -238,13 +228,6 @@ static void mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra); -#else -static bool mongo_foreign_grouping_ok(PlannerInfo *root, - RelOptInfo *grouped_rel); -static void mongo_add_foreign_grouping_paths(PlannerInfo *root, - RelOptInfo *input_rel, - RelOptInfo *grouped_rel); -#endif #if PG_VERSION_NUM >= 120000 static void mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, @@ -372,11 +355,9 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) /* Support for ANALYZE */ fdwRoutine->AnalyzeForeignTable = mongoAnalyzeForeignTable; -#if PG_VERSION_NUM >= 110000 /* Partition routing and/or COPY from */ fdwRoutine->BeginForeignInsert = mongoBeginForeignInsert; fdwRoutine->EndForeignInsert = mongoEndForeignInsert; -#endif #ifdef META_DRIVER /* Support function for join push-down */ @@ -1451,11 +1432,7 @@ mongoPlanForeignModify(PlannerInfo *root, for (attnum = 1; attnum <= tupdesc->natts; attnum++) { -#if PG_VERSION_NUM < 110000 - Form_pg_attribute attr = tupdesc->attrs[attnum - 1]; -#else Form_pg_attribute attr = TupleDescAttr(tupdesc, attnum - 1); -#endif if (!attr->attisdropped) targetAttrs = lappend_int(targetAttrs, attnum); @@ -1583,12 +1560,8 @@ mongoBeginForeignModify(ModifyTableState *mtstate, foreach(lc, fmstate->target_attrs) { int attnum = lfirst_int(lc); -#if PG_VERSION_NUM < 110000 - Form_pg_attribute attr = RelationGetDescr(rel)->attrs[attnum - 1]; -#else Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1); -#endif Assert(!attr->attisdropped); @@ -1635,20 +1608,12 @@ mongoExecForeignInsert(EState *estate, value = slot_getattr(slot, attnum, &isnull); /* First column of MongoDB's foreign table must be _id */ -#if PG_VERSION_NUM < 110000 - if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "_id") != 0) -#else if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "_id") != 0) -#endif 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 PG_VERSION_NUM < 110000 - if (strcmp(slot->tts_tupleDescriptor->attrs[0]->attname.data, "__doc") == 0) -#else if (strcmp(TupleDescAttr(slot->tts_tupleDescriptor, 0)->attname.data, "__doc") == 0) -#endif continue; /* @@ -1666,19 +1631,11 @@ mongoExecForeignInsert(EState *estate, continue; } -#if PG_VERSION_NUM < 110000 - append_mongo_value(bsonDoc, - slot->tts_tupleDescriptor->attrs[attnum - 1]->attname.data, - value, - isnull, - slot->tts_tupleDescriptor->attrs[attnum - 1]->atttypid); -#else append_mongo_value(bsonDoc, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->attname.data, value, isnull, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); -#endif } } bsonFinish(bsonDoc); @@ -1720,12 +1677,8 @@ mongoAddForeignUpdateTargets(Query *parsetree, /* * What we need is the rowid which is the first column */ -#if PG_VERSION_NUM < 110000 - Form_pg_attribute attr = RelationGetDescr(target_relation)->attrs[0]; -#else Form_pg_attribute attr = TupleDescAttr(RelationGetDescr(target_relation), 0); -#endif /* Make a Var representing the desired value */ #if PG_VERSION_NUM >= 140000 @@ -1779,11 +1732,7 @@ mongoExecForeignUpdate(EState *estate, /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, fmstate->rowidAttno, &isNull); -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(foreignTableId, 1); -#else columnName = get_attname(foreignTableId, 1, false); -#endif /* First column of MongoDB's foreign table must be _id */ if (strcmp(columnName, "_id") != 0) @@ -1806,12 +1755,8 @@ mongoExecForeignUpdate(EState *estate, foreach(lc, fmstate->target_attrs) { int attnum = lfirst_int(lc); -#if PG_VERSION_NUM < 110000 - Form_pg_attribute attr = slot->tts_tupleDescriptor->attrs[attnum - 1]; -#else Form_pg_attribute attr = TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1); -#endif Datum value; bool isnull; @@ -1878,11 +1823,7 @@ mongoExecForeignDelete(EState *estate, /* Get the id that was passed up as a resjunk column */ datum = ExecGetJunkAttribute(planSlot, 1, &isNull); -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(foreignTableId, 1); -#else columnName = get_attname(foreignTableId, 1, false); -#endif /* First column of MongoDB's foreign table must be _id */ if (strcmp(columnName, "_id") != 0) @@ -2056,15 +1997,8 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, if (IsA(column, Var)) { if (relType == UPPER_REL) - { -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(foreignTableId, - column->varattno); -#else columnName = get_attname(foreignTableId, column->varattno, false); -#endif - } else columnName = strVal(list_nth(colNameList, listIndex++)); @@ -2080,12 +2014,7 @@ column_mapping_hash(Oid foreignTableId, List *columnList, List *colNameList, } else { -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(foreignTableId, - column->varattno); -#else columnName = get_attname(foreignTableId, column->varattno, false); -#endif hashKey = (void *) columnName; } @@ -2977,18 +2906,11 @@ mongo_acquire_sample_rows(Relation relation, for (columnId = 1; columnId <= columnCount; columnId++) { Var *column = (Var *) palloc0(sizeof(Var)); -#if PG_VERSION_NUM >= 110000 Form_pg_attribute attr = TupleDescAttr(tupleDescriptor, columnId - 1); column->varattno = columnId; column->vartype = attr->atttypid; column->vartypmod = attr->atttypmod; -#else - /* Only assign required fields for column mapping hash */ - column->varattno = columnId; - column->vartype = tupleDescriptor->attrs[columnId - 1]->atttypid; - column->vartypmod = tupleDescriptor->attrs[columnId - 1]->atttypmod; -#endif columnList = lappend(columnList, column); } @@ -3028,17 +2950,9 @@ mongo_acquire_sample_rows(Relation relation, * Use per-tuple memory context to prevent leak of memory used to read * rows from the file with copy routines. */ -#if PG_VERSION_NUM < 110000 - tupleContext = AllocSetContextCreate(CurrentMemoryContext, - "mongo_fdw temporary context", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); -#else tupleContext = AllocSetContextCreate(CurrentMemoryContext, "mongo_fdw temporary context", ALLOCSET_DEFAULT_SIZES); -#endif /* Prepare for sampling rows */ randomState = anl_init_selection_state(targetRowCount); @@ -3161,7 +3075,6 @@ mongo_fdw_version(PG_FUNCTION_ARGS) PG_RETURN_INT32(CODE_VERSION); } -#if PG_VERSION_NUM >= 110000 /* * mongoBeginForeignInsert * Prepare for an insert operation triggered by partition routing @@ -3192,7 +3105,6 @@ mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_EXECUTION), errmsg("COPY and foreign partition routing not supported in mongo_fdw"))); } -#endif #ifdef META_DRIVER /* @@ -3382,11 +3294,8 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, return false; rte = planner_rt_fetch(var->varno, root); -#if PG_VERSION_NUM >= 110000 colname = get_attname(rte->relid, var->varattno, false); -#else - colname = get_relid_attribute_name(rte->relid, var->varattno); -#endif + /* Don't support full document retrieval */ if (strcmp("__doc", colname) == 0) return false; @@ -3559,21 +3468,12 @@ mongo_prepare_qual_info(List *quals, MongoRelQualInfo *qual_info) * information we obtain in this function to MongoFdwRelationInfo of * the input relation. */ -#if PG_VERSION_NUM >= 110000 static bool mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, Node *havingQual) -#else -static bool -mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) -#endif { Query *query = root->parse; -#if PG_VERSION_NUM >= 110000 PathTarget *grouping_target = grouped_rel->reltarget; -#else - PathTarget *grouping_target = root->upper_targets[UPPERREL_GROUP_AGG]; -#endif MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) grouped_rel->fdw_private; MongoFdwRelationInfo *ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; ListCell *lc; @@ -3693,19 +3593,11 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) * Classify the pushable and non-pushable having clauses and save them in * remote_conds and local_conds of the grouped rel's fpinfo. */ -#if PG_VERSION_NUM >= 110000 if (havingQual) { ListCell *lc; foreach(lc, (List *) havingQual) -#else - if (root->hasHavingQual && query->havingQual) - { - ListCell *lc; - - foreach(lc, (List *) query->havingQual) -#endif { Expr *expr = (Expr *) lfirst(lc); RestrictInfo *rinfo; @@ -3803,16 +3695,10 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel) * Add paths for post-join operations like aggregation, grouping etc. if * corresponding operations are safe to push down. */ -#if PG_VERSION_NUM >= 110000 static void mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra) -#else -static void -mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, - RelOptInfo *input_rel, RelOptInfo *output_rel) -#endif { MongoFdwRelationInfo *fpinfo; @@ -3857,11 +3743,9 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, elog(ERROR, "unexpected upper relation: %d", (int) stage); break; } -#elif PG_VERSION_NUM >= 110000 +#else mongo_add_foreign_grouping_paths(root, input_rel, output_rel, (GroupPathExtraData *) extra); -#else - mongo_add_foreign_grouping_paths(root, input_rel, output_rel); #endif } @@ -3872,16 +3756,10 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, * Given input_rel represents the underlying scan. The paths are added to the * given grouped_rel. */ -#if PG_VERSION_NUM >= 110000 static void mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra) -#else -static void -mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, - RelOptInfo *grouped_rel) -#endif { Query *parse = root->parse; MongoFdwRelationInfo *fpinfo = grouped_rel->fdw_private; @@ -3907,11 +3785,7 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, return; /* Assess if it is safe to push down aggregation and grouping. */ -#if PG_VERSION_NUM >= 110000 if (!mongo_foreign_grouping_ok(root, grouped_rel, extra->havingQual)) -#else - if (!mongo_foreign_grouping_ok(root, grouped_rel)) -#endif return; /* @@ -3947,21 +3821,10 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, NIL, /* no pathkeys */ NULL, NIL); /* no fdw_private */ -#elif PG_VERSION_NUM >= 110000 - grouppath = create_foreignscan_path(root, - grouped_rel, - grouped_rel->reltarget, - num_groups, - startup_cost, - total_cost, - NIL, /* no pathkeys */ - grouped_rel->lateral_relids, - NULL, - NIL); /* no fdw_private */ #else grouppath = create_foreignscan_path(root, grouped_rel, - root->upper_targets[UPPERREL_GROUP_AGG], + grouped_rel->reltarget, num_groups, startup_cost, total_cost, diff --git a/mongo_query.c b/mongo_query.c index 28b2e80..8ae7db3 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -367,11 +367,7 @@ mongo_query_document(ForeignScanState *scanStateNode) paramNode = (Param *) find_argument_of_type(argumentList, T_Param); columnId = column->varattno; -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(relationId, columnId); -#else columnName = get_attname(relationId, columnId, false); -#endif if (constant != NULL) append_constant_value(filter, columnName, constant); @@ -402,11 +398,7 @@ mongo_query_document(ForeignScanState *scanStateNode) if (relationId != 0) { columnId = column->varattno; -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(relationId, columnId); -#else columnName = get_attname(relationId, columnId, false); -#endif } /* Find all expressions that correspond to the column */ @@ -1291,16 +1283,10 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, List *columnList = NIL; ListCell *lc; RelOptInfo *scanrel; -#if PG_VERSION_NUM >= 100000 MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; MongoFdwRelationInfo *ofpinfo; -#endif -#if PG_VERSION_NUM >= 100000 scanrel = IS_UPPER_REL(foreignrel) ? fpinfo->outerrel : foreignrel; -#else - scanrel = foreignrel; -#endif if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel)) ofpinfo = (MongoFdwRelationInfo *) fpinfo->outerrel->fdw_private; @@ -1358,11 +1344,7 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; char *columnName; -#if PG_VERSION_NUM < 110000 - columnName = get_relid_attribute_name(rte->relid, var->varattno); -#else columnName = get_attname(rte->relid, var->varattno, false); -#endif *column_name_list = lappend(*column_name_list, makeString(columnName)); if (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel) && From e62d0b297abf15ffd16d7916a6e571111b859124 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 28 Mar 2023 15:42:27 +0530 Subject: [PATCH 207/239] Update EDB copyrights for 2023. Jeevan Chalke. --- Makefile | 2 +- Makefile.legacy | 2 +- Makefile.meta | 2 +- README.md | 2 +- autogen.sh | 2 +- connection.c | 2 +- deparse.c | 2 +- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 2 +- mongo_fdw.control | 2 +- mongo_fdw.h | 2 +- mongo_query.c | 2 +- mongo_query.h | 2 +- mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- mongo_wrapper_meta.c | 2 +- option.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index f2775c7..eabf306 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.legacy b/Makefile.legacy index 4d8db93..0f5ef4a 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/Makefile.meta b/Makefile.meta index 0ec3e4d..a450ba0 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -1,6 +1,6 @@ # mongo_fdw/Makefile.meta # -# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index e3ef0e3..d2b8f1d 100644 --- a/README.md +++ b/README.md @@ -472,7 +472,7 @@ also support mongo_fdw. License ------- -Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. This program is free software: you can redistribute it and/or modify it diff --git a/autogen.sh b/autogen.sh index 8278c23..7500d69 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index 09160f1..64bbf09 100644 --- a/connection.c +++ b/connection.c @@ -4,7 +4,7 @@ * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/deparse.c b/deparse.c index 59fdba9..7e2f4cc 100644 --- a/deparse.c +++ b/deparse.c @@ -4,7 +4,7 @@ * Query deparser for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index d5a9a51..84e6ab7 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index 4112f9b..0996836 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw.c b/mongo_fdw.c index 78208df..de87c4f 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index e207efd..582bad4 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index 3bd080d..98e9216 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index 8ae7db3..da51522 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index 0bf0b0a..c6ef94e 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 90f0e49..6c8aa87 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index ba00b3f..ba28c17 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c index 0598235..4969ce9 100644 --- a/mongo_wrapper_meta.c +++ b/mongo_wrapper_meta.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index d2e58d1..469833e 100644 --- a/option.c +++ b/option.c @@ -4,7 +4,7 @@ * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2022, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From e320b403fd17dd5e46fd85edf1940f040cdd0649 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 24 May 2023 14:38:09 +0530 Subject: [PATCH 208/239] Add enable_aggregate_pushdown guc to control aggregate push-down. Sometimes, rare though, performing grouping on a remote server is slower than doing it locally. Thus provide a session-level guc named enable_aggregate_pushdown to control the aggregation pushdown. Default is true. FDW-559, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan. --- README.md | 4 + expected/aggregate_pushdown.out | 165 +++++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 165 +++++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 168 ++++++++++++++++++++++++++++++ mongo_fdw.c | 14 ++- sql/aggregate_pushdown.sql | 43 ++++++++ 6 files changed, 558 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2b8f1d..433d46c 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,10 @@ GUC variables: 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`. diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 3feec30..0f49f0a 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -1621,6 +1621,171 @@ SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index e700678..0405c51 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -1621,6 +1621,171 @@ SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index ce95727..21847fb 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -1670,6 +1670,174 @@ SELECT c1, avg(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 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 +----------------------------------------------------------------------------------------------------------------------- + Sort + Output: (count(*)), t1.c8 + Sort Key: t1.c8 NULLS FIRST + -> Foreign Scan + Output: (count(*)), t1.c8 + Foreign Namespace: Aggregate on ((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) + +--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) + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; diff --git a/mongo_fdw.c b/mongo_fdw.c index de87c4f..80a6994 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -90,6 +90,7 @@ PG_MODULE_MAGIC; /* GUC variables. */ static bool enable_join_pushdown = true; static bool enable_order_by_pushdown = true; +static bool enable_aggregate_pushdown = true; #endif /* @@ -313,6 +314,17 @@ _PG_init(void) NULL, NULL); + DefineCustomBoolVariable("mongo_fdw.enable_aggregate_pushdown", + "Enable/Disable aggregate push down", + NULL, + &enable_aggregate_pushdown, + true, + PGC_SUSET, + 0, + NULL, + NULL, + NULL); + /* Initialize MongoDB C driver */ mongoc_init(); #endif @@ -3781,7 +3793,7 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, ((MongoFdwRelationInfo *) input_rel->fdw_private)->is_agg_scanrel_pushable; /* If aggregate pushdown is not enabled, honor it. */ - if (!fpinfo->is_agg_scanrel_pushable) + if (!enable_aggregate_pushdown || !fpinfo->is_agg_scanrel_pushable) return; /* Assess if it is safe to push down aggregation and grouping. */ diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index 9c8574a..dc6d21e 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -428,6 +428,49 @@ 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; + -- Cleanup DELETE FROM fdw137_t1 WHERE c8 IS NULL; DELETE FROM fdw137_t1 WHERE c8 = 60; From d5d80847719821dd31a69f9ecdba521599dfefa8 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 24 May 2023 14:47:34 +0530 Subject: [PATCH 209/239] Add enable_order_by_pushdown option to control sort push-down. For some queries, we have observed that sorting on a remote server is very slow. Thus we have added a guc named enable_order_by_pushdown. However, if we want to disable/enable remote server sorting at the table or server level, then it can't be done with the guc. Thus add the server/table level option to control the remote sorting. The table-level value of the option takes precedence over the server-level option value. Default is true. FDW-589, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan. --- README.md | 8 ++ expected/aggregate_pushdown.out | 192 ++++++++++++++++++++++++++++ expected/aggregate_pushdown_1.out | 192 ++++++++++++++++++++++++++++ expected/aggregate_pushdown_2.out | 204 ++++++++++++++++++++++++++++++ expected/join_pushdown.out | 139 ++++++++++++++++++++ expected/join_pushdown_1.out | 139 ++++++++++++++++++++ expected/join_pushdown_2.out | 139 ++++++++++++++++++++ expected/pushdown.out | 91 +++++++++++++ expected/pushdown_1.out | 111 ++++++++++++++++ mongo_fdw.c | 19 ++- mongo_fdw.h | 9 +- option.c | 8 +- sql/aggregate_pushdown.sql | 51 +++++++- sql/join_pushdown.sql | 43 +++++++ sql/pushdown.sql | 37 ++++++ 15 files changed, 1377 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 433d46c..7d810d9 100644 --- a/README.md +++ b/README.md @@ -298,6 +298,12 @@ The following options are only supported with meta driver: 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. Default is `true`. + * `enable_order_by_pushdown`: 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. Default is `true`. The following parameters can be set on a MongoDB foreign table object: @@ -309,6 +315,8 @@ The following parameters can be set on a MongoDB foreign table object: configured at table level as well. Default is `true`. * `enable_aggregate_pushdown`: Similar to the server-level option, but can be configured at table level as well. Default is `true`. + * `enable_order_by_pushdown`: Similar to the server-level option, but can + be configured at table level as well. Default is `true`. The following parameters can be supplied while creating user mapping: diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 0f49f0a..620e2fd 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -1786,6 +1786,198 @@ SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c 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; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 0405c51..e915224 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -1786,6 +1786,198 @@ SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c 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; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 21847fb..aae1f77 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -1838,6 +1838,210 @@ SELECT count(*), t1.c8 FROM fdw137_t1 t1 LEFT JOIN fdw137_t2 t2 ON (t1.c8 = t2.c 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 +--------------------------------------------------------------------------------- + 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) + +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 +----------------------------------------------------------------------------------------------------------------------- + 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) + +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 +--------------------------------------------------------------------------------- + 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) + +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 +----------------------------------------------------------------------------------------------------------------------- + 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) + +-- 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; diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index da06e05..2907478 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -1960,6 +1960,145 @@ SELECT d.c1, 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) + 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; diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index 5e4967c..c93140a 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -1930,6 +1930,145 @@ SELECT d.c1, 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) + 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index 4a412e5..49ff150 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1944,6 +1944,145 @@ SELECT d.c1, 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) + 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; diff --git a/expected/pushdown.out b/expected/pushdown.out index dcd27ab..1a223d4 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -756,6 +756,97 @@ SELECT MIN(a) FROM f_mongo_test ORDER BY 1 USING OPERATOR(public.<^); Output: $0 (12 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'); -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index aa11550..179902f 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -796,6 +796,117 @@ SELECT MIN(a) FROM f_mongo_test ORDER BY 1 USING OPERATOR(public.<^); Foreign Namespace: mongo_fdw_regress.mongo_test (8 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: invalid option "enable_order_by_pushdown" +HINT: Valid options in this context are: address, port, use_remote_estimate. +ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'false'); +ERROR: invalid option "enable_order_by_pushdown" +HINT: Valid options in this context are: address, port, use_remote_estimate. +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 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 rows) + +ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true'); +ERROR: option "enable_order_by_pushdown" not found +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 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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'); +ERROR: option "enable_order_by_pushdown" not found +-- Test the option at table level. +ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true'); +ERROR: invalid option "enable_order_by_pushdown" +HINT: Valid options in this context are: database, collection. +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 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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'); +ERROR: option "enable_order_by_pushdown" not found +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 + Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) + Foreign Namespace: mongo_fdw_regress.test_tbl1 +(7 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'); +ERROR: option "enable_order_by_pushdown" not found +ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true'); +ERROR: option "enable_order_by_pushdown" not found -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; diff --git a/mongo_fdw.c b/mongo_fdw.c index 80a6994..47e7e5c 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -504,6 +504,9 @@ mongoGetForeignRelSize(PlannerInfo *root, * 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; #endif } @@ -3434,6 +3437,10 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, 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. @@ -3800,6 +3807,9 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, 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. * @@ -4134,7 +4144,8 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *useful_pathkeys_list = NIL; /* List of all pathkeys */ /* If orderby pushdown is not enabled, honor it. */ - if (!enable_order_by_pushdown) + if (!enable_order_by_pushdown || + !((MongoFdwRelationInfo *) rel->fdw_private)->is_order_by_pushable) return; /* @@ -4268,8 +4279,12 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, 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) + if (!enable_order_by_pushdown || !fpinfo->is_order_by_pushable) return; /* Shouldn't get here unless the query has ORDER BY */ diff --git a/mongo_fdw.h b/mongo_fdw.h index 98e9216..5bcb220 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -158,6 +158,7 @@ #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" #endif /* Default values for option parameters */ @@ -203,7 +204,7 @@ typedef struct MongoValidOption /* Array of options that are valid for mongo_fdw */ #ifdef META_DRIVER -static const uint32 ValidOptionCount = 21; +static const uint32 ValidOptionCount = 23; #else static const uint32 ValidOptionCount = 7; #endif @@ -227,6 +228,7 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_WEAK_CERT, ForeignServerRelationId}, {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignServerRelationId}, {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignServerRelationId}, + {OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignServerRelationId}, #endif /* Foreign table options */ @@ -235,6 +237,7 @@ static const MongoValidOption ValidOptionArray[] = #ifdef META_DRIVER {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignTableRelationId}, {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignTableRelationId}, + {OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignTableRelationId}, #endif /* User mapping options */ @@ -269,6 +272,7 @@ typedef struct MongoFdwOptions bool weak_cert_validation; bool enable_join_pushdown; bool enable_aggregate_pushdown; + bool enable_order_by_pushdown; #endif } MongoFdwOptions; @@ -372,6 +376,9 @@ typedef struct MongoFdwRelationInfo * pushable. */ bool is_agg_scanrel_pushable; + + /* Inherit required flags from MongoFdwOptions */ + bool is_order_by_pushable; } MongoFdwRelationInfo; /* diff --git a/option.c b/option.c index 469833e..d1dd054 100644 --- a/option.c +++ b/option.c @@ -110,7 +110,8 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) || 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_AGGREGATE_PUSHDOWN) == 0 || + strcmp(optionName, OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN) == 0 #endif ) { @@ -188,6 +189,7 @@ mongo_get_options(Oid foreignTableId) options->weak_cert_validation = false; options->enable_join_pushdown = true; options->enable_aggregate_pushdown = true; + options->enable_order_by_pushdown = true; #endif /* Loop through the options */ @@ -233,6 +235,10 @@ mongo_get_options(Oid foreignTableId) 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 */ #endif diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index dc6d21e..cf0538c 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -70,7 +70,6 @@ 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; @@ -471,6 +470,56 @@ 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; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 709e533..48711a4 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -70,6 +70,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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 @@ -530,6 +531,48 @@ 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; + 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; diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 480881c..23c0487 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -320,6 +320,43 @@ SELECT * FROM f_mongo_test ORDER BY a USING OPERATOR(public.<^); EXPLAIN (COSTS FALSE, VERBOSE) SELECT MIN(a) FROM f_mongo_test 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'); + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; From d742be95077d364f5531d0b9802c68b2aa5e8fb3 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 6 Jul 2023 15:48:32 +0530 Subject: [PATCH 210/239] Fix boolean expression handling in query document building. Reported on GitHub through issue #58 by simon-wolf. FDW-631, Vaibhav Dalvi, reviewed by Jeevan Chalke. --- deparse.c | 3 +-- expected/pushdown.out | 37 +++++++++++++++++++++++++++++++++++++ expected/pushdown_1.out | 39 +++++++++++++++++++++++++++++++++++++++ sql/pushdown.sql | 8 ++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) diff --git a/deparse.c b/deparse.c index 7e2f4cc..e7ad9a4 100644 --- a/deparse.c +++ b/deparse.c @@ -380,8 +380,7 @@ mongo_append_bool_expr(BoolExpr *node, BSON *child_doc, pipeline_cxt *context) break; case NOT_EXPR: op = "$not"; - mongo_append_expr(linitial(node->args), child_doc, context); - return; + break; } bsonAppendStartObject(child_doc, psprintf("%d", context->arrayIndex), &expr); diff --git a/expected/pushdown.out b/expected/pushdown.out index 1a223d4..f1c47b6 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -847,6 +847,43 @@ SELECT c1, c4 FROM f_test_tbl1 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) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index 179902f..cab8360 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -907,6 +907,45 @@ ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true'); ERROR: option "enable_order_by_pushdown" not found ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true'); ERROR: option "enable_order_by_pushdown" not found +-- 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 + Filter: (NOT f_test_tbl3.pass) + Foreign Namespace: mongo_fdw_regress.test_tbl3 +(7 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 + Filter: f_test_tbl3.pass + Foreign Namespace: mongo_fdw_regress.test_tbl3 +(7 rows) + +SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name; + name | pass +------+------ + vdd | t +(1 row) + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 23c0487..3cf124a 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -357,6 +357,14 @@ SELECT c1, c4 FROM f_test_tbl1 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; + -- Cleanup DELETE FROM f_mongo_test WHERE a != 0; DELETE FROM f_test_tbl2 WHERE c1 IS NULL; From 0919481bc78ffc704fa89f636d310972d18fd188 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 6 Jul 2023 15:49:05 +0530 Subject: [PATCH 211/239] Add support for PostgreSQL 16 and EDB Postgres Advanced Server 16. Code changes involve adjusting the make_restrictinfo() call per PostgreSQL's upstream changes. Matches reltarget entries only on varno/varattno. It also takes into account the new RTEPermissionInfo stuff. Beginning with v16, fs_relids includes the rangetable indexes of outer joins, if any were involved in this join. The new field fs_base_relids includes only base relation indexes and thus mimics fs_relids's old semantics. Need to adjust the fdw code to account for this. FDW-603, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajay Pal, a few changes by Jeevan Chalke. --- Makefile | 4 +-- Makefile.legacy | 4 +-- Makefile.meta | 4 +-- README.md | 2 +- mongo_fdw.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++--- mongo_query.c | 56 ++++++++++++++++++++++++++++++++++++++++++ option.c | 7 +++--- 7 files changed, 129 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index eabf306..d59afec 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) - $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15 16)) + $(error PostgreSQL 11, 12, 13, 14, 15, or 16 is required to compile this extension) endif diff --git a/Makefile.legacy b/Makefile.legacy index 0f5ef4a..d3fc16a 100644 --- a/Makefile.legacy +++ b/Makefile.legacy @@ -46,6 +46,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) - $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15 16)) + $(error PostgreSQL 11, 12, 13, 14 15, or 16 is required to compile this extension) endif diff --git a/Makefile.meta b/Makefile.meta index a450ba0..2dec22f 100644 --- a/Makefile.meta +++ b/Makefile.meta @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15)) - $(error PostgreSQL 11, 12, 13, 14, or 15 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15 16)) + $(error PostgreSQL 11, 12, 13, 14 15, or 16 is required to compile this extension) endif diff --git a/README.md b/README.md index 7d810d9..a9114e1 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ 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 11, 12, 13, 14, and 15. +Postgres Advanced Server 11, 12, 13, 14, 15, and 16. Installation ------------ diff --git a/mongo_fdw.c b/mongo_fdw.c index 47e7e5c..64d8b94 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -45,6 +45,9 @@ #include "optimizer/var.h" #endif #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" @@ -1163,7 +1166,11 @@ mongoExplainForeignScan(ForeignScanState *node, ExplainState *es) 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) @@ -1256,10 +1263,19 @@ mongoBeginForeignScan(ForeignScanState *node, int eflags) 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; @@ -1455,12 +1471,32 @@ mongoPlanForeignModify(PlannerInfo *root, } else if (operation == CMD_UPDATE) { - Bitmapset *tmpset = bms_copy(rte->updatedCols); + 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"); @@ -1516,6 +1552,12 @@ mongoBeginForeignModify(ModifyTableState *mtstate, 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 @@ -1524,8 +1566,14 @@ mongoBeginForeignModify(ModifyTableState *mtstate, 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); - userid = GetUserId(); /* Get info about foreign table. */ table = GetForeignTable(foreignTableId); @@ -3626,7 +3674,18 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, * RestrictInfos, so we must make our own. */ Assert(!IsA(expr, RestrictInfo)); -#if PG_VERSION_NUM >= 140000 +#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, diff --git a/mongo_query.c b/mongo_query.c index da51522..762797f 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -102,6 +102,10 @@ static void mongo_append_clauses_to_pipeline(List *clause, BSON *child_doc, pipeline_cxt *context); #endif +#if PG_VERSION_NUM >= 160000 +static List *mongo_append_unique_var(List *varlist, Var *var); +#endif + #ifndef META_DRIVER /* * find_argument_of_type @@ -907,8 +911,12 @@ unique_column_list(List *operatorList) Var *column = (Var *) find_argument_of_type(argumentList, T_Var); +#if PG_VERSION_NUM >= 160000 + uniqueColumnList = mongo_append_unique_var(uniqueColumnList, column); +#else /* List membership is determined via column's equal() function */ uniqueColumnList = list_append_unique(uniqueColumnList, column); +#endif } return uniqueColumnList; @@ -1303,7 +1311,11 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, */ 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; } @@ -1320,6 +1332,9 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, List *wr_var_list; RangeTblEntry *rte = rt_fetch(var->varno, root->parse->rtable); Bitmapset *attrs_used; +#if PG_VERSION_NUM >= 160000 + ListCell *cell; +#endif Assert(OidIsValid(rte->relid)); @@ -1332,11 +1347,25 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, 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))) @@ -2077,3 +2106,30 @@ mongo_is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr) return false; } #endif + +#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 diff --git a/option.c b/option.c index d1dd054..6b3b636 100644 --- a/option.c +++ b/option.c @@ -243,16 +243,16 @@ mongo_get_options(Oid foreignTableId) #endif if (strcmp(def->defname, OPTION_NAME_ADDRESS) == 0) - options->svr_address = defGetString(def); + 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 = defGetString(def); + options->svr_database = pstrdup(defGetString(def)); else if (strcmp(def->defname, OPTION_NAME_COLLECTION) == 0) - options->collectionName = defGetString(def); + options->collectionName = pstrdup(defGetString(def)); else if (strcmp(def->defname, OPTION_NAME_USERNAME) == 0) options->svr_username = defGetString(def); @@ -287,6 +287,7 @@ mongo_free_options(MongoFdwOptions *options) { pfree(options->svr_address); pfree(options->svr_database); + pfree(options->collectionName); pfree(options); } } From bfc93f97f221406830e1351e5ae2f3a681818eb1 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 7 Jul 2023 10:40:07 +0530 Subject: [PATCH 212/239] Add missing expected output files. I forgot to include them in the previous commit, i.e. 0919481bc7. FDW-603, Vaibhav Dalvi. --- expected/aggregate_pushdown_3.out | 2003 +++++++++++++++++++++++++++ expected/join_pushdown_3.out | 2127 +++++++++++++++++++++++++++++ 2 files changed, 4130 insertions(+) create mode 100644 expected/aggregate_pushdown_3.out create mode 100644 expected/join_pushdown_3.out diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out new file mode 100644 index 0000000..3635786 --- /dev/null +++ b/expected/aggregate_pushdown_3.out @@ -0,0 +1,2003 @@ +\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 'edb' user with 'edb' 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Sort + Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (max(fdw137_t1.c4)) + -> HashAggregate + Output: max(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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; + max +------ + 400 + 600 + 700 + 800 + 900 + 1300 + +(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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + min | c1 +-----+---- + 10 | 10 + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 + | +(6 rows) + +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; + min | c1 +-----+---- + 10 | 10 +(1 row) + +-- Limit 0, Offset 0 with aggregates. +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; + min | c1 +-----+---- +(0 rows) + +-- Limit NULL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit ALL +EXPLAIN (VERBOSE, COSTS OFF) +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + QUERY PLAN +--------------------------------------------------------------------------- + Foreign Scan + Output: (min(c1)), c1 + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(3 rows) + +SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; + min | c1 +-----+---- + 20 | 20 + 30 | 30 + 40 | 40 + 50 | 50 +(4 rows) + +-- Limit with -ve value. Shouldn't pushdown. +EXPLAIN (VERBOSE, COSTS FALSE) +SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +-------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> GroupAggregate + Output: c1, max(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 rows) + +-- Should throw an error. +SELECT c1, max(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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; + QUERY PLAN +--------------------------------------------------------------------------------- + Limit + Output: c1, (max(c1)) + -> Foreign Scan + Output: c1, (max(c1)) + Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) +(5 rows) + +-- Should throw an error. +SELECT c1, max(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)) + -> GroupAggregate + Output: c1, avg(c1) + Group Key: fdw137_t2.c1 + -> Foreign Scan on public.fdw137_t2 + Output: _id, c1, c2, c3 + Foreign Namespace: mongo_fdw_regress.test_tbl2 +(8 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out new file mode 100644 index 0000000..b3bd36c --- /dev/null +++ b/expected/join_pushdown_3.out @@ -0,0 +1,2127 @@ +\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 'edb' user with 'edb' 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'); +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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + 30 | SALES | 200 | EMP2 | 1600 | 30 + 30 | SALES | 300 | EMP3 | 1250 | 30 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 + 30 | SALES | 500 | EMP5 | 1250.23 | 30 + 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 + 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + 30 | SALES | 200 | EMP2 | 1600 | 30 + 30 | SALES | 300 | EMP3 | 1250 | 30 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 + 30 | SALES | 500 | EMP5 | 1250.23 | 30 + 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 + 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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 | 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 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. +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) + +-- Don't pushdown when full document retrieval is involved. +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) + +-- 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 | 10 + 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 | 10 + 100 | 20 + 100 | 30 +(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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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) + +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; +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 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 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; From d54d029ab61dbc242d2df3079b0cec2b549eec13 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 7 Jul 2023 10:43:36 +0530 Subject: [PATCH 213/239] Fix warnings produced by -Wshadow=compatible-local FDW-626, Vaibhav Dalvi, reviewed by Jeevan Chalke. --- mongo_fdw.c | 9 +-------- mongo_query.c | 3 --- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 64d8b94..ec3bf94 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -792,8 +792,6 @@ mongoGetForeignPlan(PlannerInfo *root, */ if (outer_plan) { - ListCell *lc; - /* * First, update the plan's qual list if possible. In some cases, * the quals might be enforced below the topmost plan level, in @@ -2215,9 +2213,7 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, Oid columnTypeId = InvalidOid; Oid columnArrayTypeId = InvalidOid; bool compatibleTypes = false; - bool handleFound = false; const char *bsonFullKey; - void *hashKey; int32 attnum; bool is_agg = false; @@ -3645,7 +3641,7 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, */ foreach(l, aggvars) { - Expr *expr = (Expr *) lfirst(l); + expr = (Expr *) lfirst(l); if (IsA(expr, Aggref)) tlist = add_to_flat_tlist(tlist, list_make1(expr)); @@ -3662,8 +3658,6 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, */ if (havingQual) { - ListCell *lc; - foreach(lc, (List *) havingQual) { Expr *expr = (Expr *) lfirst(lc); @@ -3720,7 +3714,6 @@ mongo_foreign_grouping_ok(PlannerInfo *root, RelOptInfo *grouped_rel, if (fpinfo->local_conds) { List *aggvars = NIL; - ListCell *lc; foreach(lc, fpinfo->local_conds) { diff --git a/mongo_query.c b/mongo_query.c index 762797f..c901b12 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -692,7 +692,6 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Add HAVING operation */ if (having_expr) { - BSON match_stage; pipeline_cxt context; context.colInfoHash = columnInfoHash; @@ -1330,7 +1329,6 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, if (var->varattno == 0) { List *wr_var_list; - RangeTblEntry *rte = rt_fetch(var->varno, root->parse->rtable); Bitmapset *attrs_used; #if PG_VERSION_NUM >= 160000 ListCell *cell; @@ -1370,7 +1368,6 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, if (IS_JOIN_REL(foreignrel) || (IS_UPPER_REL(foreignrel) && IS_JOIN_REL(scanrel))) { - MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; char *columnName; columnName = get_attname(rte->relid, var->varattno, false); From 4cdcfae022a9bfd0e9b717e10b23dc0eee6f2ad5 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 7 Jul 2023 20:21:47 +0530 Subject: [PATCH 214/239] Update comments in README.md and test case files. The execution of mongodb_init.sh is compulsory before running regression, and it is missed to mention it in the README file. Also, update a few comments in test cases. FDW-600, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke. --- README.md | 13 ++++++++++--- expected/aggregate_pushdown.out | 6 +++--- expected/aggregate_pushdown_1.out | 6 +++--- expected/aggregate_pushdown_2.out | 6 +++--- expected/aggregate_pushdown_3.out | 6 +++--- expected/connection_validation.out | 7 +++---- expected/connection_validation_1.out | 7 +++---- expected/dml.out | 8 ++++---- expected/join_pushdown.out | 6 +++--- expected/join_pushdown_1.out | 6 +++--- expected/join_pushdown_2.out | 6 +++--- expected/join_pushdown_3.out | 6 +++--- expected/limit_offset_pushdown.out | 5 +++-- expected/limit_offset_pushdown_1.out | 5 +++-- expected/pushdown.out | 7 +++---- expected/pushdown_1.out | 7 +++---- expected/select.out | 7 +++---- expected/select_1.out | 7 +++---- expected/server_options.out | 7 +++---- expected/server_options_1.out | 7 +++---- mongodb_init.sh | 2 ++ sql/aggregate_pushdown.sql | 6 +++--- sql/connection_validation.sql | 7 +++---- sql/dml.sql | 8 ++++---- sql/join_pushdown.sql | 6 +++--- sql/limit_offset_pushdown.sql | 5 +++-- sql/pushdown.sql | 7 +++---- sql/select.sql | 7 +++---- sql/server_options.sql | 7 +++---- 29 files changed, 95 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index a9114e1..8b31b8d 100644 --- a/README.md +++ b/README.md @@ -176,12 +176,19 @@ EDB Postgres Advanced Server. 4. Running regression test. + Run `mongodb_init.sh` file to load required collections. + + ```sh + source mongodb_init.sh + ``` + Finally, run regression. + ```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. + 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]. diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 620e2fd..005aba8 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index e915224..374a004 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index aae1f77..432d90e 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out index 3635786..12a3956 100644 --- a/expected/aggregate_pushdown_3.out +++ b/expected/aggregate_pushdown_3.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/connection_validation.out b/expected/connection_validation.out index 9cfcd63..bcf665f 100644 --- a/expected/connection_validation.out +++ b/expected/connection_validation.out @@ -3,10 +3,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out index 92678f0..2bd1251 100644 --- a/expected/connection_validation_1.out +++ b/expected/connection_validation_1.out @@ -3,10 +3,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/dml.out b/expected/dml.out index b094a93..cff659a 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -2,10 +2,10 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 2907478..3fa1e39 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out index c93140a..5115d36 100644 --- a/expected/join_pushdown_1.out +++ b/expected/join_pushdown_1.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index 49ff150..a7f4557 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index b3bd36c..096ef91 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -2,9 +2,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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 diff --git a/expected/limit_offset_pushdown.out b/expected/limit_offset_pushdown.out index b797352..0227f28 100644 --- a/expected/limit_offset_pushdown.out +++ b/expected/limit_offset_pushdown.out @@ -2,8 +2,9 @@ \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 'edb' user with 'edb' password and ran +-- 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; diff --git a/expected/limit_offset_pushdown_1.out b/expected/limit_offset_pushdown_1.out index b4d361b..4983483 100644 --- a/expected/limit_offset_pushdown_1.out +++ b/expected/limit_offset_pushdown_1.out @@ -2,8 +2,9 @@ \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 'edb' user with 'edb' password and ran +-- 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; diff --git a/expected/pushdown.out b/expected/pushdown.out index f1c47b6..035d15f 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out index cab8360..750701a 100644 --- a/expected/pushdown_1.out +++ b/expected/pushdown_1.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/select.out b/expected/select.out index 967d790..e98ec03 100644 --- a/expected/select.out +++ b/expected/select.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/select_1.out b/expected/select_1.out index ef1712e..9aa50f5 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/server_options.out b/expected/server_options.out index f0858da..fb9216d 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/expected/server_options_1.out b/expected/server_options_1.out index 46191ba..62f3b9d 100644 --- a/expected/server_options_1.out +++ b/expected/server_options_1.out @@ -2,10 +2,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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 diff --git a/mongodb_init.sh b/mongodb_init.sh index 8a140d6..358d929 100755 --- a/mongodb_init.sh +++ b/mongodb_init.sh @@ -11,6 +11,8 @@ export MONGO_PWD="edb" # 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 diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index cf0538c..cb6f4e0 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -3,9 +3,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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; diff --git a/sql/connection_validation.sql b/sql/connection_validation.sql index daacd20..4d5ae79 100644 --- a/sql/connection_validation.sql +++ b/sql/connection_validation.sql @@ -4,10 +4,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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; diff --git a/sql/dml.sql b/sql/dml.sql index 503bb5f..aa61bff 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -3,10 +3,10 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 48711a4..4cd4cdd 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -3,9 +3,9 @@ \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 'edb' user with 'edb' password and ran --- mongodb_init.sh file to load collections. +-- 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; diff --git a/sql/limit_offset_pushdown.sql b/sql/limit_offset_pushdown.sql index 9071483..af357b7 100644 --- a/sql/limit_offset_pushdown.sql +++ b/sql/limit_offset_pushdown.sql @@ -3,8 +3,9 @@ \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 'edb' user with 'edb' password and ran +-- 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 diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 3cf124a..0345355 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -3,10 +3,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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; diff --git a/sql/select.sql b/sql/select.sql index 5629613..5e07dc6 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -3,10 +3,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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; diff --git a/sql/server_options.sql b/sql/server_options.sql index 01b53c1..7c444a0 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -3,10 +3,9 @@ \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 and --- mongo_fdw_regress1 databases on MongoDB with all permission for --- MONGO_USER_NAME user with MONGO_PASS password and ran mongodb_init.sh file --- to load collections. +-- 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; From 9bfc78d52bd6dfe6b51ff3eee6c7be4d93d513f4 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 14 Jul 2023 15:30:25 +0530 Subject: [PATCH 215/239] Stamp 5.5.1. --- expected/select.out | 2 +- expected/select_1.out | 2 +- mongo_fdw.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/expected/select.out b/expected/select.out index e98ec03..8bc9883 100644 --- a/expected/select.out +++ b/expected/select.out @@ -14,7 +14,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50500 + 50501 (1 row) -- Create foreign tables diff --git a/expected/select_1.out b/expected/select_1.out index 9aa50f5..4b6988b 100644 --- a/expected/select_1.out +++ b/expected/select_1.out @@ -14,7 +14,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50500 + 50501 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index ec3bf94..185a959 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -66,9 +66,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.5.0 so number will be 50500 + * our version is 5.5.1 so number will be 50501 */ -#define CODE_VERSION 50500 +#define CODE_VERSION 50501 #ifdef META_DRIVER /* From c2d2b7e4c3df331c3681c2bebf8d900c46909db5 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 4 Jan 2024 11:40:33 +0530 Subject: [PATCH 216/239] Remove support for legacy drivers. MongoDB stopped supporting this driver version for a few years now, and we have already given deprecation warnings in the previous releases. So, clean up the related code. FDW-664, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajay Pal. --- Makefile | 4 +- Makefile.legacy | 51 - Makefile.meta | 47 - README.md | 62 +- autogen.sh | 81 +- connection.c | 2 - deparse.c | 4 - expected/connection_validation_1.out | 71 -- expected/pushdown_1.out | 964 ------------------ expected/select_1.out | 1379 -------------------------- expected/server_options.out | 2 - expected/server_options_1.out | 195 ---- mongo_fdw.c | 276 +----- mongo_fdw.h | 58 -- mongo_query.c | 312 +----- mongo_wrapper.c | 585 +++++++---- mongo_wrapper.h | 23 +- mongo_wrapper_meta.c | 780 --------------- option.c | 6 - sql/server_options.sql | 2 - 20 files changed, 441 insertions(+), 4463 deletions(-) delete mode 100644 Makefile.legacy delete mode 100644 Makefile.meta delete mode 100644 expected/connection_validation_1.out delete mode 100644 expected/pushdown_1.out delete mode 100644 expected/select_1.out delete mode 100644 expected/server_options_1.out delete mode 100644 mongo_wrapper_meta.c diff --git a/Makefile b/Makefile index d59afec..f0b4064 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,10 @@ LIBJSON_OBJS = $(LIBJSON)/json_util.o $(LIBJSON)/json_object.o $(LIBJSON)/json_ $(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) -DMETA_DRIVER +PG_CPPFLAGS = --std=c99 $(MONGO_INCLUDE) -I$(LIBJSON) SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) -OBJS = connection.o option.o mongo_wrapper_meta.o mongo_fdw.o mongo_query.o deparse.o $(LIBJSON_OBJS) +OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o deparse.o $(LIBJSON_OBJS) EXTENSION = mongo_fdw diff --git a/Makefile.legacy b/Makefile.legacy deleted file mode 100644 index d3fc16a..0000000 --- a/Makefile.legacy +++ /dev/null @@ -1,51 +0,0 @@ -# mongo_fdw/Makefile -# -# Portions Copyright (c) 2004-2023, 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. -# -MONGO_DRIVER = mongo-c-driver -MONGO_PATH = $(MONGO_DRIVER)/src -MONGO_OBJS = $(MONGO_PATH)/bson.os $(MONGO_PATH)/encoding.os $(MONGO_PATH)/md5.os \ - $(MONGO_PATH)/mongo.os $(MONGO_PATH)/numbers.os $(MONGO_PATH)/env.os -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 -PG_CPPFLAGS = --std=c99 -I$(MONGO_PATH) -I$(LIBJSON) -OBJS = connection.o option.o mongo_wrapper.o mongo_fdw.o mongo_query.o $(MONGO_OBJS) $(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 -REGRESS_OPTS = --load-extension=$(EXTENSION) - -$(MONGO_DRIVER)/%.os: - $(MAKE) -C $(MONGO_DRIVER) $*.os -#$(LIBJSON)/json.o: -# $(MAKE) -C $(LIBJSON) - -# -# 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), 11 12 13 14 15 16)) - $(error PostgreSQL 11, 12, 13, 14 15, or 16 is required to compile this extension) -endif diff --git a/Makefile.meta b/Makefile.meta deleted file mode 100644 index 2dec22f..0000000 --- a/Makefile.meta +++ /dev/null @@ -1,47 +0,0 @@ -# mongo_fdw/Makefile.meta -# -# Portions Copyright (c) 2004-2023, 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) -DMETA_DRIVER -SHLIB_LINK = $(shell pkg-config --libs libmongoc-1.0) - -OBJS = connection.o option.o mongo_wrapper_meta.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) - -# -# 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), 11 12 13 14 15 16)) - $(error PostgreSQL 11, 12, 13, 14 15, or 16 is required to compile this extension) -endif diff --git a/README.md b/README.md index 8b31b8d..1e0f51d 100644 --- a/README.md +++ b/README.md @@ -27,18 +27,13 @@ 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. -Build with [MongoDB][1]'s legacy branch driver (Deprecated in mongo_fdw 5.4.0) - * autogen.sh --with-legacy + * autogen.sh -Build [MongoDB][1]'s master branch driver - * autogen.sh --with-master - -The script autogen.sh will do all the necessary steps to build with legacy and -meta driver accordingly. +The script autogen.sh will do all the necessary steps to build with mongoc +driver accordingly. ## Steps for manual installation ### mongo-c -#### meta driver 1. Download and extract source code of mongoc driver for version `1.17.3` ```sh @@ -70,21 +65,6 @@ meta driver accordingly. For more details on installation of mongo-c driver, you can refer [here][5]. -#### Legacy driver - -Deprecation Notice: -The legacy driver support has been deprecated in mongo_fdw 5.4.0 and is -expected to be removed entirely in a future release. - -* Checkout, extract legacy branch - - ```sh - wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz - tar -zxf v0.8.tar.gz - rm -rf mongo-c-driver - mv mongo-c-driver-0.8 mongo-c-driver - ``` - ### json-c 1. Download and extract source code @@ -116,20 +96,6 @@ expected to be removed entirely in a future release. For more details on installation of json-c library, you can refer [here][6]. -### How to compile against mongo-c Meta or Legacy driver? -To compile against legacy driver, 'Makefile.legacy' must be used and -'Makefile.meta' must be used to compile against the meta driver. For example, -this can be achieved by copying required Makefile as shown below: -For meta, - - cp Makefile.meta Makefile - -For legacy (Deprecated in mongo_fdw 5.4.0), - - cp Makefile.legacy Makefile - -The default compilation is with Meta driver. - ## 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, @@ -242,25 +208,6 @@ 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. -### New MongoDB C Driver Support -This enhancement is to add a new [MongoDB][1]' C driver. The current -implementation is based on the legacy driver of MongoDB. But -[MongoDB][1] is provided completely new library for driver called -MongoDB's meta driver. Added support for the same. Now compile time -option is available to use legacy and meta driver. - -In order to use MongoDB driver 1.17.0+, take the following steps: - - * clone `libmongoc` version 1.17.0+ - (https://github.com/mongodb/mongo-c-driver) and follow the install - directions given there. `libbson` is now maintained in a subdirectory - of the `libmongoc`. - (https://github.com/mongodb/mongo-c-driver/tree/master/src/libbson). - * ensure pkg-config / pkgconf is installed on your system. - * run `make -f Makefile.meta && make -f Makefile.meta install` - * if you get an error when trying to `CREATE EXTENSION mongo_fdw;`, - then try running `ldconfig` - Usage ----- The following parameters can be set on a MongoDB foreign server object: @@ -270,9 +217,6 @@ The following parameters can be set on a MongoDB foreign server object: * `port`: Port number of the MongoDB server. Defaults to `27017`. * `use_remote_estimate`: Controls whether mongo_fdw uses exact rows from remote collection to obtain cost estimates. Default is `false`. - -The following options are only supported with meta driver: - * `authentication_database`: Database against which user will be authenticated against. Only valid with password based authentication. * `replica_set`: Replica set the server is member of. If set, diff --git a/autogen.sh b/autogen.sh index 7500d69..ac17137 100755 --- a/autogen.sh +++ b/autogen.sh @@ -19,8 +19,9 @@ JSONC_VERSION=0.15-20200726 MONGOC_INSTALL="${MONGOC_INSTALL_DIR}" JSONC_INSTALL="${JSONC_INSTALL_DIR}" -if [ "$#" -ne 1 ]; then - echo "Usage: autogen.sh --[with-legacy | with-master]" +# Don't allow input to the script +if [ "$#" -ne 0 ]; then + echo "Usage: autogen.sh" exit fi @@ -41,17 +42,6 @@ function checkout_mongo_driver rm -rf mongo-c-driver-$MONGOC_VERSION.tar.gz } -### -# Pull the legacy branch of the Mongo C Driver -# -function checkout_legacy_branch -{ - rm -rf mongo-c-driver && - wget https://github.com/mongodb/mongo-c-driver/archive/v0.8.tar.gz && - tar -zxf v0.8.tar.gz && - mv mongo-c-driver-0.8 mongo-c-driver && - rm -rf v0.8.tar.gz -} ## # Pull the json-c library # @@ -89,60 +79,17 @@ function install_mongoc_driver cd .. } -### -# Cleanup the system -# -function cleanup -{ - rm -f config.h - touch config.h -} - -### -# Create a config file and append #define META_DRIVER which will be -# used in case of Meta Driver (master branch) option. -# -function create_config -{ - echo "#ifdef __CONFIG__" >> config.h && - echo "#define META_DRIVER" >> config.h && - echo "#endif" >> config.h -} - -cleanup - -if [ "--with-legacy" = $1 ]; then - echo "Warning: The legacy driver support has been deprecated in mongo_fdw 5.4.0 and is expected to be removed entirely in a future release." - checkout_json_lib && - checkout_legacy_branch && - install_json_lib && - cp Makefile.legacy Makefile - - ret=$? - if [ "$ret" -ne 0 ]; then - echo "Failed" - exit $ret - else - echo "Done" - exit 0 - fi -elif [ "--with-master" == $1 ]; then - checkout_mongo_driver && - checkout_json_lib && - install_mongoc_driver && - install_json_lib && - create_config && - export PKG_CONFIG_PATH=mongo-c-driver/src/libmongoc/src:mongo-c-driver/src/libbson/src && - cp Makefile.meta Makefile +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 +ret=$? +if [ "$ret" -ne 0 ]; then + echo "Failed" + exit $ret else - echo "Usage: autogen.sh --[with-legacy | with-master]" + echo "Done" + exit 0 fi diff --git a/connection.c b/connection.c index 64bbf09..b1a8d28 100644 --- a/connection.c +++ b/connection.c @@ -136,7 +136,6 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, ObjectIdGetDatum(user->umid)); } -#ifdef META_DRIVER /* Check if the existing or new connection is reachable/active or not? */ if (entry->conn != NULL) { @@ -154,7 +153,6 @@ mongo_get_connection(ForeignServer *server, UserMapping *user, server->servername), errhint("Mongo error: \"%s\"", error.message))); } -#endif return entry->conn; } diff --git a/deparse.c b/deparse.c index e7ad9a4..0512fc2 100644 --- a/deparse.c +++ b/deparse.c @@ -23,11 +23,7 @@ #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" #endif -#ifdef META_DRIVER #include "mongoc.h" -#else -#include "mongo.h" -#endif #include "mongo_query.h" #if PG_VERSION_NUM < 120000 #include "nodes/relation.h" diff --git a/expected/connection_validation_1.out b/expected/connection_validation_1.out deleted file mode 100644 index 2bd1251..0000000 --- a/expected/connection_validation_1.out +++ /dev/null @@ -1,71 +0,0 @@ -\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 127.0.0.10:9999 -UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 2; -ERROR: could not connect to 127.0.0.10:9999 -DELETE FROM f_mongo_test WHERE a = 2; -ERROR: could not connect to 127.0.0.10:9999 -SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -ERROR: could not connect to 127.0.0.10:9999 --- 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 localhost:27017 -UPDATE f_mongo_test SET b = 'RECORD UPDATED' WHERE a = 3; -ERROR: could not connect to localhost:27017 -DELETE FROM f_mongo_test WHERE a = 3; -ERROR: could not connect to localhost:27017 -SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -ERROR: could not connect to localhost:27017 --- 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; diff --git a/expected/pushdown_1.out b/expected/pushdown_1.out deleted file mode 100644 index 750701a..0000000 --- a/expected/pushdown_1.out +++ /dev/null @@ -1,964 +0,0 @@ -\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 ---------------------------------------------------------- - Sort - Output: c1, c2, c6, c8 - Sort Key: e.c1 NULLS FIRST - -> 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 -(7 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 --------------------------------------------------------- - Sort - Output: c1, c2, c6 - Sort Key: e.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 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 --------------------------------------------------------- - Sort - Output: c1, c2, c6, c8 - Sort Key: e.c1 DESC NULLS LAST - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6, c8 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 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 --------------------------------------------------------- - Sort - Output: c1, c2, c6, c8 - Sort Key: e.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c6, c8 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 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 --------------------------------------------------------- - Sort - Output: c1, c2, c5 - Sort Key: e.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 e - Output: c1, c2, c5 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 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 ---------------------------------------------------------- - Sort - Output: a - Sort Key: f_mongo_test.a NULLS FIRST - -> Foreign Scan on public.f_mongo_test - Output: a - Filter: ((f_mongo_test.a % 2) = 1) - Foreign Namespace: mongo_fdw_regress.mongo_test -(7 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 --------------------------------------------------------- - Sort - Output: c1, c2 - Sort Key: f_test_tbl1.c2 DESC NULLS LAST - -> Foreign Scan on public.f_test_tbl1 - Output: c1, c2 - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(6 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 --------------------------------------------------------- - Sort - Output: c1, c4 - Sort Key: f_test_tbl1.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 - Output: c1, c4 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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 - Filter: ((f_test_tbl1.c1 < f_test_tbl1.c4) AND (f_test_tbl1.c7 < f_test_tbl1.c8)) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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 --------------------------------------------------------- - Sort - Output: c1, c2 - Sort Key: f_test_tbl1.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 - Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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 --------------------------------------------------------- - Sort - Output: c1, c2 - Sort Key: f_test_tbl1.c1 NULLS FIRST - -> Foreign Scan on public.f_test_tbl1 - Output: c1, c2 - Filter: ((f_test_tbl1.c1 > 1000) > false) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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 --------------------------------------------------------- - Sort - Output: name, marks - Sort Key: f_test_tbl3.name DESC NULLS LAST - -> Foreign Scan on public.f_test_tbl3 - Output: name, marks - Filter: f_test_tbl3.pass - Foreign Namespace: mongo_fdw_regress.test_tbl3 -(7 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 - Filter: ((f_test_tbl2.c2)::text = (f_test_tbl2.c3)::text) - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(4 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 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 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 - -> 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; - _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 ORDER BY 1 USING OPERATOR(public.<^); - QUERY PLAN ---------------------------------------------------------------- - Sort - Output: (min(a)) - Sort Key: (min(f_mongo_test.a)) USING <^ - -> Aggregate - Output: min(a) - -> Foreign Scan on public.f_mongo_test - Output: _id, a, b - Foreign Namespace: mongo_fdw_regress.mongo_test -(8 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: invalid option "enable_order_by_pushdown" -HINT: Valid options in this context are: address, port, use_remote_estimate. -ALTER SERVER mongo_server OPTIONS (ADD enable_order_by_pushdown 'false'); -ERROR: invalid option "enable_order_by_pushdown" -HINT: Valid options in this context are: address, port, use_remote_estimate. -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 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 rows) - -ALTER SERVER mongo_server OPTIONS (SET enable_order_by_pushdown 'true'); -ERROR: option "enable_order_by_pushdown" not found -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 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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'); -ERROR: option "enable_order_by_pushdown" not found --- Test the option at table level. -ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (ADD enable_order_by_pushdown 'true'); -ERROR: invalid option "enable_order_by_pushdown" -HINT: Valid options in this context are: database, collection. -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 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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'); -ERROR: option "enable_order_by_pushdown" not found -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 - Filter: (f_test_tbl1.c1 > f_test_tbl1.c4) - Foreign Namespace: mongo_fdw_regress.test_tbl1 -(7 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'); -ERROR: option "enable_order_by_pushdown" not found -ALTER FOREIGN TABLE f_test_tbl1 OPTIONS (SET enable_order_by_pushdown 'true'); -ERROR: option "enable_order_by_pushdown" not found --- 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 - Filter: (NOT f_test_tbl3.pass) - Foreign Namespace: mongo_fdw_regress.test_tbl3 -(7 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 - Filter: f_test_tbl3.pass - Foreign Namespace: mongo_fdw_regress.test_tbl3 -(7 rows) - -SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name; - name | pass -------+------ - vdd | t -(1 row) - --- 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; diff --git a/expected/select_1.out b/expected/select_1.out deleted file mode 100644 index 4b6988b..0000000 --- a/expected/select_1.out +++ /dev/null @@ -1,1379 +0,0 @@ -\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 -------------------- - 50501 -(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'); -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"; -ERROR: full document retrival only available in MongoC meta driver -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"; -ERROR: full document retrival only available in MongoC meta driver -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"; -ERROR: full document retrival only available in MongoC meta driver -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"; -ERROR: full document retrival only available in MongoC meta driver --- 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 ---------------------------------------------------------------- - Sort - Output: t1.a, t1.b, t2.a, (t1.a) - Sort Key: t1.a NULLS FIRST - -> 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 -(12 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 ---------------- - 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 ----- - 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) - --- 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 USER MAPPING FOR public SERVER mongo_server; -DROP SERVER mongo_server; -DROP EXTENSION mongo_fdw; diff --git a/expected/server_options.out b/expected/server_options.out index fb9216d..53c8771 100644 --- a/expected/server_options.out +++ b/expected/server_options.out @@ -116,8 +116,6 @@ SELECT a, b FROM f_mongo_test ORDER BY 1, 2; (1 row) -- Alter server to add authentication_database option --- authentication_database options is not supported with legacy driver --- so below queries will fail when compiled with legacy driver. 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); diff --git a/expected/server_options_1.out b/expected/server_options_1.out deleted file mode 100644 index 62f3b9d..0000000 --- a/expected/server_options_1.out +++ /dev/null @@ -1,195 +0,0 @@ -\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: invalid option "ssl" -HINT: Valid options in this context are: address, port, use_remote_estimate. -ALTER SERVER mongo_server OPTIONS (ssl 'x'); -ERROR: invalid option "ssl" -HINT: Valid options in this context are: address, port, use_remote_estimate. --- 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'); -ERROR: invalid option "ssl" -HINT: Valid options in this context are: address, port, use_remote_estimate. --- 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; - a | b ----+----------------------- - 0 | mongo_test collection -(1 row) - -\set VERBOSITY default --- Switch back to 'false'. -ALTER SERVER mongo_server OPTIONS (SET ssl 'false'); -ERROR: option "ssl" not found --- 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 --- authentication_database options is not supported with legacy driver --- so below queries will fail when compiled with legacy driver. -ALTER SERVER mongo_server OPTIONS (ADD authentication_database 'NOT_EXIST_DB'); -ERROR: invalid option "authentication_database" -HINT: Valid options in this context are: address, port, use_remote_estimate. -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 localhost:27017 -HINT: Mongo driver connection error: --- Now changed to valid authentication_database so select query should work. -ALTER SERVER mongo_server - OPTIONS (SET authentication_database 'mongo_fdw_regress'); -ERROR: option "authentication_database" not found -SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -ERROR: could not connect to localhost:27017 -HINT: Mongo driver connection error: -ALTER SERVER mongo_server - OPTIONS (DROP authentication_database); -ERROR: option "authentication_database" not found -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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 185a959..c1fd598 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -70,7 +70,6 @@ PG_MODULE_MAGIC; */ #define CODE_VERSION 50501 -#ifdef META_DRIVER /* * Macro to check unsupported sorting methods. Currently, ASC NULLS FIRST and * DESC NULLS LAST give the same sorting result on MongoDB and Postgres. So, @@ -94,7 +93,6 @@ PG_MODULE_MAGIC; static bool enable_join_pushdown = true; static bool enable_order_by_pushdown = true; static bool enable_aggregate_pushdown = true; -#endif /* * This enum describes what's kept in the fdw_private list for a ForeignPath. @@ -180,7 +178,6 @@ static void mongoBeginForeignInsert(ModifyTableState *mtstate, ResultRelInfo *resultRelInfo); static void mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo); -#ifdef META_DRIVER static void mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, @@ -191,7 +188,6 @@ static void mongoGetForeignUpperPaths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *output_rel, void *extra); -#endif /* * Helper functions @@ -219,7 +215,6 @@ static int mongo_acquire_sample_rows(Relation relation, double *totalRowCount, double *totalDeadRowCount); static void mongo_fdw_exit(int code, Datum arg); -#ifdef META_DRIVER static bool mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, JoinType jointype, RelOptInfo *outerrel, RelOptInfo *innerrel, @@ -238,16 +233,9 @@ static void mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *final_rel, FinalPathExtraData *extra); #endif -#endif -#ifndef META_DRIVER -static const char *escape_json_string(const char *string); -static void bson_to_json_string(StringInfo output, BSON_ITERATOR iter, - bool isArray); -#endif static void mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, Oid foreigntableid); -#ifdef META_DRIVER static List *mongo_get_useful_ecs_for_relation(PlannerInfo *root, RelOptInfo *rel); static List *mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, @@ -265,7 +253,6 @@ static void mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel); #endif -#endif /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -289,7 +276,6 @@ JsonSemAction nullSemAction = void _PG_init(void) { -#ifdef META_DRIVER /* * Sometimes getting a join or sorted result from MongoDB server is slower * than performing those operations locally. To have that flexibility add @@ -330,7 +316,6 @@ _PG_init(void) /* Initialize MongoDB C driver */ mongoc_init(); -#endif on_proc_exit(&mongo_fdw_exit, PointerGetDatum(NULL)); } @@ -374,13 +359,11 @@ mongo_fdw_handler(PG_FUNCTION_ARGS) fdwRoutine->BeginForeignInsert = mongoBeginForeignInsert; fdwRoutine->EndForeignInsert = mongoEndForeignInsert; -#ifdef META_DRIVER /* Support function for join push-down */ fdwRoutine->GetForeignJoinPaths = mongoGetForeignJoinPaths; /* Support functions for upper relation push-down */ fdwRoutine->GetForeignUpperPaths = mongoGetForeignUpperPaths; -#endif PG_RETURN_POINTER(fdwRoutine); } @@ -393,10 +376,8 @@ static void mongo_fdw_exit(int code, Datum arg) { mongo_cleanup_connection(); -#ifdef META_DRIVER /* Release all memory and other resources allocated by the driver */ mongoc_cleanup(); -#endif } /* @@ -432,12 +413,7 @@ mongoGetForeignRelSize(PlannerInfo *root, { RestrictInfo *ri = (RestrictInfo *) lfirst(lc); -#ifndef META_DRIVER - if (IsA(ri->clause, OpExpr) && - mongo_is_foreign_expr(root, baserel, ri->clause, false)) -#else if (mongo_is_foreign_expr(root, baserel, ri->clause, false)) -#endif fpinfo->remote_conds = lappend(fpinfo->remote_conds, ri); else fpinfo->local_conds = lappend(fpinfo->local_conds, ri); @@ -499,8 +475,6 @@ mongoGetForeignRelSize(PlannerInfo *root, /* Also store the options in fpinfo for further use */ fpinfo->options = options; -#ifdef META_DRIVER - /* * Store aggregation enable/disable option in the fpinfo directly for * further use. This flag can be useful when options are not accessible @@ -510,7 +484,6 @@ mongoGetForeignRelSize(PlannerInfo *root, /* Set the flag is_order_by_pushable of the base relation */ fpinfo->is_order_by_pushable = options->enable_order_by_pushdown; -#endif } /* @@ -618,10 +591,8 @@ mongoGetForeignPaths(PlannerInfo *root, /* Add foreign path as the only possible path */ add_path(baserel, foreignPath); -#ifdef META_DRIVER /* Add paths with pathkeys */ mongo_add_paths_with_pathkeys(root, baserel, NULL, startupCost, totalCost); -#endif } /* @@ -654,7 +625,6 @@ mongoGetForeignPlan(PlannerInfo *root, List *is_inner_column_list = NIL; List *quals = NIL; MongoFdwRelType mongofdwreltype; -#ifdef META_DRIVER MongoRelQualInfo *qual_info; MongoFdwRelationInfo *ofpinfo; List *pathKeyList = NIL; @@ -675,8 +645,6 @@ mongoGetForeignPlan(PlannerInfo *root, FdwPathPrivateHasLimit)); } -#endif - /* Set scan relation id */ if (IS_SIMPLE_REL(foreignrel)) scan_relid = foreignrel->relid; @@ -868,8 +836,6 @@ mongoGetForeignPlan(PlannerInfo *root, else mongofdwreltype = BASE_REL; -#ifdef META_DRIVER - /* * We use MongoRelQualInfo to pass various information related to joining * quals and grouping target to fdw_private which is used to form @@ -987,15 +953,11 @@ mongoGetForeignPlan(PlannerInfo *root, */ mongo_prepare_qual_info(quals, qual_info); } -#else - quals = remote_exprs; -#endif /* * Check the ORDER BY clause, and if we found any useful pathkeys, then * store the required information. */ -#ifdef META_DRIVER foreach(lc, best_path->path.pathkeys) { EquivalenceMember *em; @@ -1075,7 +1037,6 @@ mongoGetForeignPlan(PlannerInfo *root, offset_value = DatumGetInt64(((Const *) node)->constvalue); } } -#endif /* * Unlike postgres_fdw, remote query formation is done in the execution @@ -1094,7 +1055,6 @@ mongoGetForeignPlan(PlannerInfo *root, /* Append relation type */ fdw_private = lappend(fdw_private, makeInteger(mongofdwreltype)); -#ifdef META_DRIVER fdw_private = lappend(fdw_private, qual_info->colNameList); fdw_private = lappend(fdw_private, qual_info->colNumList); fdw_private = lappend(fdw_private, qual_info->rtiList); @@ -1133,7 +1093,6 @@ mongoGetForeignPlan(PlannerInfo *root, fdw_private = lappend(fdw_private, makeInteger(tfpinfo->jointype)); } } -#endif /* Create the foreign scan node */ foreignScan = make_foreignscan(targetList, local_exprs, @@ -1699,7 +1658,6 @@ mongoExecForeignInsert(EState *estate, TupleDescAttr(slot->tts_tupleDescriptor, attnum - 1)->atttypid); } } - bsonFinish(bsonDoc); /* Now we are ready to insert tuple/document into MongoDB */ mongoInsert(fmstate->mongoConnection, fmstate->options->svr_database, @@ -1828,17 +1786,11 @@ mongoExecForeignUpdate(EState *estate, elog(ERROR, "system column '__doc' update is not supported"); value = slot_getattr(slot, attnum, &isnull); -#ifdef META_DRIVER append_mongo_value(&set, attr->attname.data, value, isnull ? true : false, attr->atttypid); -#else - append_mongo_value(document, attr->attname.data, value, - isnull ? true : false, attr->atttypid); -#endif } } bsonAppendFinishObject(document, &set); - bsonFinish(document); op = bsonCreate(); if (!append_mongo_value(op, columnName, datum, isNull, typoid)) @@ -1846,7 +1798,6 @@ mongoExecForeignUpdate(EState *estate, bsonDestroy(document); return NULL; } - bsonFinish(op); /* We are ready to update the row into MongoDB */ mongoUpdate(fmstate->mongoConnection, fmstate->options->svr_database, @@ -1902,7 +1853,6 @@ mongoExecForeignDelete(EState *estate, bsonDestroy(document); return NULL; } - bsonFinish(document); /* Now we are ready to delete a single document from MongoDB */ mongoDelete(fmstate->mongoConnection, fmstate->options->svr_database, @@ -2315,10 +2265,8 @@ column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || bsonType == BSON_TYPE_DOUBLE) compatibleTypes = true; -#ifdef META_DRIVER if (bsonType == BSON_TYPE_BOOL) compatibleTypes = true; -#endif break; case BOOLOID: if (bsonType == BSON_TYPE_INT32 || bsonType == BSON_TYPE_INT64 || @@ -2334,10 +2282,8 @@ column_types_compatible(BSON_TYPE bsonType, Oid columnTypeId) case BYTEAOID: if (bsonType == BSON_TYPE_BINDATA) compatibleTypes = true; -#ifdef META_DRIVER if (bsonType == BSON_TYPE_OID) compatibleTypes = true; -#endif break; case NAMEOID: @@ -2573,7 +2519,7 @@ column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, int value_len; char *value; bytea *result; -#ifdef META_DRIVER + switch (bsonIterType(bsonIterator)) { case BSON_TYPE_OID: @@ -2585,10 +2531,6 @@ column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, (uint32_t *) &value_len); break; } -#else - value_len = bsonIterBinLen(bsonIterator); - value = (char *) bsonIterBinData(bsonIterator); -#endif result = (bytea *) palloc(value_len + VARHDRSZ); memcpy(VARDATA(result), value, value_len); SET_VARSIZE(result, value_len + VARHDRSZ); @@ -2627,15 +2569,9 @@ column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, ereport(ERROR, (errmsg("cannot convert to json"))); -#ifdef META_DRIVER /* Convert BSON to JSON value */ bsonToJsonStringValue(buffer, bsonIterator, BSON_TYPE_ARRAY == type); -#else - /* Convert BSON to JSON value */ - bson_to_json_string(buffer, *bsonIterator, - BSON_TYPE_ARRAY == type); -#endif result = cstring_to_text_with_len(buffer->data, buffer->len); lex = makeJsonLexContext(result, false); pg_parse_json(lex, &nullSemAction); @@ -2653,189 +2589,6 @@ column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, return columnValue; } -#ifndef META_DRIVER -static void -bson_to_json_string(StringInfo output, BSON_ITERATOR i, bool isArray) -{ - const char *key; - bool isFirstElement; - char beginSymbol = '{'; - char endSymbol = '}'; - BSON_TYPE bsonType; - - if (isArray) - { - beginSymbol = '['; - endSymbol = ']'; - } - - appendStringInfoChar(output, beginSymbol); - - isFirstElement = true; - while (bsonIterNext(&i)) - { - if (!isFirstElement) - appendStringInfoChar(output, ','); - - bsonType = bsonIterType(&i); - if (bsonType == 0) - break; - - key = bsonIterKey(&i); - - if (!isArray) - appendStringInfo(output, "\"%s\":", key); - - switch (bsonType) - { - case BSON_TYPE_DOUBLE: - appendStringInfo(output, "%f", bsonIterDouble(&i)); - break; - case BSON_TYPE_UTF8: - appendStringInfo(output, "\"%s\"", - escape_json_string(bsonIterString(&i))); - break; - case BSON_TYPE_SYMBOL: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("\"symbol\" BSON type is deprecated and unsupported"), - errhint("Symbol: %s", bsonIterString(&i)))); - break; - case BSON_TYPE_OID: - { - char oidhex[25]; - - bsonOidToString(bsonIterOid(&i), oidhex); - appendStringInfo(output, "{\"$oid\":\"%s\"}", oidhex); - break; - } - case BSON_TYPE_BOOL: - appendStringInfoString(output, - bsonIterBool(&i) ? "true" : "false"); - break; - case BSON_TYPE_DATE_TIME: - appendStringInfo(output, "{\"$date\":%ld}", - (long int) bsonIterDate(&i)); - break; - case BSON_TYPE_BINDATA: - /* It's possible to encode the data with base64 here. */ - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for \"binary data\" BSON type is not implemented"))); - break; - case BSON_TYPE_UNDEFINED: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("\"undefined\" BSON type is deprecated and unsupported"))); - break; - case BSON_TYPE_NULL: - appendStringInfoString(output, "null"); - break; - case BSON_TYPE_REGEX: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for \"regex\" BSON type is not implemented"), - errhint("Regex: %s", bsonIterRegex(&i)))); - break; - case BSON_TYPE_CODE: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for \"code\" BSON type is not implemented"), - errhint("Code: %s", bsonIterCode(&i)))); - break; - case BSON_TYPE_CODEWSCOPE: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("support for \"code\" with scope` BSON type is not implemented"))); - break; - case BSON_TYPE_INT32: - appendStringInfo(output, "%d", bsonIterInt32(&i)); - break; - case BSON_TYPE_INT64: - appendStringInfo(output, "%lu", (uint64_t) bsonIterInt64(&i)); - break; - case BSON_TYPE_TIMESTAMP: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("internal `timestamp` BSON type is and unsupported"))); - break; - case BSON_TYPE_DOCUMENT: - bson_to_json_string(output, i, false); - break; - case BSON_TYPE_ARRAY: - bson_to_json_string(output, i, true); - break; - default: - ereport(ERROR, - (errcode(ERRCODE_FDW_INVALID_DATA_TYPE), - errmsg("unsupported BSON type: %x", bsonType))); - } - isFirstElement = false; - } - appendStringInfoChar(output, endSymbol); -} - -/* - * escape_json_string - * Escapes a string for safe inclusion in JSON. - */ -static const char * -escape_json_string(const char *string) -{ - StringInfo buffer; - const char *ptr; - int i; - int segmentStartIdx; - int len; - bool needsEscaping = false; - - for (ptr = string; *ptr; ++ptr) - { - if (*ptr == '"' || *ptr == '\r' || *ptr == '\n' || *ptr == '\t' || - *ptr == '\\') - { - needsEscaping = true; - break; - } - } - - if (!needsEscaping) - return string; - - buffer = makeStringInfo(); - len = strlen(string); - segmentStartIdx = 0; - for (i = 0; i < len; ++i) - { - if (string[i] == '"' || string[i] == '\r' || string[i] == '\n' || - string[i] == '\t' || string[i] == '\\') - { - if (segmentStartIdx < i) - appendBinaryStringInfo(buffer, string + segmentStartIdx, - i - segmentStartIdx); - - appendStringInfoChar(buffer, '\\'); - if (string[i] == '"') - appendStringInfoChar(buffer, '"'); - else if (string[i] == '\r') - appendStringInfoChar(buffer, 'r'); - else if (string[i] == '\n') - appendStringInfoChar(buffer, 'n'); - else if (string[i] == '\t') - appendStringInfoChar(buffer, 't'); - else if (string[i] == '\\') - appendStringInfoChar(buffer, '\\'); - - segmentStartIdx = i + 1; - } - } - if (segmentStartIdx < len) - appendBinaryStringInfo(buffer, string + segmentStartIdx, - len - segmentStartIdx); - return buffer->data; -} -#endif - /* * mongo_free_scan_state * Closes the cursor and connection to MongoDB, and reclaims all Mongo @@ -2986,19 +2739,6 @@ mongo_acquire_sample_rows(Relation relation, */ mongoConnection = mongo_get_connection(server, user, options); - if (!bsonFinish(queryDocument)) - { -#ifdef META_DRIVER - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); -#else - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON error: %d", queryDocument->err))); -#endif - } - /* Create cursor for collection name and set query */ mongoCursor = mongoCursorCreate(mongoConnection, options->svr_database, options->collectionName, queryDocument); @@ -3044,22 +2784,12 @@ mongo_acquire_sample_rows(Relation relation, } else { -#ifdef META_DRIVER 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))); -#else - mongo_cursor_error_t errorCode = mongoCursor->err; - - if (errorCode != MONGO_CURSOR_EXHAUSTED) - ereport(ERROR, - (errmsg("could not iterate over mongo collection"), - errhint("Mongo driver cursor error code: %d", - errorCode))); -#endif break; } @@ -3165,7 +2895,6 @@ mongoEndForeignInsert(EState *estate, ResultRelInfo *resultRelInfo) errmsg("COPY and foreign partition routing not supported in mongo_fdw"))); } -#ifdef META_DRIVER /* * mongoGetForeignJoinPaths * Add possible ForeignPath to joinrel, if the join is safe to push down. @@ -3911,7 +3640,6 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, /* Add generated path into grouped_rel by add_path(). */ add_path(grouped_rel, (Path *) grouppath); } -#endif /* End of META_DRIVER */ /* * mongoEstimateCosts @@ -3936,7 +3664,6 @@ mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, *total_cost = baserel->rows + *startup_cost; } -#ifdef META_DRIVER /* * mongo_get_useful_ecs_for_relation * Determine which EquivalenceClasses might be involved in useful @@ -4793,4 +4520,3 @@ mongo_is_default_sort_operator(EquivalenceMember *em, PathKey *pathkey) return false; } -#endif /* End of META_DRIVER */ diff --git a/mongo_fdw.h b/mongo_fdw.h index 5bcb220..205834f 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -15,14 +15,9 @@ #ifndef MONGO_FDW_H #define MONGO_FDW_H -#include "config.h" #include "mongo_wrapper.h" -#ifdef META_DRIVER #include "mongoc.h" -#else -#include "mongo.h" -#endif #include "access/reloptions.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" @@ -53,7 +48,6 @@ #include "utils/memutils.h" #include "utils/timestamp.h" -#ifdef META_DRIVER #define BSON bson_t #define BSON_TYPE bson_type_t #define BSON_ITERATOR bson_iter_t @@ -97,45 +91,6 @@ #define BSON_ITER_NEXT bson_iter_next #define BSON_ITER_TYPE bson_iter_type #define BSON_ITER_BINARY bson_iter_binary -#else -#define BSON bson -#define BSON_TYPE bson_type -#define BSON_ITERATOR bson_iterator -#define MONGO_CONN mongo -#define MONGO_CURSOR mongo_cursor -#define BSON_TYPE_DOCUMENT BSON_OBJECT -#define BSON_TYPE_NULL BSON_NULL -#define BSON_TYPE_ARRAY BSON_ARRAY -#define BSON_TYPE_INT32 BSON_INT -#define BSON_TYPE_INT64 BSON_LONG -#define BSON_TYPE_DOUBLE BSON_DOUBLE -#define BSON_TYPE_BINDATA BSON_BINDATA -#define BSON_TYPE_BOOL BSON_BOOL -#define BSON_TYPE_UTF8 BSON_STRING -#define BSON_TYPE_OID BSON_OID -#define BSON_TYPE_DATE_TIME BSON_DATE -#define BSON_TYPE_SYMBOL BSON_SYMBOL -#define BSON_TYPE_UNDEFINED BSON_UNDEFINED -#define BSON_TYPE_REGEX BSON_REGEX -#define BSON_TYPE_CODE BSON_CODE -#define BSON_TYPE_CODEWSCOPE BSON_CODEWSCOPE -#define BSON_TYPE_TIMESTAMP BSON_TIMESTAMP - -#define BSON_ITER_BOOL bson_iterator_bool -#define BSON_ITER_DOUBLE bson_iterator_double -#define BSON_ITER_INT32 bson_iterator_int -#define BSON_ITER_INT64 bson_iterator_long -#define BSON_ITER_OID bson_iterator_oid -#define BSON_ITER_UTF8 bson_iterator_string -#define BSON_ITER_REGEX bson_iterator_regex -#define BSON_ITER_DATE_TIME bson_iterator_date -#define BSON_ITER_CODE bson_iterator_code -#define BSON_ITER_VALUE bson_iterator_value -#define BSON_ITER_KEY bson_iterator_key -#define BSON_ITER_NEXT bson_iterator_next -#define BSON_ITER_TYPE bson_iterator_type -#define BSON_ITER_BINARY bson_iterator_bin_data -#endif /* Defines for valid option names */ #define OPTION_NAME_ADDRESS "address" @@ -145,7 +100,6 @@ #define OPTION_NAME_USERNAME "username" #define OPTION_NAME_PASSWORD "password" #define OPTION_NAME_USE_REMOTE_ESTIMATE "use_remote_estimate" -#ifdef META_DRIVER #define OPTION_NAME_READ_PREFERENCE "read_preference" #define OPTION_NAME_AUTHENTICATION_DATABASE "authentication_database" #define OPTION_NAME_REPLICA_SET "replica_set" @@ -159,7 +113,6 @@ #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" -#endif /* Default values for option parameters */ #define DEFAULT_IP_ADDRESS "127.0.0.1" @@ -203,19 +156,13 @@ typedef struct MongoValidOption } MongoValidOption; /* Array of options that are valid for mongo_fdw */ -#ifdef META_DRIVER static const uint32 ValidOptionCount = 23; -#else -static const uint32 ValidOptionCount = 7; -#endif static const MongoValidOption ValidOptionArray[] = { /* Foreign server options */ {OPTION_NAME_ADDRESS, ForeignServerRelationId}, {OPTION_NAME_PORT, ForeignServerRelationId}, {OPTION_NAME_USE_REMOTE_ESTIMATE, ForeignServerRelationId}, - -#ifdef META_DRIVER {OPTION_NAME_READ_PREFERENCE, ForeignServerRelationId}, {OPTION_NAME_AUTHENTICATION_DATABASE, ForeignServerRelationId}, {OPTION_NAME_REPLICA_SET, ForeignServerRelationId}, @@ -229,16 +176,13 @@ static const MongoValidOption ValidOptionArray[] = {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignServerRelationId}, {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignServerRelationId}, {OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignServerRelationId}, -#endif /* Foreign table options */ {OPTION_NAME_DATABASE, ForeignTableRelationId}, {OPTION_NAME_COLLECTION, ForeignTableRelationId}, -#ifdef META_DRIVER {OPTION_NAME_ENABLE_JOIN_PUSHDOWN, ForeignTableRelationId}, {OPTION_NAME_ENABLE_AGGREGATE_PUSHDOWN, ForeignTableRelationId}, {OPTION_NAME_ENABLE_ORDER_BY_PUSHDOWN, ForeignTableRelationId}, -#endif /* User mapping options */ {OPTION_NAME_USERNAME, UserMappingRelationId}, @@ -259,7 +203,6 @@ typedef struct MongoFdwOptions char *svr_username; char *svr_password; bool use_remote_estimate; /* use remote estimate for rows */ -#ifdef META_DRIVER char *readPreference; char *authenticationDatabase; char *replicaSet; @@ -273,7 +216,6 @@ typedef struct MongoFdwOptions bool enable_join_pushdown; bool enable_aggregate_pushdown; bool enable_order_by_pushdown; -#endif } MongoFdwOptions; /* diff --git a/mongo_query.c b/mongo_query.c index c901b12..361c3e6 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -31,11 +31,7 @@ #if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" #endif -#ifdef META_DRIVER #include "mongoc.h" -#else -#include "mongo.h" -#endif #include "mongo_query.h" #if PG_VERSION_NUM < 120000 #include "nodes/relation.h" @@ -55,10 +51,6 @@ typedef struct foreign_glob_cxt { PlannerInfo *root; /* global planner state */ RelOptInfo *foreignrel; /* the foreign relation we are planning for */ -#ifndef META_DRIVER - unsigned short varcount; /* Var count */ - unsigned short opexprcount; -#endif Relids relids; /* relids of base relations in the underlying * scan */ bool is_having_cond; /* "true" for HAVING clause condition */ @@ -82,61 +74,22 @@ typedef struct foreign_loc_cxt } foreign_loc_cxt; /* Local functions forward declarations */ -#ifndef META_DRIVER -static Expr *find_argument_of_type(List *argumentList, NodeTag argumentType); -static List *equality_operator_list(List *operatorList); -static List *unique_column_list(List *operatorList); -static List *column_operator_list(Var *column, List *operatorList); -#endif 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); -#ifdef META_DRIVER 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); -#endif #if PG_VERSION_NUM >= 160000 static List *mongo_append_unique_var(List *varlist, Var *var); #endif -#ifndef META_DRIVER -/* - * find_argument_of_type - * Walks over the given argument list, looks for an argument with the - * given type, and returns the argument if it is found. - */ -static Expr * -find_argument_of_type(List *argumentList, NodeTag argumentType) -{ - Expr *foundArgument = NULL; - ListCell *argumentCell; - - foreach(argumentCell, argumentList) - { - Expr *argument = (Expr *) lfirst(argumentCell); - - /* For RelabelType type, examine the inner node */ - if (IsA(argument, RelabelType)) - argument = ((RelabelType *) argument)->arg; - - if (nodeTag(argument) == argumentType) - { - foundArgument = argument; - break; - } - } - - return foundArgument; -} -#endif - /* * mongo_query_document * Takes in the applicable operator expressions for relation, the join @@ -251,7 +204,6 @@ mongo_query_document(ForeignScanState *scanStateNode) List *PrivateList = fsplan->fdw_private; List *opExpressionList = list_nth(PrivateList, mongoFdwPrivateRemoteExprList); -#ifdef META_DRIVER MongoFdwModifyState *fmstate = (MongoFdwModifyState *) scanStateNode->fdw_state; BSON root_pipeline; BSON match_stage; @@ -308,7 +260,6 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Prepare array of stages */ bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); -#endif /* * Add filter into query pipeline if available. These are remote_exprs @@ -317,7 +268,6 @@ mongo_query_document(ForeignScanState *scanStateNode) */ if (opExpressionList) { -#ifdef META_DRIVER pipeline_cxt context; context.colInfoHash = columnInfoHash; @@ -331,129 +281,8 @@ mongo_query_document(ForeignScanState *scanStateNode) mongo_prepare_pipeline(opExpressionList, &match_stage, &context); bsonAppendFinishArray(filter, &match_stage); -#else - Oid relationId; - List *equalityOperatorList; - List *comparisonOperatorList; - List *columnList; - ListCell *equalityOperatorCell; - ListCell *columnCell; - - if (fsplan->scan.scanrelid > 0) - relationId = RelationGetRelid(scanStateNode->ss.ss_currentRelation); - else - relationId = 0; - - /* - * We distinguish between equality expressions and others since we - * need to insert the latter (<, >, <=, >=, <>) as separate - * sub-documents into the BSON query object. - */ - equalityOperatorList = equality_operator_list(opExpressionList); - comparisonOperatorList = list_difference(opExpressionList, - equalityOperatorList); - - /* Append equality expressions to the query */ - foreach(equalityOperatorCell, equalityOperatorList) - { - OpExpr *equalityOperator; - Oid columnId = InvalidOid; - char *columnName; - Const *constant; - Param *paramNode; - List *argumentList; - Var *column; - - equalityOperator = (OpExpr *) lfirst(equalityOperatorCell); - argumentList = equalityOperator->args; - column = (Var *) find_argument_of_type(argumentList, T_Var); - constant = (Const *) find_argument_of_type(argumentList, T_Const); - paramNode = (Param *) find_argument_of_type(argumentList, T_Param); - - columnId = column->varattno; - columnName = get_attname(relationId, columnId, false); - - if (constant != NULL) - append_constant_value(filter, columnName, constant); - else - append_param_value(filter, columnName, paramNode, - scanStateNode); - } - - /* - * For comparison expressions, we need to group them by their columns - * and then append all expressions that correspond to a column as one - * sub-document. Otherwise, even when we have two expressions to - * define the upper and lower bound of a range, Mongo uses only one of - * these expressions during an index search. - */ - columnList = unique_column_list(comparisonOperatorList); - - /* Append comparison expressions, grouped by columns, to the query */ - foreach(columnCell, columnList) - { - Var *column = (Var *) lfirst(columnCell); - Oid columnId = InvalidOid; - char *columnName; - List *columnOperatorList; - ListCell *columnOperatorCell; - BSON childDocument; - - if (relationId != 0) - { - columnId = column->varattno; - columnName = get_attname(relationId, columnId, false); - } - - /* Find all expressions that correspond to the column */ - columnOperatorList = column_operator_list(column, - comparisonOperatorList); - - /* For comparison expressions, start a sub-document */ - bsonAppendStartObject(filter, columnName, &childDocument); - - foreach(columnOperatorCell, columnOperatorList) - { - OpExpr *columnOperator; - char *operatorName; - char *mongoOperatorName; - List *argumentList; - Const *constant; - Param *paramNode; - - columnOperator = (OpExpr *) lfirst(columnOperatorCell); - argumentList = columnOperator->args; - constant = (Const *) find_argument_of_type(argumentList, - T_Const); - paramNode = (Param *) find_argument_of_type(argumentList, - T_Param); - operatorName = get_opname(columnOperator->opno); - mongoOperatorName = mongo_operator_name(operatorName); - - if (constant != NULL) - append_constant_value(filter, mongoOperatorName, constant); - else - append_param_value(filter, mongoOperatorName, paramNode, - scanStateNode); - } - bsonAppendFinishObject(filter, &childDocument); - } -#endif - } - if (!bsonFinish(filter)) - { -#ifdef META_DRIVER - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); -#else - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON error: %d", queryDocument->err))); -#endif } -#ifdef META_DRIVER if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { BSON inner_pipeline; @@ -706,11 +535,6 @@ mongo_query_document(ForeignScanState *scanStateNode) mongo_prepare_pipeline(having_expr, &match_stage, &context); bsonAppendFinishObject(&root_pipeline, &match_stage); - - if (!bsonFinish(filter)) - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); } } @@ -812,17 +636,7 @@ mongo_query_document(ForeignScanState *scanStateNode) bsonAppendFinishArray(queryDocument, &root_pipeline); - if (!bsonFinish(queryDocument)) - { - ereport(ERROR, - (errmsg("could not create document for query"), - errhint("BSON flags: %d", queryDocument->flags))); - } - return queryDocument; -#endif - - return filter; } /* @@ -865,88 +679,6 @@ mongo_operator_name(const char *operatorName) return (char *) mongoOperatorName; } -#ifndef META_DRIVER -/* - * equality_operator_list - * Finds the equality (=) operators in the given list, and returns these - * operators in a new list. - */ -static List * -equality_operator_list(List *operatorList) -{ - List *equalityOperatorList = NIL; - ListCell *operatorCell; - - foreach(operatorCell, operatorList) - { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - - if (strncmp(get_opname(operator->opno), EQUALITY_OPERATOR_NAME, - NAMEDATALEN) == 0) - equalityOperatorList = lappend(equalityOperatorList, operator); - } - - return equalityOperatorList; -} - -/* - * unique_column_list - * Walks over the given operator list, and extracts the column argument in - * each operator. - * - * The function then de-duplicates extracted columns, and returns them in a new - * list. - */ -static List * -unique_column_list(List *operatorList) -{ - List *uniqueColumnList = NIL; - ListCell *operatorCell; - - foreach(operatorCell, operatorList) - { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - List *argumentList = operator->args; - Var *column = (Var *) find_argument_of_type(argumentList, - T_Var); - -#if PG_VERSION_NUM >= 160000 - uniqueColumnList = mongo_append_unique_var(uniqueColumnList, column); -#else - /* List membership is determined via column's equal() function */ - uniqueColumnList = list_append_unique(uniqueColumnList, column); -#endif - } - - return uniqueColumnList; -} - -/* - * column_operator_list - * Finds all expressions that correspond to the given column, and returns - * them in a new list. - */ -static List * -column_operator_list(Var *column, List *operatorList) -{ - List *columnOperatorList = NIL; - ListCell *operatorCell; - - foreach(operatorCell, operatorList) - { - OpExpr *operator = (OpExpr *) lfirst(operatorCell); - List *argumentList = operator->args; - Var *foundColumn = (Var *) find_argument_of_type(argumentList, - T_Var); - - if (equal(column, foundColumn)) - columnOperatorList = lappend(columnOperatorList, operator); - } - - return columnOperatorList; -} -#endif - void append_param_value(BSON *queryDocument, const char *keyName, Param *paramNode, ForeignScanState *scanStateNode) @@ -1088,7 +820,6 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, len = VARSIZE_4B(result) - VARHDRSZ; data = VARDATA_4B(result); } -#ifdef META_DRIVER if (strcmp(keyName, "_id") == 0) { bson_oid_t oid; @@ -1099,9 +830,6 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, else status = bsonAppendBinary(queryDocument, keyName, data, len); -#else - status = bsonAppendBinary(queryDocument, keyName, data, len); -#endif } break; case NAMEOID: @@ -1173,13 +901,8 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, valueDatum = DirectFunctionCall1(numeric_float8, elem_values[i]); valueFloat = DatumGetFloat8(valueDatum); -#ifdef META_DRIVER status = bsonAppendDouble(&childDocument, keyName, valueFloat); -#else - status = bsonAppendDouble(queryDocument, keyName, - valueFloat); -#endif } bsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); @@ -1220,13 +943,8 @@ append_mongo_value(BSON *queryDocument, const char *keyName, Datum value, &typeVarLength); valueString = OidOutputFunctionCall(outputFunctionId, elem_values[i]); -#ifdef META_DRIVER status = bsonAppendUTF8(&childDocument, keyName, valueString); -#else - status = bsonAppendUTF8(queryDocument, keyName, - valueString); -#endif } bsonAppendFinishArray(queryDocument, &childDocument); pfree(elem_values); @@ -1426,11 +1144,6 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, { Var *var = (Var *) node; -#ifndef META_DRIVER - /* Increment the Var count */ - glob_cxt->varcount++; -#endif - /* * If the Var is from the foreign table, we consider its * collation (if any) safe to use. If it is from another @@ -1514,11 +1227,6 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, !glob_cxt->is_having_cond) return false; -#ifndef META_DRIVER - /* Increment the operator expression count */ - glob_cxt->opexprcount++; -#endif - /* * We support =, <, >, <=, >=, <>, +, -, *, /, %, ^, |/, and @ * operators for joinclause of join relation. @@ -1534,11 +1242,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, * conditions of simple as well as join relation. */ if (!foreign_expr_walker((Node *) oe->args, glob_cxt, - &inner_cxt) -#ifndef META_DRIVER - || (glob_cxt->opexprcount > 1) -#endif - ) + &inner_cxt)) return false; /* @@ -1607,11 +1311,7 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, foreach(lc, l) { if ((!foreign_expr_walker((Node *) lfirst(lc), - glob_cxt, &inner_cxt)) -#ifndef META_DRIVER - || (glob_cxt->varcount > 1) -#endif - ) + glob_cxt, &inner_cxt))) return false; } @@ -1639,7 +1339,6 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, state = FDW_COLLATE_NONE; } break; -#ifdef META_DRIVER case T_Aggref: { Aggref *agg = (Aggref *) node; @@ -1727,7 +1426,6 @@ foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt, state = FDW_COLLATE_UNSAFE; } break; -#endif default: /* @@ -1816,10 +1514,6 @@ mongo_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expression, else glob_cxt.relids = baserel->relids; -#ifndef META_DRIVER - glob_cxt.varcount = 0; - glob_cxt.opexprcount = 0; -#endif glob_cxt.is_having_cond = is_having_cond; loc_cxt.collation = InvalidOid; loc_cxt.state = FDW_COLLATE_NONE; @@ -1901,7 +1595,6 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) return tlist; } -#ifdef META_DRIVER /* * column_info_hash * Creates a hash table that maps varno and varattno to the column names, @@ -2102,7 +1795,6 @@ mongo_is_foreign_param(PlannerInfo *root, RelOptInfo *baserel, Expr *expr) } return false; } -#endif #if PG_VERSION_NUM >= 160000 /* diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 6c8aa87..36d16ec 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -14,167 +14,332 @@ */ #include "postgres.h" -#ifdef META_DRIVER -#include "mongoc.h" -#else -#include "mongo.h" -#endif +#include #include "mongo_wrapper.h" -#define QUAL_STRING_LEN 512 +#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 *conn; - - conn = mongo_alloc(); - mongo_init(conn); + MONGO_CONN *client; + char *uri; - if (mongo_connect(conn, opt->svr_address, - (int32) opt->svr_port) != MONGO_OK) - { - int err = conn->err; - - mongo_destroy(conn); - mongo_dealloc(conn); - ereport(ERROR, - (errmsg("could not connect to %s:%hu", opt->svr_address, - opt->svr_port), - errhint("Mongo driver connection error: %d.", err))); - } if (opt->svr_username && opt->svr_password) { - if (mongo_cmd_authenticate(conn, opt->svr_database, opt->svr_username, - opt->svr_password) != MONGO_OK) + if (opt->authenticationDatabase) { - char *str = pstrdup(conn->errstr); - - mongo_destroy(conn); - mongo_dealloc(conn); - ereport(ERROR, - (errmsg("could not connect to %s:%hu", opt->svr_address, - opt->svr_port), - errhint("Mongo driver connection error: %s", str))); + 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"); - return conn; + + 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) { - mongo_destroy(conn); - mongo_dealloc(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) +mongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b) { - char qual[QUAL_STRING_LEN]; + mongoc_collection_t *c; + bson_error_t error; + bool r = false; - snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); - if (mongo_insert(conn, qual, b, NULL) != MONGO_OK) + 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 driver insert error: %d.", conn->err))); + 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) { - char qual[QUAL_STRING_LEN]; + mongoc_collection_t *c; + bson_error_t error; + bool r = false; - snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); - if (mongo_update(conn, qual, b, op, MONGO_UPDATE_BASIC, 0) != MONGO_OK) + 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 driver update error: %d.", conn->err))); + errhint("Mongo error: \"%s\"", error.message))); return true; } +/* + * mongoDelete + * Delete MongoDB's document. + */ bool mongoDelete(MONGO_CONN *conn, char *database, char *collection, BSON *b) { - char qual[QUAL_STRING_LEN]; + mongoc_collection_t *c; + bson_error_t error; + bool r = false; + + c = mongoc_client_get_collection(conn, database, collection); - snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); - if (mongo_remove(conn, qual, b, NULL) != MONGO_OK) + 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 driver delete error: %d.", conn->err))); + 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) { - MONGO_CURSOR *c; - char qual[QUAL_STRING_LEN]; + mongoc_collection_t *c; + MONGO_CURSOR *cur; + bson_error_t error; - snprintf(qual, QUAL_STRING_LEN, "%s.%s", database, collection); - c = mongo_cursor_alloc(); - mongo_cursor_init(c, conn, qual); - mongo_cursor_set_query(c, q); + 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))); - return c; + mongoc_collection_destroy(c); + + return cur; } -const bson * -mongoCursorBson(MONGO_CURSOR *c) +/* + * mongoCursorDestroy + * Destroy cursor created by calling mongoCursorCreate function. + */ +void +mongoCursorDestroy(MONGO_CURSOR *c) { - return mongo_cursor_bson(c); + mongoc_cursor_destroy(c); } -bool -mongoCursorNext(MONGO_CURSOR *c, BSON *b) + +/* + * mongoCursorBson + * Get the current document from cursor. + */ +const BSON * +mongoCursorBson(MONGO_CURSOR *c) { - return (mongo_cursor_next(c) == MONGO_OK); + return mongoc_cursor_current(c); } -void -mongoCursorDestroy(MONGO_CURSOR *c) +/* + * mongoCursorNext + * Get the next document from the cursor. + */ +bool +mongoCursorNext(MONGO_CURSOR *c, BSON *b) { - mongo_cursor_destroy(c); - mongo_cursor_dealloc(c); + 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() +bsonCreate(void) { - BSON *doc = NULL; + BSON *doc; - doc = bson_alloc(); + doc = bson_new(); bson_init(doc); return doc; } +/* + * bsonDestroy + * Destroy Bson object created by bsonCreate function. + */ void bsonDestroy(BSON *b) { bson_destroy(b); - bson_dealloc(b); } +/* + * bsonIterInit + * Initialize the bson Iterator. + */ bool bsonIterInit(BSON_ITERATOR *it, BSON *b) { - bson_iterator_init(it, b); - - return true; + return bson_iter_init(it, b); } bool bsonIterSubObject(BSON_ITERATOR *it, BSON *b) { - bson_iterator_subobject_init(it, b, 0); + const uint8_t *buffer; + uint32_t len; + + bson_iter_document(it, &len, &buffer); + bson_init_static(b, buffer, len); return true; } @@ -182,11 +347,14 @@ bsonIterSubObject(BSON_ITERATOR *it, BSON *b) int32_t bsonIterInt32(BSON_ITERATOR *it) { - switch (bson_iterator_type(it)) + BSON_ASSERT(it); + switch ((int) ITER_TYPE(it)) { - case BSON_DOUBLE: + case BSON_TYPE_BOOL: + return (int32) bson_iter_bool(it); + case BSON_TYPE_DOUBLE: { - double val = bson_iterator_double_raw(it); + double val = bson_iter_double(it); if (val < PG_INT32_MIN || val > PG_INT32_MAX) ereport(ERROR, @@ -196,9 +364,9 @@ bsonIterInt32(BSON_ITERATOR *it) return (int32) val; } - case BSON_LONG: + case BSON_TYPE_INT64: { - int64 val = bson_iterator_long_raw(it); + int64 val = bson_iter_int64(it); if (val < PG_INT32_MIN || val > PG_INT32_MAX) ereport(ERROR, @@ -208,8 +376,8 @@ bsonIterInt32(BSON_ITERATOR *it) return (int32) val; } - case BSON_INT: - return bson_iterator_int_raw(it); + case BSON_TYPE_INT32: + return bson_iter_int32(it); default: return 0; } @@ -218,170 +386,166 @@ bsonIterInt32(BSON_ITERATOR *it) int64_t bsonIterInt64(BSON_ITERATOR *it) { - return bson_iterator_long(it); + return bson_iter_as_int64(it); } double bsonIterDouble(BSON_ITERATOR *it) { - return bson_iterator_double(it); + return bson_iter_as_double(it); } bool bsonIterBool(BSON_ITERATOR *it) { - return bson_iterator_bool(it); + return bson_iter_as_bool(it); } const char * bsonIterString(BSON_ITERATOR *it) { - return bson_iterator_string(it); + uint32_t len = 0; + + return bson_iter_utf8(it, &len); } const char * -bsonIterBinData(BSON_ITERATOR *it) +bsonIterBinData(BSON_ITERATOR *it, uint32_t *len) { - return bson_iterator_bin_data(it); -} + const uint8_t *binary = NULL; + bson_subtype_t subtype = BSON_SUBTYPE_BINARY; -int -bsonIterBinLen(BSON_ITERATOR *it) -{ - return bson_iterator_bin_len(it); + bson_iter_binary(it, &subtype, len, &binary); + + return (char *) binary; } -bson_oid_t * +const bson_oid_t * bsonIterOid(BSON_ITERATOR *it) { - return bson_iterator_oid(it); + return bson_iter_oid(it); } time_t bsonIterDate(BSON_ITERATOR *it) { - return bson_iterator_date(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_iterator_type(it); + return bson_iter_type(it); } int bsonIterNext(BSON_ITERATOR *it) { - return bson_iterator_next(it); + return bson_iter_next(it); } bool bsonIterSubIter(BSON_ITERATOR *it, BSON_ITERATOR *sub) { - bson_iterator_subiterator(it, sub); - - return true; + return bson_iter_recurse(it, sub); } void bsonOidFromString(bson_oid_t *o, char *str) { - bson_oid_from_string(o, 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, v) == MONGO_OK); + return bson_append_oid(b, key, strlen(key), v); } bool bsonAppendBool(BSON *b, const char *key, bool v) { - return (bson_append_int(b, key, v) == MONGO_OK); + return bson_append_bool(b, key, -1, v); } bool -bsonAppendNull(BSON *b, const char *key) +bsonAppendStartObject(BSON *b, char *key, BSON *r) { - return (bson_append_null(b, key) == MONGO_OK); + return bson_append_document_begin(b, key, strlen(key), r); } bool -bsonAppendInt32(BSON *b, const char *key, int v) +bsonAppendFinishObject(BSON *b, BSON *r) { - return (bson_append_int(b, key, v) == MONGO_OK); + return bson_append_document_end(b, r); } bool -bsonAppendInt64(BSON *b, const char *key, int64_t v) +bsonAppendNull(BSON *b, const char *key) { - return (bson_append_long(b, key, v) == MONGO_OK); + return bson_append_null(b, key, strlen(key)); } bool -bsonAppendDouble(BSON *b, const char *key, double v) +bsonAppendInt32(BSON *b, const char *key, int v) { - return (bson_append_double(b, key, v) == MONGO_OK); + return bson_append_int32(b, key, strlen(key), v); } bool -bsonAppendUTF8(BSON *b, const char *key, char *v) +bsonAppendInt64(BSON *b, const char *key, int64_t v) { - return (bson_append_string(b, key, v) == MONGO_OK); + return bson_append_int64(b, key, strlen(key), v); } bool -bsonAppendBinary(BSON *b, const char *key, char *v, size_t len) -{ - return (bson_append_binary(b, key, BSON_BIN_BINARY, v, len) == MONGO_OK); -} -bool -bsonAppendDate(BSON *b, const char *key, time_t v) +bsonAppendDouble(BSON *b, const char *key, double v) { - return (bson_append_date(b, key, v) == MONGO_OK); + return bson_append_double(b, key, strlen(key), v); } bool -bsonAppendStartArray(BSON *b, const char *key, BSON *c) +bsonAppendUTF8(BSON *b, const char *key, char *v) { - return (bson_append_start_array(b, key) == MONGO_OK); -} -bool -bsonAppendFinishArray(BSON *b, BSON *c) -{ - return (bson_append_finish_array(b) == MONGO_OK); + return bson_append_utf8(b, key, strlen(key), v, strlen(v)); } bool -bsonAppendStartObject(BSON *b, char *key, BSON *r) +bsonAppendBinary(BSON *b, const char *key, char *v, size_t len) { - return (bson_append_start_object(b, key) == MONGO_OK); + return bson_append_binary(b, key, (int) strlen(key), BSON_SUBTYPE_BINARY, + (const uint8_t *) v, len); } bool -bsonAppendFinishObject(BSON *b, BSON *r) +bsonAppendDate(BSON *b, const char *key, time_t v) { - return (bson_append_finish_object(b) == MONGO_OK); + return bson_append_date_time(b, key, strlen(key), v); } bool bsonAppendBson(BSON *b, char *key, BSON *c) { - return (bson_append_bson(b, key, c) == MONGO_OK); + return bson_append_document(b, key, strlen(key), c); } bool -bsonFinish(BSON *b) +bsonAppendStartArray(BSON *b, const char *key, BSON *c) { - return (bson_finish(b) == MONGO_OK); + return bson_append_array_begin(b, key, -1, c); } -json_object * -jsonTokenerPrase(char *s) +bool +bsonAppendFinishArray(BSON *b, BSON *c) { - return json_tokener_parse(s); + return bson_append_array_end(b, c); } bool @@ -391,26 +555,27 @@ jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) if (!v) { - bson_append_null(bb, k); + bsonAppendNull(bb, k); return status; } switch (json_object_get_type(v)) { case json_type_int: - bson_append_int(bb, k, json_object_get_int(v)); + bsonAppendInt32(bb, k, json_object_get_int(v)); break; case json_type_boolean: - bson_append_bool(bb, k, json_object_get_boolean(v)); + bsonAppendBool(bb, k, json_object_get_boolean(v)); break; case json_type_double: - bson_append_double(bb, k, json_object_get_double(v)); + bsonAppendDouble(bb, k, json_object_get_double(v)); break; case json_type_string: - bson_append_string(bb, k, json_object_get_string(v)); + 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"); @@ -420,8 +585,7 @@ jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) bson_oid_t bsonObjectId; memset(bsonObjectId.bytes, 0, sizeof(bsonObjectId.bytes)); - bsonOidFromString(&bsonObjectId, - (char *) json_object_get_string(joj)); + bsonOidFromString(&bsonObjectId, (char *) json_object_get_string(joj)); status = bsonAppendOid(bb, k, &bsonObjectId); break; } @@ -431,29 +595,28 @@ jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) status = bsonAppendDate(bb, k, json_object_get_int64(joj)); break; } - - bson_append_start_object(bb, k); + bsonAppendStartObject(bb, (char *) k, &t); { json_object_object_foreach(v, kk, vv) - jsonToBsonAppendElement(bb, kk, vv); + jsonToBsonAppendElement(&t, kk, vv); } - bson_append_finish_object(bb); + bsonAppendFinishObject(bb, &t); } break; case json_type_array: { int i; char buf[10]; + BSON t; - bson_append_start_array(bb, k); + bsonAppendStartArray(bb, k, &t); for (i = 0; i < json_object_array_length(v); i++) { sprintf(buf, "%d", i); - jsonToBsonAppendElement(bb, buf, - json_object_array_get_idx(v, i)); + jsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i)); } - bson_append_finish_object(bb); + bsonAppendFinishObject(bb, &t); } break; default: @@ -466,17 +629,53 @@ jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *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) { - return mongo_count(conn, database, collection, b); -} + BSON *command; + BSON *reply; + double count = 0; + mongoc_cursor_t *cursor; -void -bsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer) -{ - bson_iterator_from_buffer(i, buffer); + command = bsonCreate(); + reply = bsonCreate(); + bsonAppendUTF8(command, "count", (char *) collection); + if (b) /* Not empty */ + bsonAppendBson(command, "query", (BSON *) b); + + cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, + 0, command, NULL, NULL); + if (cursor) + { + BSON *doc; + bool ret; + + ret = mongoc_cursor_next(cursor, (const BSON **) &doc); + if (ret) + { + bson_iter_t it; + + bson_copy_to(doc, reply); + if (bson_iter_init_find(&it, reply, "n")) + count = bsonIterDouble(&it); + } + mongoc_cursor_destroy(cursor); + } + bsonDestroy(reply); + bsonDestroy(command); + + return count; } void @@ -488,30 +687,82 @@ bsonOidToString(const bson_oid_t *o, char str[25]) const char * bsonIterCode(BSON_ITERATOR *i) { - return bson_iterator_code(i); + return bson_iter_code(i, NULL); } const char * bsonIterRegex(BSON_ITERATOR *i) { - return bson_iterator_regex(i); + return bson_iter_regex(i, NULL); } -const char * -bsonIterKey(BSON_ITERATOR *i) +const bson_value_t * +bsonIterValue(BSON_ITERATOR *i) { - return bson_iterator_key(i); + return bson_iter_value(i); } -const char * -bsonIterValue(BSON_ITERATOR *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 = bson_as_json(&bson, NULL); + + if (json != NULL) + { + appendStringInfoString(output, json); + bson_free(json); + } + } +} + +void +dumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { - return bson_iterator_value(i); + 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_json(&bson, NULL))) + { + appendStringInfoString(output, json); + bson_free(json); + } + } } char * bsonAsJson(const BSON *bsonDocument) { - ereport(ERROR, - (errmsg("full document retrival only available in MongoC meta driver"))); + return bson_as_json(bsonDocument, NULL); } diff --git a/mongo_wrapper.h b/mongo_wrapper.h index ba28c17..ebfe426 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -17,20 +17,13 @@ #include "mongo_fdw.h" -#ifdef META_DRIVER #include "mongoc.h" -#else -#include "mongo.h" -#endif #define json_object json_object_tmp #include -#ifdef META_DRIVER MONGO_CONN *mongoConnect(MongoFdwOptions *opt); -#else -MONGO_CONN *mongoConnect(MongoFdwOptions *opt); -#endif + void mongoDisconnect(MONGO_CONN *conn); bool mongoInsert(MONGO_CONN *conn, char *database, char *collection, BSON *b); @@ -56,17 +49,8 @@ int64_t bsonIterInt64(BSON_ITERATOR *it); double bsonIterDouble(BSON_ITERATOR *it); bool bsonIterBool(BSON_ITERATOR *it); const char *bsonIterString(BSON_ITERATOR *it); -#ifdef META_DRIVER const char *bsonIterBinData(BSON_ITERATOR *it, uint32_t *len); -#else -const char *bsonIterBinData(BSON_ITERATOR *it); -int bsonIterBinLen(BSON_ITERATOR *it); -#endif -#ifdef META_DRIVER const bson_oid_t *bsonIterOid(BSON_ITERATOR *it); -#else -bson_oid_t *bsonIterOid(BSON_ITERATOR *it); -#endif time_t bsonIterDate(BSON_ITERATOR *it); int bsonIterType(BSON_ITERATOR *it); int bsonIterNext(BSON_ITERATOR *it); @@ -76,11 +60,7 @@ 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); -#ifdef META_DRIVER const bson_value_t *bsonIterValue(BSON_ITERATOR *i); -#else -const char *bsonIterValue(BSON_ITERATOR *i); -#endif void bsonIteratorFromBuffer(BSON_ITERATOR *i, const char *buffer); @@ -99,7 +79,6 @@ 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 bsonFinish(BSON *b); bool jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v); json_object *jsonTokenerPrase(char *s); diff --git a/mongo_wrapper_meta.c b/mongo_wrapper_meta.c deleted file mode 100644 index 4969ce9..0000000 --- a/mongo_wrapper_meta.c +++ /dev/null @@ -1,780 +0,0 @@ -/*------------------------------------------------------------------------- - * - * mongo_wrapper_meta.c - * Wrapper functions for remote MongoDB servers - * - * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. - * Portions Copyright (c) 2012–2014 Citus Data, Inc. - * - * IDENTIFICATION - * mongo_wrapper_meta.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 -bsonFinish(BSON *b) -{ - /* - * There is no need for bson_finish in Meta Driver. We are doing nothing, - * just because of compatibility with legacy driver. - */ - return true; -} - -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[10]; - BSON t; - - bsonAppendStartArray(bb, k, &t); - for (i = 0; i < json_object_array_length(v); i++) - { - sprintf(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; - mongoc_cursor_t *cursor; - - command = bsonCreate(); - reply = bsonCreate(); - bsonAppendUTF8(command, "count", (char *) collection); - if (b) /* Not empty */ - bsonAppendBson(command, "query", (BSON *) b); - - bsonFinish(command); - - cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, - 0, command, NULL, NULL); - if (cursor) - { - BSON *doc; - bool ret; - - ret = mongoc_cursor_next(cursor, (const BSON **) &doc); - if (ret) - { - bson_iter_t it; - - bson_copy_to(doc, reply); - if (bson_iter_init_find(&it, reply, "n")) - count = bsonIterDouble(&it); - } - mongoc_cursor_destroy(cursor); - } - 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 = bson_as_json(&bson, NULL); - - 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_json(&bson, NULL))) - { - appendStringInfoString(output, json); - bson_free(json); - } - } -} - -char * -bsonAsJson(const BSON *bsonDocument) -{ - return bson_as_json(bsonDocument, NULL); -} diff --git a/option.c b/option.c index 6b3b636..0b0389f 100644 --- a/option.c +++ b/option.c @@ -106,13 +106,11 @@ mongo_fdw_validator(PG_FUNCTION_ARGS) "unsigned short", intString))); } else if (strcmp(optionName, OPTION_NAME_USE_REMOTE_ESTIMATE) == 0 -#ifdef META_DRIVER || 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 -#endif ) { /* These accept only boolean values */ @@ -184,20 +182,17 @@ mongo_get_options(Oid foreignTableId) options = (MongoFdwOptions *) palloc0(sizeof(MongoFdwOptions)); options->use_remote_estimate = false; -#ifdef META_DRIVER options->ssl = false; options->weak_cert_validation = false; options->enable_join_pushdown = true; options->enable_aggregate_pushdown = true; options->enable_order_by_pushdown = true; -#endif /* Loop through the options */ foreach(lc, optionList) { DefElem *def = (DefElem *) lfirst(lc); -#ifdef META_DRIVER if (strcmp(def->defname, OPTION_NAME_READ_PREFERENCE) == 0) options->readPreference = defGetString(def); @@ -240,7 +235,6 @@ mongo_get_options(Oid foreignTableId) options->enable_order_by_pushdown = defGetBoolean(def); else /* This is for continuation */ -#endif if (strcmp(def->defname, OPTION_NAME_ADDRESS) == 0) options->svr_address = pstrdup(defGetString(def)); diff --git a/sql/server_options.sql b/sql/server_options.sql index 7c444a0..03bb200 100644 --- a/sql/server_options.sql +++ b/sql/server_options.sql @@ -78,8 +78,6 @@ ALTER SERVER mongo_server OPTIONS (SET ssl 'false'); SELECT a, b FROM f_mongo_test ORDER BY 1, 2; -- Alter server to add authentication_database option --- authentication_database options is not supported with legacy driver --- so below queries will fail when compiled with legacy driver. 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); From 373ea031a0870d105e73c367fdf26fa35cafe797 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 4 Jan 2024 11:42:21 +0530 Subject: [PATCH 217/239] Refactor README.md file. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main changes include moving the installation steps to the newly created INSTALL.md file. Other changes in README.md are for unifying it with FDW documentation template. Reported on GitHub through issue #168 by mkgrgis (Михаил). FDW-665, initial patch by mkgrgis, further revised and improved by Vaibhav Dalvi. --- INSTALL.md | 155 ++++++++++++++ README.md | 595 +++++++++++++++++++++++++++++------------------------ 2 files changed, 482 insertions(+), 268 deletions(-) create mode 100644 INSTALL.md diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..ffa0a34 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,155 @@ +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.17.3 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.17.3` + + ```sh + wget https://github.com/mongodb/mongo-c-driver/releases/download/1.17.3/mongo-c-driver-1.17.3.tar.gz + tar xzf mongo-c-driver-1.17.3.tar.gz + rm -rf mongo-c-driver + mv mongo-c-driver-1.17.3 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.15-20200726.tar.gz + tar -xzf json-c-0.15-20200726.tar.gz + rm -rf json-c + mv json-c-json-c-0.15-20200726/ 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 +``` + +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]: http://mongoc.org/libmongoc/1.17.3/installing.html#configuring-the-build +[4]: https://github.com/json-c/json-c/tree/json-c-0.15-20200726#build-instructions-- diff --git a/README.md b/README.md index 1e0f51d..62b5b50 100644 --- a/README.md +++ b/README.md @@ -1,292 +1,294 @@ -# MongoDB Foreign Data Wrapper for PostgreSQL +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 11, 12, 13, 14, 15, and 16. +Postgres Advanced Server 11, 12, 13, 14, 15 and 16. -Installation ------------- -To compile the [MongoDB][1] foreign data wrapper, 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. +PostgreSQL + 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: +Contents +-------- -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. +1. [Features](#features) +2. [Supported platforms](#supported-platforms) +3. [Installation](#installation) +4. [Usage](#usage) +5. [Functions](#functions) +6. [Identifier case handling](#identifier-case-handling) +7. [Generated columns](#generated-columns) +8. [Character set handling](#character-set-handling) +9. [Examples](#examples) +10. [Limitations](#limitations) +11. [Contributing](#contributing) +12. [Useful links](#useful-links) + +Features +-------- - * autogen.sh +The following enhancements are added to the latest version of `mongo_fdw`: -The script autogen.sh will do all the necessary steps to build with mongoc -driver accordingly. +#### 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`. -## Steps for manual installation -### mongo-c -1. Download and extract source code of mongoc driver for version `1.17.3` +#### 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. - ```sh - wget https://github.com/mongodb/mongo-c-driver/releases/download/1.17.3/mongo-c-driver-1.17.3.tar.gz - tar xzf mongo-c-driver-1.17.3.tar.gz - rm -rf mongo-c-driver - mv mongo-c-driver-1.17.3 mongo-c-driver - cd mongo-c-driver - ``` +#### 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. -2. Configure mongoc driver +#### 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. - ```sh - cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF . - ``` +#### 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. - To install at custom location: +#### 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. - ```sh - cmake -DCMAKE_INSTALL_PREFIX=YOUR_INSTALLATION_DIRECTORY -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF . - ``` +#### GUC variables: -3. Compile and install + * `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`. - ```sh - cmake --build . - cmake --build . --target install - ``` +Supported platforms +------------------- -For more details on installation of mongo-c driver, you can refer [here][5]. +`mongo_fdw` was developed on Linux, and should run on any +reasonably POSIX-compliant system. -### json-c -1. Download and extract source code +Installation +------------ - ```sh - wget https://github.com/json-c/json-c/archive/json-c-0.15-20200726.tar.gz - tar -xzf json-c-0.15-20200726.tar.gz - rm -rf json-c - mv json-c-json-c-0.15-20200726/ json-c - cd json-c - ``` +About script or manual installation, `mongo-c` driver please read the following [instructions in INSTALL.md](INSTALL.md). -2. Configure +If you run into any issues, please [let us know][2]. - ```sh - cmake . - ``` - To install at custom location: +Usage +----- - ```sh - cmake -DCMAKE_INSTALL_PREFIX=YOUR_INSTALLATION_DIRECTORY . - ``` +## CREATE SERVER options -3. Compile and install +`mongo_fdw` accepts the following options via the `CREATE SERVER` command: - ```sh - make - make install - ``` +- **address** as *string*, optional, default `127.0.0.1` -For more details on installation of json-c library, you can refer [here][6]. + Address or hostname of the MongoDB server. -## 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, +- **port** as *integer*, optional, default `27017`. -```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 -``` + Port number of the MongoDB server. -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: +- **use_remote_estimate** as *boolean*, optional, default `false` -```sh -export LD_LIBRARY_PATH=/home/mongo-c/lib64:$LD_LIBRARY_PATH -``` + Controls whether `mongo_fdw` uses exact rows from + remote collection to obtain cost estimates. -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. +- **authentication_database** as *string*, optional + Database against which user will be + authenticated against. Only valid with password based authentication. -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: +- **replica_set** as *string*, optional - ```sh - export PATH=/usr/local/pgsql/bin/:$PATH - ``` + Replica set the server is member of. If set, + driver will auto-connect to correct primary in the replica set when + writing. -2. Compile the code using make. +- **read_preference** as *string*, optional, default `primary` - ```sh - make USE_PGXS=1 - ``` + `primary`, `secondary`, `primaryPreferred`, + `secondaryPreferred`, or `nearest`. -3. Finally install the foreign data wrapper. +- **ssl** as *boolean*, optional, default `false` - ```sh - make USE_PGXS=1 install - ``` + Enable ssl. See http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to + understand the options. -4. Running regression test. +- **pem_file** as *string*, optional - Run `mongodb_init.sh` file to load required collections. + The .pem file that contains both the TLS/SSL certificate and + key. - ```sh - source mongodb_init.sh - ``` - Finally, run regression. +- **pem_pwd** as *string*, optional - ```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. + The password to decrypt the certificate key file(i.e. pem_file) +- **ca_file** as *string*, optional -If you run into any issues, please [let us know][2]. + The .pem file that contains the root certificate chain from the + Certificate Authority. -Enhancements ------------ -The following enhancements are added to the latest version of mongo_fdw: +- **ca_dir** as *string*, optional -### 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. + The absolute path to the `ca_file`. -### 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. +- **crl_file** as *string*, optional -### JOIN push-down -mongo_fdw now also supports join push-down. The joins between two -foreign tables from the same remote MySQL 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. + The .pem file that contains the Certificate Revocation List. -### 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. +- **weak_cert_validation** as *boolean*, optional, default `false` -### 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. + Enable the validation checks for TLS/SSL certificates and allows the use of invalid + certificates to connect if set to `true`. -### 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. +- **enable_join_pushdown** as *boolean*, optional, default `true` -Usage ------ -The following parameters can be set on a MongoDB foreign server object: - - * `address`: Address or hostname of the MongoDB server. Defaults to - `127.0.0.1` - * `port`: Port number of the MongoDB server. Defaults to `27017`. - * `use_remote_estimate`: Controls whether mongo_fdw uses exact rows from - remote collection to obtain cost estimates. Default is `false`. - * `authentication_database`: Database against which user will be - authenticated against. Only valid with password based authentication. - * `replica_set`: Replica set the server is member of. If set, - driver will auto-connect to correct primary in the replica set when - writing. - * `read_preference`: primary [default], secondary, primaryPreferred, - secondaryPreferred, or nearest. - * `ssl`: false [default], true to enable ssl. See - http://mongoc.org/libmongoc/current/mongoc_ssl_opt_t.html to - understand the options. - * `pem_file`: The .pem file that contains both the TLS/SSL certificate and - key. - * `pem_pwd`: The password to decrypt the certificate key file(i.e. pem_file) - * `ca_file`: The .pem file that contains the root certificate chain from the - Certificate Authority. - * `ca_dir`: The absolute path to the `ca_file`. - * `crl_file`: The .pem file that contains the Certificate Revocation List. - * `weak_cert_validation`: false [default], This is to enable or disable the - validation checks for TLS/SSL certificates and allows the use of invalid - certificates to connect if set to `true`. - * `enable_join_pushdown`: If `true`, pushes the join between two foreign + 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. Default is `true`. - * `enable_aggregate_pushdown`: If `true`, push aggregates to the remote + 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. Default is `true`. - * `enable_order_by_pushdown`: 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. Default is `true`. + option value. -The following parameters can be set on a MongoDB foreign table object: +- **enable_order_by_pushdown** as *boolean*, optional, default `true` - * `database`: Name of the MongoDB database to query. Defaults to - `test`. - * `collection`: Name of the MongoDB collection to query. Defaults to - the foreign table name used in the relevant `CREATE` command. - * `enable_join_pushdown`: Similar to the server-level option, but can be - configured at table level as well. Default is `true`. - * `enable_aggregate_pushdown`: Similar to the server-level option, but - can be configured at table level as well. Default is `true`. - * `enable_order_by_pushdown`: Similar to the server-level option, but can - be configured at table level as well. Default is `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. -The following parameters can be supplied while creating user mapping: +## CREATE USER MAPPING options - * `username`: Username to use when connecting to MongoDB. - * `password`: Password to authenticate to the MongoDB server. +`mongo_fdw` accepts the following options via the `CREATE USER MAPPING` +command: -GUC variables: +- **username** as *string*, optional - * `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`. + Username to use when connecting to MongoDB. - * `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`. +- **password** as *string*, optional - * `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`. + 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: + +**Yet not described!**. + +Identifier case handling +------------------------ + +PostgreSQL folds identifiers to lower case by default, MongoDB use JSON notation of identifiers. + +All transformation rules and problems **yet not described**. + +Generated columns +----------------- + +`mongo_fdw` doesn't provides support for PostgreSQL's generated +columns (PostgreSQL 12+). + +**Behaviour with generated columns yet not tested and not described**. + +Note that while `mongo_fdw` will `INSERT` or `UPDATE` the generated column value +in MongoDB, there is nothing to stop the value being modified within MongoDB, +and hence no guarantee that in subsequent `SELECT` operations the column will +still contain the expected generated value. This limitation also applies to +`postgres_fdw`. + +For more details on generated columns see: + +- [Generated Columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) +- [CREATE FOREIGN TABLE](https://www.postgresql.org/docs/current/sql-createforeigntable.html) + +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. + +Encodings mapping between PostgreSQL and MongoDB **yet not described**. + +Examples +-------- As an example, the following commands demonstrate loading the `mongo_fdw` wrapper, creating a server, and then creating a foreign @@ -298,45 +300,76 @@ isn't provided, the wrapper uses the default value mentioned above. them when estimating costs for the query execution plan. To see selected execution plans for a query, just run `EXPLAIN`. -Examples --------- +### Install the extension: -Examples with [MongoDB][1]'s equivalent statements. +Once for a database you need, as PostgreSQL superuser. ```sql --- load extension first time after install CREATE EXTENSION mongo_fdw; +``` --- create server object -CREATE SERVER mongo_server - FOREIGN DATA WRAPPER mongo_fdw - OPTIONS (address '127.0.0.1', port '27017'); - --- create user mapping -CREATE USER MAPPING FOR postgres - SERVER mongo_server - OPTIONS (username 'mongo_user', password 'mongo_pass'); - --- create foreign table -CREATE FOREIGN TABLE warehouse - ( - _id name, - warehouse_id int, - warehouse_name text, - warehouse_created timestamptz - ) - SERVER mongo_server - OPTIONS (database 'db', collection 'warehouse'); +### 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 from table +#### `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 ( { @@ -349,13 +382,14 @@ db.warehouse.find "warehouse_name" : "UPS", "warehouse_created" : ISODate("2014-12-12T07:12:10Z") } - --- insert row in table +``` +#### `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 ( { @@ -364,20 +398,24 @@ db.warehouse.insert "warehouse_created" : ISODate("2015-11-11T08:13:10Z") } ) - --- delete row from table +``` +#### `DELETE` +```sql DELETE FROM warehouse WHERE warehouse_id = 2; - +``` +``` db.warehouse.remove ( { "warehouse_id" : 2 } ) - --- update a row of table +``` +#### `UPDATE` +```sql UPDATE warehouse SET warehouse_name = 'UPS_NEW' WHERE warehouse_id = 1; - +``` +``` db.warehouse.update ( { @@ -389,39 +427,62 @@ db.warehouse.update "warehouse_created" : ISODate("2014-12-12T07:12:10Z") } ) - --- explain a table +``` +#### `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) - --- collect data distribution statistics +``` +``` ANALYZE warehouse; - ``` Limitations ----------- - * If the BSON document key contains uppercase letters or occurs within - a nested document, `mongo_fdw` requires the corresponding column names + - 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 + - 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]. +Useful links +------------ + +### 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/ Support ------- @@ -430,7 +491,7 @@ 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. +also support `mongo_fdw`. License @@ -449,5 +510,3 @@ See the [`LICENSE`][4] file for full details. [2]: https://github.com/enterprisedb/mongo_fdw/issues/new [3]: CONTRIBUTING.md [4]: LICENSE -[5]: http://mongoc.org/libmongoc/1.17.3/installing.html#configuring-the-build -[6]: https://github.com/json-c/json-c/tree/json-c-0.15-20200726#build-instructions-- From 9de8f0fd4eda0c98b7283b0be6c23e55e0c21de1 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 4 Jan 2024 16:26:28 +0530 Subject: [PATCH 218/239] Tweak some tests to produce consistent output on various platforms. Tests involving MIN()/MAX() aggregates sometimes take a shortcut path due to hardware differences, resulting in the explain plan changes and, thus causing regression failures. To get a consistent result, change those to SUM()/AVG() aggregates or add a relevant grouping clause. The patch also tweaks the startup cost for the ordered paths so that ORDER BY will get pushed for a few more queries. FDW-598, Vaibhav Dalvi, reviewed by Suraj Kharage and Jeevan Chalke, tested by Ajay Pal. --- expected/aggregate_pushdown.out | 108 ++++++++++++++---------------- expected/aggregate_pushdown_1.out | 108 ++++++++++++++---------------- expected/aggregate_pushdown_2.out | 94 +++++++++++++------------- expected/aggregate_pushdown_3.out | 108 ++++++++++++++---------------- expected/pushdown.out | 24 +++---- mongo_fdw.c | 2 +- sql/aggregate_pushdown.sql | 30 ++++----- sql/pushdown.sql | 2 +- 8 files changed, 226 insertions(+), 250 deletions(-) diff --git a/expected/aggregate_pushdown.out b/expected/aggregate_pushdown.out index 005aba8..3a752ad 100644 --- a/expected/aggregate_pushdown.out +++ b/expected/aggregate_pushdown.out @@ -176,30 +176,30 @@ SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 -- Aggregate with unshippable GROUP BY clause are not pushed EXPLAIN (VERBOSE, COSTS OFF) -SELECT max(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; QUERY PLAN ------------------------------------------------------------------------------------ Sort - Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) - Sort Key: (max(fdw137_t1.c4)) + Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (avg(fdw137_t1.c4)) -> HashAggregate - Output: max(c4), ((c4 * ((random() <= '1'::double precision))::integer)) + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - max ------- - 400 - 600 - 700 - 800 - 900 - 1300 - +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) @@ -1473,8 +1473,8 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY (3 rows) -- FDW-131: Limit and offset pushdown with Aggregate pushdown. -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; - min | c1 +SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + avg | c1 -----+---- 10 | 10 20 | 20 @@ -1485,47 +1485,47 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; (6 rows) EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - min | c1 +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 @@ -1535,16 +1535,16 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit ALL EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - min | c1 +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 @@ -1554,51 +1554,45 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit with -ve value. Shouldn't pushdown. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; - QUERY PLAN --------------------------------------------------------------- +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +--------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) - -> GroupAggregate - Output: c1, max(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + 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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; QUERY PLAN --------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) + Output: c1, (sum(c1)) -> Foreign Scan - Output: c1, (max(c1)) + Output: c1, (sum(c1)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (5 rows) -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +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 --------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------- Limit Output: c1, (avg(c1)) - -> GroupAggregate - Output: c1, avg(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + -> 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; diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out index 374a004..a96eab8 100644 --- a/expected/aggregate_pushdown_1.out +++ b/expected/aggregate_pushdown_1.out @@ -176,30 +176,30 @@ SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 -- Aggregate with unshippable GROUP BY clause are not pushed EXPLAIN (VERBOSE, COSTS OFF) -SELECT max(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; QUERY PLAN ------------------------------------------------------------------------------------ Sort - Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) - Sort Key: (max(fdw137_t1.c4)) + Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (avg(fdw137_t1.c4)) -> HashAggregate - Output: max(c4), ((c4 * ((random() <= '1'::double precision))::integer)) + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - max ------- - 400 - 600 - 700 - 800 - 900 - 1300 - +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) @@ -1473,8 +1473,8 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY (3 rows) -- FDW-131: Limit and offset pushdown with Aggregate pushdown. -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; - min | c1 +SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + avg | c1 -----+---- 10 | 10 20 | 20 @@ -1485,47 +1485,47 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; (6 rows) EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - min | c1 +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 @@ -1535,16 +1535,16 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit ALL EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - min | c1 +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 @@ -1554,51 +1554,45 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit with -ve value. Shouldn't pushdown. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; - QUERY PLAN --------------------------------------------------------------- +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +--------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) - -> GroupAggregate - Output: c1, max(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + 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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; QUERY PLAN --------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) + Output: c1, (sum(c1)) -> Foreign Scan - Output: c1, (max(c1)) + Output: c1, (sum(c1)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (5 rows) -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +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 --------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------- Limit Output: c1, (avg(c1)) - -> GroupAggregate - Output: c1, avg(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + -> 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; diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out index 432d90e..4b25668 100644 --- a/expected/aggregate_pushdown_2.out +++ b/expected/aggregate_pushdown_2.out @@ -185,30 +185,30 @@ SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 -- Aggregate with unshippable GROUP BY clause are not pushed EXPLAIN (VERBOSE, COSTS OFF) -SELECT max(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; QUERY PLAN ------------------------------------------------------------------------------------ Sort - Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) - Sort Key: (max(fdw137_t1.c4)) + Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (avg(fdw137_t1.c4)) -> HashAggregate - Output: max(c4), ((c4 * ((random() <= '1'::double precision))::integer)) + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - max ------- - 400 - 600 - 700 - 800 - 900 - 1300 - +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) @@ -1496,8 +1496,8 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY (3 rows) -- FDW-131: Limit and offset pushdown with Aggregate pushdown. -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; - min | c1 +SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + avg | c1 -----+---- 10 | 10 20 | 20 @@ -1508,62 +1508,62 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; (6 rows) EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN -------------------------------------------------------------- Limit - Output: (min(c1)), c1 + Output: (sum(c1)), c1 -> GroupAggregate - Output: min(c1), c1 + Output: sum(c1), c1 Group Key: fdw137_t2.c1 -> Foreign Scan on public.fdw137_t2 Output: _id, c1, c2, c3 Foreign Namespace: mongo_fdw_regress.test_tbl2 (8 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - min | c1 +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 min(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; QUERY PLAN -------------------------------------------------------------- Limit - Output: (min(c1)), c1 + Output: (sum(c1)), c1 -> GroupAggregate - Output: min(c1), c1 + Output: sum(c1), c1 Group Key: fdw137_t2.c1 -> Foreign Scan on public.fdw137_t2 Output: _id, c1, c2, c3 Foreign Namespace: mongo_fdw_regress.test_tbl2 (8 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------------------- Limit - Output: (min(c1)), c1 + Output: (sum(c1)), c1 -> Sort - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Sort Key: fdw137_t2.c1 NULLS FIRST -> Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (8 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - min | c1 +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 @@ -1573,21 +1573,21 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit ALL EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------------------- Limit - Output: (min(c1)), c1 + Output: (sum(c1)), c1 -> Sort - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Sort Key: fdw137_t2.c1 NULLS FIRST -> Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (8 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - min | c1 +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 @@ -1597,13 +1597,13 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit with -ve value. Shouldn't pushdown. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; QUERY PLAN -------------------------------------------------------------- Limit - Output: c1, (max(c1)) + Output: c1, (sum(c1)) -> GroupAggregate - Output: c1, max(c1) + Output: c1, sum(c1) Group Key: fdw137_t2.c1 -> Foreign Scan on public.fdw137_t2 Output: _id, c1, c2, c3 @@ -1611,25 +1611,25 @@ SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT - (8 rows) -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; QUERY PLAN --------------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) + Output: c1, (sum(c1)) -> Sort - Output: c1, (max(c1)) + Output: c1, (sum(c1)) Sort Key: fdw137_t2.c1 NULLS FIRST -> Foreign Scan - Output: c1, (max(c1)) + Output: c1, (sum(c1)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (8 rows) -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +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) diff --git a/expected/aggregate_pushdown_3.out b/expected/aggregate_pushdown_3.out index 12a3956..5d989eb 100644 --- a/expected/aggregate_pushdown_3.out +++ b/expected/aggregate_pushdown_3.out @@ -176,30 +176,30 @@ SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 -- Aggregate with unshippable GROUP BY clause are not pushed EXPLAIN (VERBOSE, COSTS OFF) -SELECT max(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; QUERY PLAN ------------------------------------------------------------------------------------ Sort - Output: (max(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) - Sort Key: (max(fdw137_t1.c4)) + Output: (avg(c4)), ((c4 * ((random() <= '1'::double precision))::integer)) + Sort Key: (avg(fdw137_t1.c4)) -> HashAggregate - Output: max(c4), ((c4 * ((random() <= '1'::double precision))::integer)) + 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 max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; - max ------- - 400 - 600 - 700 - 800 - 900 - 1300 - +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) @@ -1482,8 +1482,8 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY (3 rows) -- FDW-131: Limit and offset pushdown with Aggregate pushdown. -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; - min | c1 +SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; + avg | c1 -----+---- 10 | 10 20 | 20 @@ -1494,47 +1494,47 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; (6 rows) EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; - min | c1 +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 min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; - min | c1 +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 @@ -1544,16 +1544,16 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit ALL EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(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; QUERY PLAN --------------------------------------------------------------------------- Foreign Scan - Output: (min(c1)), c1 + Output: (sum(c1)), c1 Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (3 rows) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; - min | c1 +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 @@ -1563,51 +1563,45 @@ SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT -- Limit with -ve value. Shouldn't pushdown. EXPLAIN (VERBOSE, COSTS FALSE) -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; - QUERY PLAN --------------------------------------------------------------- +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; + QUERY PLAN +--------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) - -> GroupAggregate - Output: c1, max(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + 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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; QUERY PLAN --------------------------------------------------------------------------------- Limit - Output: c1, (max(c1)) + Output: c1, (sum(c1)) -> Foreign Scan - Output: c1, (max(c1)) + Output: c1, (sum(c1)) Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) (5 rows) -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +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 --------------------------------------------------------------- + QUERY PLAN +--------------------------------------------------------------------------------- Limit Output: c1, (avg(c1)) - -> GroupAggregate - Output: c1, avg(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 rows) + -> 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; diff --git a/expected/pushdown.out b/expected/pushdown.out index 035d15f..3abbd3c 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -738,22 +738,16 @@ SELECT * FROM f_mongo_test ORDER BY a USING OPERATOR(public.<^); (6 rows) EXPLAIN (COSTS FALSE, VERBOSE) -SELECT MIN(a) FROM f_mongo_test ORDER BY 1 USING OPERATOR(public.<^); - QUERY PLAN ------------------------------------------------------------------ +SELECT MIN(a) FROM f_mongo_test GROUP BY b ORDER BY 1 USING OPERATOR(public.<^); + QUERY PLAN +------------------------------------------------------------------------------------- Sort - Output: ($0) - Sort Key: ($0) USING <^ - InitPlan 1 (returns $0) - -> Limit - Output: f_mongo_test.a - -> Foreign Scan on public.f_mongo_test - Output: f_mongo_test.a - Filter: (f_mongo_test.a IS NOT NULL) - Foreign Namespace: mongo_fdw_regress.mongo_test - -> Result - Output: $0 -(12 rows) + 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. diff --git a/mongo_fdw.c b/mongo_fdw.c index c1fd598..4b61db8 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4154,7 +4154,7 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, fpinfo->pushdown_safe = true; /* TODO: Put accurate estimates */ - startup_cost = 15; + startup_cost = 10; total_cost = 10 + startup_cost; rows = 10; diff --git a/sql/aggregate_pushdown.sql b/sql/aggregate_pushdown.sql index cb6f4e0..372cb49 100644 --- a/sql/aggregate_pushdown.sql +++ b/sql/aggregate_pushdown.sql @@ -59,8 +59,8 @@ SELECT c1, sum(c1+2) FROM fdw137_t1 GROUP BY c1 HAVING min(c1) > 500 ORDER BY c1 -- Aggregate with unshippable GROUP BY clause are not pushed EXPLAIN (VERBOSE, COSTS OFF) -SELECT max(c4) FROM fdw137_t1 GROUP BY c4 * (random() <= 1)::int ORDER BY 1; -SELECT max(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; +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; @@ -384,37 +384,37 @@ SELECT a32, sum(a32) FROM f_test_large GROUP BY a31 ASC NULLS FIRST, a32 ASC NULLS FIRST; -- FDW-131: Limit and offset pushdown with Aggregate pushdown. -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; +SELECT avg(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1; EXPLAIN (VERBOSE, COSTS OFF) -SELECT min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 1 OFFSET 1; -SELECT min(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; +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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT 0 OFFSET 0; -SELECT min(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; +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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT NULL OFFSET 2; -SELECT min(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; +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 min(c1), c1 FROM fdw137_t2 GROUP BY c1 ORDER BY c1 ASC NULLS FIRST LIMIT ALL OFFSET 2; -SELECT min(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; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST LIMIT -1; +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, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +SELECT c1, sum(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; -- Should throw an error. -SELECT c1, max(c1) FROM fdw137_t2 GROUP BY c1 ORDER BY 1 ASC NULLS FIRST OFFSET -2; +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) diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 0345355..6bf665b 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -317,7 +317,7 @@ CREATE OPERATOR CLASS my_op_class FOR TYPE INT USING btree FAMILY my_op_family A 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 ORDER BY 1 USING OPERATOR(public.<^); +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. From 9a98959a325850785f6d49e7c35da0dc993b783b Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 4 Jan 2024 16:32:15 +0530 Subject: [PATCH 219/239] Update EDB copyrights for 2024. --- Makefile | 2 +- README.md | 2 +- autogen.sh | 2 +- connection.c | 2 +- deparse.c | 2 +- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 2 +- mongo_fdw.control | 2 +- mongo_fdw.h | 2 +- mongo_query.c | 2 +- mongo_query.h | 2 +- mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- option.c | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index f0b4064..3802b79 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index 62b5b50..2c22225 100644 --- a/README.md +++ b/README.md @@ -496,7 +496,7 @@ also support `mongo_fdw`. License ------- -Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. Portions Copyright © 2012–2014 Citus Data, Inc. This program is free software: you can redistribute it and/or modify it diff --git a/autogen.sh b/autogen.sh index ac17137..459ae8c 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index b1a8d28..b35bd35 100644 --- a/connection.c +++ b/connection.c @@ -4,7 +4,7 @@ * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/deparse.c b/deparse.c index 0512fc2..1684b97 100644 --- a/deparse.c +++ b/deparse.c @@ -4,7 +4,7 @@ * Query deparser for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index 84e6ab7..12f3753 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index 0996836..47ec3c2 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +-- Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. -- Portions Copyright © 2012–2014 Citus Data, Inc. -- complain if script is sourced in psql, rather than via CREATE EXTENSION diff --git a/mongo_fdw.c b/mongo_fdw.c index 4b61db8..4fe31be 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index 582bad4..311c587 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index 205834f..0dc1eb1 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index 361c3e6..89bbd80 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index c6ef94e..8d6540a 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 36d16ec..69ad65e 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index ebfe426..30efe7f 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index 0b0389f..0dba5d4 100644 --- a/option.c +++ b/option.c @@ -4,7 +4,7 @@ * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2023, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From 2cfaa39442cd4106633d2e5cb2c12ad4aa10b7bf Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 3 Apr 2024 17:53:38 +0530 Subject: [PATCH 220/239] Update README with EDB's external documentation link. FDW-676, Suraj Kharage. --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2c22225..3153474 100644 --- a/README.md +++ b/README.md @@ -465,6 +465,10 @@ out the contribution guidelines [here][3]. Useful links ------------ +### Documentation + + - For details, please refer to [mongo_fdw documentation][5]. + ### Source code Reference FDW realization, `postgres_fdw` @@ -510,3 +514,4 @@ See the [`LICENSE`][4] file for full details. [2]: https://github.com/enterprisedb/mongo_fdw/issues/new [3]: CONTRIBUTING.md [4]: LICENSE +[5]: https://www.enterprisedb.com/docs/mongo_data_adapter/latest/ From d07ef2de113c10eceac46fdd008939829811e1a7 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Wed, 3 Apr 2024 18:34:11 +0530 Subject: [PATCH 221/239] Remove support for v11. Standard Support for both EDB Postgres Advanced Server 11 and PostgreSQL 11 is already ended. Thus, adjust Makefile so that we restrict compilation of mongo_fdw code against v11. Update the README accordingly. Also, clean up the code for the same. FDW-673, Suraj Kharage, reviewed by Sravan Velagandula, tested by Ajaykumar Pal. --- Makefile | 4 +- README.md | 2 +- deparse.c | 10 - expected/aggregate_pushdown_2.out | 2058 ------------------------- expected/join_pushdown_2.out | 2107 -------------------------- expected/limit_offset_pushdown_1.out | 398 ----- mongo_fdw.c | 68 - mongo_fdw.h | 3 - mongo_query.c | 11 - 9 files changed, 3 insertions(+), 4658 deletions(-) delete mode 100644 expected/aggregate_pushdown_2.out delete mode 100644 expected/join_pushdown_2.out delete mode 100644 expected/limit_offset_pushdown_1.out diff --git a/Makefile b/Makefile index 3802b79..f0e63d9 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 11 12 13 14 15 16)) - $(error PostgreSQL 11, 12, 13, 14, 15, or 16 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 12 13 14 15 16)) + $(error PostgreSQL 12, 13, 14, 15, or 16 is required to compile this extension) endif diff --git a/README.md b/README.md index 3153474..c403d94 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 11, 12, 13, 14, 15 and 16. +Postgres Advanced Server 12, 13, 14, 15 and 16. PostgreSQL + MongoDB diff --git a/deparse.c b/deparse.c index 1684b97..0cc6115 100644 --- a/deparse.c +++ b/deparse.c @@ -25,13 +25,7 @@ #endif #include "mongoc.h" #include "mongo_query.h" -#if PG_VERSION_NUM < 120000 -#include "nodes/relation.h" -#include "optimizer/var.h" -#endif -#if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" -#endif #include "parser/parsetree.h" #include "utils/rel.h" #include "utils/syscache.h" @@ -677,9 +671,5 @@ mongo_is_foreign_pathkey(PlannerInfo *root, RelOptInfo *baserel, bool mongo_is_builtin(Oid oid) { -#if PG_VERSION_NUM >= 120000 return (oid < FirstGenbkiObjectId); -#else - return (oid < FirstBootstrapObjectId); -#endif } diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out deleted file mode 100644 index 4b25668..0000000 --- a/expected/aggregate_pushdown_2.out +++ /dev/null @@ -1,2058 +0,0 @@ -\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 ---------------------------------------------------------------------------------- - Sort - Output: c1, (sum(c1)), (count(*)) - Sort Key: fdw137_t1.c1 NULLS FIRST - -> Foreign Scan - Output: c1, (sum(c1)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 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 ---------------------------------------------------------------------------------- - Sort - Output: c8, (min(c2)) - Sort Key: fdw137_t1.c8 NULLS FIRST - -> Foreign Scan - Output: c8, (min(c2)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl1 fdw137_t1) -(6 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 ---------------------------------------------------------------------------------- - 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) - --- 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 ---------------------------------------------------------------------------------------------------------- - GroupAggregate - Output: t1.c1, count(*), t2.c4 - Group Key: t1.c1, t2.c4 - -> Foreign Scan - Output: t1.c1, t2.c4 - Foreign Namespace: (mongo_fdw_regress.test_tbl2 t1) INNER JOIN (mongo_fdw_regress.test_tbl1 t2) -(6 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 | 900 - 10 | 1 | - 10 | 1 | 700 - 20 | 1 | 1300 - 20 | 1 | 900 - 20 | 1 | 400 - 20 | 1 | 800 - 20 | 1 | 400 - 30 | 3 | 600 - 30 | 1 | 900 - 30 | 2 | 600 -(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) - --- 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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) - Sort Key: (sum(ftprt1_p1.c1)) - -> Append - -> Foreign Scan - Output: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) - -> Foreign Scan - Output: ftprt1_p2.c1, (sum(ftprt1_p2.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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) - Sort Key: (sum(ftprt1_p1.c2)) - -> Append - -> Foreign Scan - Output: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) - -> Foreign Scan - Output: ftprt1_p2.c1, (sum(ftprt1_p2.c2)), (min(ftprt1_p2.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 ------------------------------------------------------------------------------------------------------------------------ - Sort - Output: (sum(t2.c1)), t1.c8 - Sort Key: t1.c8 NULLS FIRST - -> 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)) -(6 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 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - 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 - 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 - -> 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) -(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 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 --------------------------------------------------------------- - Limit - Output: (sum(c1)), c1 - -> GroupAggregate - Output: sum(c1), c1 - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 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 --------------------------------------------------------------- - Limit - Output: (sum(c1)), c1 - -> GroupAggregate - Output: sum(c1), c1 - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 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 ---------------------------------------------------------------------------------------- - Limit - Output: (sum(c1)), c1 - -> Sort - Output: (sum(c1)), c1 - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: (sum(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 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 ---------------------------------------------------------------------------------------- - Limit - Output: (sum(c1)), c1 - -> Sort - Output: (sum(c1)), c1 - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: (sum(c1)), c1 - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 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)) - -> GroupAggregate - Output: c1, sum(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 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)) - -> Sort - Output: c1, (sum(c1)) - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: c1, (sum(c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(8 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)) - -> GroupAggregate - Output: c1, avg(c1) - Group Key: fdw137_t2.c1 - -> Foreign Scan on public.fdw137_t2 - Output: _id, c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(8 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) - -> Sort - Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) - Sort Key: fdw137_t2.c1 NULLS FIRST - -> Foreign Scan - Output: fdw137_t2.c1, (avg(fdw137_t2.c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test_tbl2 fdw137_t2) -(12 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 ------------------------------------------------------------------------------------------------------------------------ - Sort - Output: (count(*)), t1.c8 - Sort Key: t1.c8 NULLS FIRST - -> Foreign Scan - Output: (count(*)), t1.c8 - Foreign Namespace: Aggregate on ((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) - ---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 ---------------------------------------------------------------------------------- - 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) - -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 ------------------------------------------------------------------------------------------------------------------------ - 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) - -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 ---------------------------------------------------------------------------------- - 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) - -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 ------------------------------------------------------------------------------------------------------------------------ - 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) - --- 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out deleted file mode 100644 index a7f4557..0000000 --- a/expected/join_pushdown_2.out +++ /dev/null @@ -1,2107 +0,0 @@ -\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'); -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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 ------------------------------------------------------------------------------------------------------- - Limit - -> Foreign Scan - 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 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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - | | 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 | | -(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) - -> Limit - -> 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) -(8 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) - -> Limit - -> 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) -(8 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 | 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 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: t.c1_1, t.c2_1, t.c1_3 - Sort Key: t.c1_3, t.c1_1 - CTE t - -> Foreign Scan - Output: d.c1, d.c3, e.c1 - Foreign Namespace: (mongo_fdw_regress.test_tbl1 d) INNER JOIN (mongo_fdw_regress.test_tbl2 e) - -> CTE Scan on t - Output: t.c1_1, t.c2_1, t.c1_3 -(9 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. -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) - --- Don't pushdown when full document retrieval is involved. -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) - --- 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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 | 30 - 100 | 40 -(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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 ---------------------------------------------------------------------------------------------------------- - 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) - -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 | 10 - 100 | 20 - 100 | 30 -(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.c1, t2.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Foreign Scan - Output: t1_1.c1, t2_1.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.c1, t2.c2, t3.c2 - Hash Cond: (t1.c1 = t3.c1) - -> Foreign Scan - Output: t1.c1, t2.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Hash - Output: t3.c2, t3.c1 - -> Foreign Scan on public.ftprt1_p1 t3 - Output: t3.c2, t3.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> 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.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2) - -> Hash - Output: t3_1.c2, t3_1.c1 - -> Foreign Scan on public.ftprt1_p2 t3_1 - Output: t3_1.c2, t3_1.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.c1, t1.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Foreign Scan - Output: t1_1.c1, t1_1.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 --- Table alias in foreign scan is different for v12, v11 and v10. -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 ------------------------------------------------------------------------------------- - Sort - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 - -> Append - -> Hash Left Join - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Hash - Output: ftprt2_p1.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p1 - Output: ftprt2_p1.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test3 - -> Hash Left Join - Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) - Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Foreign Namespace: mongo_fdw_regress.test2 - -> Hash - Output: ftprt2_p2.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p2 - Output: ftprt2_p2.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test4 -(26 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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) - -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; -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 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 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; diff --git a/expected/limit_offset_pushdown_1.out b/expected/limit_offset_pushdown_1.out deleted file mode 100644 index 4983483..0000000 --- a/expected/limit_offset_pushdown_1.out +++ /dev/null @@ -1,398 +0,0 @@ -\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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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 --------------------------------------------------------- - Limit - Output: c1, c2, c3 - -> Foreign Scan on public.fdw131_t1 - Output: c1, c2, c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 -(5 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 4fe31be..c20fa5a 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -16,12 +16,7 @@ #include "mongo_wrapper.h" #include "access/htup_details.h" -#if PG_VERSION_NUM < 120000 -#include "access/sysattr.h" -#endif -#if PG_VERSION_NUM >= 120000 #include "access/table.h" -#endif #include "catalog/heap.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" @@ -36,14 +31,9 @@ #if PG_VERSION_NUM >= 140000 #include "optimizer/appendinfo.h" #endif -#if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" -#endif #include "optimizer/paths.h" #include "optimizer/tlist.h" -#if PG_VERSION_NUM < 120000 -#include "optimizer/var.h" -#endif #include "parser/parsetree.h" #if PG_VERSION_NUM >= 160000 #include "parser/parse_relation.h" @@ -227,12 +217,10 @@ static void mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *grouped_rel, GroupPathExtraData *extra); -#if PG_VERSION_NUM >= 120000 static void mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *final_rel, FinalPathExtraData *extra); -#endif static void mongoEstimateCosts(RelOptInfo *baserel, Cost *startup_cost, Cost *total_cost, Oid foreigntableid); @@ -248,11 +236,9 @@ static void mongo_add_paths_with_pathkeys(PlannerInfo *root, static EquivalenceMember *mongo_find_em_for_rel_target(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel); -#if PG_VERSION_NUM >= 120000 static void mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel); -#endif /* The null action object used for pure validation */ #if PG_VERSION_NUM < 130000 @@ -692,11 +678,7 @@ mongoGetForeignPlan(PlannerInfo *root, if (var->varattno >= 0) continue; -#if PG_VERSION_NUM >= 120000 attr = SystemAttributeDefinition(var->varattno); -#else - attr = SystemAttributeDefinition(var->varattno, false); -#endif ereport(ERROR, (errcode(ERRCODE_FDW_COLUMN_NAME_NOT_FOUND), errmsg("system attribute \"%s\" can't be fetched from remote relation", @@ -2982,7 +2964,6 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, * Create a new join path and add it to the joinrel which represents a * join between foreign tables. */ -#if PG_VERSION_NUM >= 120000 joinpath = create_foreign_join_path(root, joinrel, NULL, @@ -2993,18 +2974,6 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, joinrel->lateral_relids, epq_path, NULL); /* no fdw_private */ -#else - joinpath = create_foreignscan_path(root, - joinrel, - NULL, /* default pathtarget */ - 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); @@ -3511,12 +3480,8 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, return; /* Ignore stages we don't support; and skip any duplicate calls. */ -#if PG_VERSION_NUM >= 120000 if ((stage != UPPERREL_GROUP_AGG && stage != UPPERREL_ORDERED && stage != UPPERREL_FINAL) || -#else - if (stage != UPPERREL_GROUP_AGG || -#endif output_rel->fdw_private) return; @@ -3525,7 +3490,6 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, fpinfo->stage = stage; output_rel->fdw_private = fpinfo; -#if PG_VERSION_NUM >= 120000 switch (stage) { case UPPERREL_GROUP_AGG: @@ -3543,10 +3507,6 @@ mongoGetForeignUpperPaths(PlannerInfo *root, UpperRelationKind stage, elog(ERROR, "unexpected upper relation: %d", (int) stage); break; } -#else - mongo_add_foreign_grouping_paths(root, input_rel, output_rel, - (GroupPathExtraData *) extra); -#endif } /* @@ -3614,7 +3574,6 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, #endif /* Create and add foreign path to the grouping relation. */ -#if PG_VERSION_NUM >= 120000 grouppath = create_foreign_upper_path(root, grouped_rel, grouped_rel->reltarget, @@ -3624,18 +3583,6 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, NIL, /* no pathkeys */ NULL, NIL); /* no fdw_private */ -#else - grouppath = create_foreignscan_path(root, - grouped_rel, - grouped_rel->reltarget, - num_groups, - startup_cost, - total_cost, - NIL, /* no pathkeys */ - grouped_rel->lateral_relids, - NULL, - NIL); /* no fdw_private */ -#endif /* Add generated path into grouped_rel by add_path(). */ add_path(grouped_rel, (Path *) grouppath); @@ -3963,7 +3910,6 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, useful_pathkeys, -1.0); -#if PG_VERSION_NUM >= 120000 if (IS_SIMPLE_REL(rel)) add_path(rel, (Path *) create_foreignscan_path(root, rel, @@ -3986,18 +3932,6 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, rel->lateral_relids, sorted_epq_path, NIL)); -#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)); -#endif } } @@ -4043,7 +3977,6 @@ mongo_find_em_for_rel(PlannerInfo *root, EquivalenceClass *ec, RelOptInfo *rel) * Given input_rel contains the source-data Paths. The paths are added to the * given ordered_rel. */ -#if PG_VERSION_NUM >= 120000 static void mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, RelOptInfo *ordered_rel) @@ -4404,7 +4337,6 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, /* and add it to the final_rel */ add_path(final_rel, (Path *) final_path); } -#endif /* PG_VERSION_NUM >= 120000 */ /* * mongo_find_em_for_rel_target diff --git a/mongo_fdw.h b/mongo_fdw.h index 0dc1eb1..4f615bc 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -31,9 +31,6 @@ #include "foreign/foreign.h" #include "nodes/makefuncs.h" #include "nodes/pg_list.h" -#if PG_VERSION_NUM < 120000 -#include "nodes/relation.h" -#endif #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/plancat.h" diff --git a/mongo_query.c b/mongo_query.c index 89bbd80..56bc540 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -18,13 +18,8 @@ #include #include -#if PG_VERSION_NUM < 120000 -#include "access/sysattr.h" -#endif #include "access/htup_details.h" -#if PG_VERSION_NUM >= 120000 #include "access/table.h" -#endif #include "catalog/heap.h" #include "catalog/pg_collation.h" #include "catalog/pg_operator.h" @@ -33,13 +28,7 @@ #endif #include "mongoc.h" #include "mongo_query.h" -#if PG_VERSION_NUM < 120000 -#include "nodes/relation.h" -#include "optimizer/var.h" -#endif -#if PG_VERSION_NUM >= 120000 #include "optimizer/optimizer.h" -#endif #include "parser/parsetree.h" #include "utils/rel.h" #include "utils/syscache.h" From 11921b333bbd72a32d2f79812a38c6068f9c5de7 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 24 Jun 2024 18:36:08 +0530 Subject: [PATCH 222/239] Add support for PostgreSQL 17 and EDB Postgres Advanced Server 17. Code changes involve adjusting calls to some server-side functions due to their signature changes. FDW-682, Suraj Kharage, reviewed by Jeevan Chalke. --- Makefile | 4 +- README.md | 2 +- expected/aggregate_pushdown_2.out | 1997 ++++++++++++++++++++++++ expected/join_pushdown_2.out | 2127 ++++++++++++++++++++++++++ expected/limit_offset_pushdown_1.out | 380 +++++ mongo_fdw.c | 151 +- 6 files changed, 4653 insertions(+), 8 deletions(-) create mode 100644 expected/aggregate_pushdown_2.out create mode 100644 expected/join_pushdown_2.out create mode 100644 expected/limit_offset_pushdown_1.out diff --git a/Makefile b/Makefile index f0e63d9..26dc848 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,6 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 12 13 14 15 16)) - $(error PostgreSQL 12, 13, 14, 15, or 16 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 12 13 14 15 16 17)) + $(error PostgreSQL 12, 13, 14, 15, 16, or 17 is required to compile this extension) endif diff --git a/README.md b/README.md index c403d94..b71c90f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 12, 13, 14, 15 and 16. +Postgres Advanced Server 12, 13, 14, 15, 16 and 17. PostgreSQL + MongoDB diff --git a/expected/aggregate_pushdown_2.out b/expected/aggregate_pushdown_2.out new file mode 100644 index 0000000..e968598 --- /dev/null +++ b/expected/aggregate_pushdown_2.out @@ -0,0 +1,1997 @@ +\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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out new file mode 100644 index 0000000..c664f77 --- /dev/null +++ b/expected/join_pushdown_2.out @@ -0,0 +1,2127 @@ +\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'); +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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + 30 | SALES | 200 | EMP2 | 1600 | 30 + 30 | SALES | 300 | EMP3 | 1250 | 30 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 + 30 | SALES | 500 | EMP5 | 1250.23 | 30 + 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 + 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + 30 | SALES | 200 | EMP2 | 1600 | 30 + 30 | SALES | 300 | EMP3 | 1250 | 30 + 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 + 30 | SALES | 500 | EMP5 | 1250.23 | 30 + 30 | SALES | 600 | EMP6 | 2850 | 30 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 + 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 30 | SALES | 1000 | EMP10 | 1500 | 30 + 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 + 30 | SALES | 1200 | EMP12 | 950 | 30 + 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 + 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + | | 1500 | EMP15 | 950 | 60 + | | 1600 | EMP16 | | +(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 +----+----------------+------+-------+---------+---- + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 + | | 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 | | +(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 | 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 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. +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) + +-- Don't pushdown when full document retrieval is involved. +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) + +-- 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 | 10 + 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 | 10 + 100 | 20 + 100 | 30 +(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 +-- Table alias in foreign scan is different for v12, v11 and v10. +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) + +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; +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 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 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; diff --git a/expected/limit_offset_pushdown_1.out b/expected/limit_offset_pushdown_1.out new file mode 100644 index 0000000..e349f50 --- /dev/null +++ b/expected/limit_offset_pushdown_1.out @@ -0,0 +1,380 @@ +\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; diff --git a/mongo_fdw.c b/mongo_fdw.c index c20fa5a..5e5d66b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -228,11 +228,20 @@ 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); @@ -564,6 +573,7 @@ mongoGetForeignPaths(PlannerInfo *root, } /* Create a foreign path node */ +#if PG_VERSION_NUM >= 170000 foreignPath = (Path *) create_foreignscan_path(root, baserel, NULL, /* default pathtarget */ baserel->rows, @@ -572,13 +582,30 @@ mongoGetForeignPaths(PlannerInfo *root, NIL, /* no pathkeys */ baserel->lateral_relids, NULL, /* no extra plan */ - NULL); /* no fdw_private data */ + 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 } /* @@ -2088,7 +2115,11 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, 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); @@ -2555,7 +2586,11 @@ column_value(BSON_ITERATOR *bsonIterator, Oid columnTypeId, 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); } @@ -2964,6 +2999,7 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, * Create a new join path and add it to the joinrel which represents a * join between foreign tables. */ +#if PG_VERSION_NUM >= 170000 joinpath = create_foreign_join_path(root, joinrel, NULL, @@ -2973,14 +3009,32 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, NIL, /* no pathkeys */ joinrel->lateral_relids, epq_path, - NULL); /* no fdw_private */ + 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 */ } @@ -3574,6 +3628,18 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, #endif /* Create and add foreign path to the grouping relation. */ +#if 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, @@ -3583,6 +3649,7 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, NIL, /* no pathkeys */ NULL, NIL); /* no fdw_private */ +#endif /* Add generated path into grouped_rel by add_path(). */ add_path(grouped_rel, (Path *) grouppath); @@ -3861,10 +3928,17 @@ mongo_get_useful_pathkeys_for_relation(PlannerInfo *root, RelOptInfo *rel) * 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 */ @@ -3911,6 +3985,19 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, -1.0); if (IS_SIMPLE_REL(rel)) +#if 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, @@ -3920,8 +4007,10 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, useful_pathkeys, rel->lateral_relids, sorted_epq_path, - NIL)); + NIL)); /* no fdw_private list */ +#endif else +#if PG_VERSION_NUM >= 170000 add_path(rel, (Path *) create_foreign_join_path(root, rel, NULL, @@ -3931,7 +4020,20 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, useful_pathkeys, rel->lateral_relids, sorted_epq_path, - NIL)); + 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 } } @@ -4098,6 +4200,7 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, fdw_private = list_make2(makeInteger(true), makeInteger(false)); /* Create foreign ordering path */ +#if PG_VERSION_NUM >= 170000 ordered_path = create_foreign_upper_path(root, input_rel, root->upper_targets[UPPERREL_ORDERED], @@ -4106,7 +4209,19 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, 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); @@ -4201,6 +4316,18 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, * no-longer-needed outer plan (if any), which makes the * EXPLAIN output look cleaner */ +#if 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, @@ -4209,7 +4336,8 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, path->total_cost, path->pathkeys, NULL, /* no extra plan */ - NULL); /* no fdw_private */ + NIL); /* no fdw_private */ +#endif /* and add it to the final_rel */ add_path(final_rel, (Path *) final_path); @@ -4324,6 +4452,7 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, * 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 >= 170000 final_path = create_foreign_upper_path(root, input_rel, root->upper_targets[UPPERREL_FINAL], @@ -4332,7 +4461,19 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, 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); From 5396a8b64a72dae1803d2fabe7151460e2c1ac4a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 4 Jul 2024 19:16:05 +0530 Subject: [PATCH 223/239] Update README --- README.md | 66 +++++++++++++++++-------------------------------------- 1 file changed, 20 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index b71c90f..2f048cf 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,6 @@ This PostgreSQL extension implements a Foreign Data Wrapper (FDW) for Please note that this version of mongo_fdw works with PostgreSQL and EDB Postgres Advanced Server 12, 13, 14, 15, 16 and 17. -PostgreSQL + MongoDB - Contents -------- @@ -17,13 +15,14 @@ Contents 3. [Installation](#installation) 4. [Usage](#usage) 5. [Functions](#functions) -6. [Identifier case handling](#identifier-case-handling) -7. [Generated columns](#generated-columns) -8. [Character set handling](#character-set-handling) -9. [Examples](#examples) -10. [Limitations](#limitations) -11. [Contributing](#contributing) -12. [Useful links](#useful-links) +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 -------- @@ -251,33 +250,9 @@ Functions As well as the standard `mongo_fdw_handler()` and `mongo_fdw_validator()` functions, `mongo_fdw` provides the following user-callable utility functions: -**Yet not described!**. - -Identifier case handling ------------------------- - -PostgreSQL folds identifiers to lower case by default, MongoDB use JSON notation of identifiers. - -All transformation rules and problems **yet not described**. - -Generated columns ------------------ - -`mongo_fdw` doesn't provides support for PostgreSQL's generated -columns (PostgreSQL 12+). - -**Behaviour with generated columns yet not tested and not described**. +- **mongo_fdw_version()** -Note that while `mongo_fdw` will `INSERT` or `UPDATE` the generated column value -in MongoDB, there is nothing to stop the value being modified within MongoDB, -and hence no guarantee that in subsequent `SELECT` operations the column will -still contain the expected generated value. This limitation also applies to -`postgres_fdw`. - -For more details on generated columns see: - -- [Generated Columns](https://www.postgresql.org/docs/current/ddl-generated-columns.html) -- [CREATE FOREIGN TABLE](https://www.postgresql.org/docs/current/sql-createforeigntable.html) + Returns the version number as an integer. Character set handling ---------------------- @@ -285,8 +260,6 @@ 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. -Encodings mapping between PostgreSQL and MongoDB **yet not described**. - Examples -------- @@ -462,6 +435,16 @@ 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 ------------ @@ -488,15 +471,6 @@ Reference FDW realization, `postgres_fdw` - https://wiki.postgresql.org/wiki/Fdw - https://pgxn.org/tag/fdw/ -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`. - License ------- From 123644068d68223edfa7261af6c43a46c51a1d80 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 11 Jul 2024 10:57:29 +0530 Subject: [PATCH 224/239] Stamp 5.5.2. --- expected/select.out | 2 +- mongo_fdw.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expected/select.out b/expected/select.out index 8bc9883..c6cd495 100644 --- a/expected/select.out +++ b/expected/select.out @@ -14,7 +14,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50501 + 50502 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index 5e5d66b..934ad06 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -56,9 +56,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.5.1 so number will be 50501 + * our version is 5.5.2 so number will be 50502 */ -#define CODE_VERSION 50501 +#define CODE_VERSION 50502 /* * Macro to check unsupported sorting methods. Currently, ASC NULLS FIRST and From f2c5c399e022e93cfa4a139967ad3e7d6c244b9b Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Thu, 22 Aug 2024 17:50:51 +0530 Subject: [PATCH 225/239] Support compiling from the contrib directory. This allows building the extension from the source's contrib directory without the need for pg_config availability in the path. Jeevan Chalke. --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 26dc848..a232f26 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,7 @@ 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 @@ -45,3 +46,10 @@ endif ifeq (,$(findstring $(MAJORVERSION), 12 13 14 15 16 17)) $(error PostgreSQL 12, 13, 14, 15, 16, or 17 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 From d7bba7d9c8c90b879cad28c59de3f7c84a1fae0a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 20 Dec 2024 10:43:04 +0530 Subject: [PATCH 226/239] Fix compiler warnings showing up when compiled with flag -O2. FDW-709, Vaibhav Dalvi, tested by Kashif Zeeshan. --- mongo_fdw.c | 8 ++++---- mongo_query.c | 25 +++++++++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index 934ad06..80f0ae0 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -496,8 +496,8 @@ mongoGetForeignPaths(PlannerInfo *root, { Path *foreignPath; MongoFdwOptions *options; - Cost startupCost; - Cost totalCost; + Cost startupCost = 0; + Cost totalCost = 0; /* Fetch options */ options = mongo_get_options(foreigntableid); @@ -639,7 +639,7 @@ mongoGetForeignPlan(PlannerInfo *root, List *quals = NIL; MongoFdwRelType mongofdwreltype; MongoRelQualInfo *qual_info; - MongoFdwRelationInfo *ofpinfo; + MongoFdwRelationInfo *ofpinfo = NULL; List *pathKeyList = NIL; List *isAscSortList = NIL; bool has_final_sort = false; @@ -2177,7 +2177,7 @@ fill_tuple_slot(const BSON *bsonDocument, const char *bsonDocumentKey, Oid columnArrayTypeId = InvalidOid; bool compatibleTypes = false; const char *bsonFullKey; - int32 attnum; + int32 attnum = 0; bool is_agg = false; if (!strncmp(bsonKey, "AGG_RESULT_KEY", 5) && bsonType == BSON_TYPE_INT32) diff --git a/mongo_query.c b/mongo_query.c index 56bc540..830cfec 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -197,18 +197,17 @@ mongo_query_document(ForeignScanState *scanStateNode) BSON root_pipeline; BSON match_stage; int root_index = 0; - List *joinclauses; + 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; - char *outer_relname; + char *inner_relname = NULL; + char *outer_relname = NULL; HTAB *columnInfoHash; - int jointype; - int natts; + int jointype = 0; bool has_limit; /* Retrieve data passed by planning phase */ @@ -217,10 +216,16 @@ mongo_query_document(ForeignScanState *scanStateNode) rti_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseRtiList); isouter_list = list_nth(PrivateList, mongoFdwPrivateJoinClauseIsOuterList); - /* Length should be same for all lists of column information */ - natts = list_length(colname_list); - Assert(natts == list_length(colnum_list) && natts == list_length(rti_list) - && natts == list_length(isouter_list)); +#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, @@ -998,7 +1003,7 @@ mongo_get_column_list(PlannerInfo *root, RelOptInfo *foreignrel, ListCell *lc; RelOptInfo *scanrel; MongoFdwRelationInfo *fpinfo = (MongoFdwRelationInfo *) foreignrel->fdw_private; - MongoFdwRelationInfo *ofpinfo; + MongoFdwRelationInfo *ofpinfo = NULL; scanrel = IS_UPPER_REL(foreignrel) ? fpinfo->outerrel : foreignrel; From 6d5c2ea7d22762e3dbe35e23a9371f99a6710086 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 20 Dec 2024 10:43:04 +0530 Subject: [PATCH 227/239] Fix variable name in query pipeline used for sub-column. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable name we use to declare the column of outer relation using the $let field is the same as the column name. However, if the column is a sub-column (i.e., "parent.child"), then the variable name also contains the dot ("."), which is not allowed in variable name formation, resulting in a wrong resultset being returned from the server. Since special characters other than underscore ("_") are not allowed, let's use underscore instead of dot to fix the issue. Reported on GitHub through issue #173 by Ömer Sezgin Uğurlu (ugurlu). FDW-669, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan. --- data/mongo_testdevice.json | 10 ++++++++ data/mongo_testlog.json | 14 +++++++++++ deparse.c | 6 +++-- expected/select.out | 16 ++++++++++++ mongo_query.c | 50 ++++++++++++++++++++++++++++++++------ mongo_query.h | 2 ++ mongodb_init.sh | 2 ++ sql/select.sql | 12 +++++++++ 8 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 data/mongo_testdevice.json create mode 100644 data/mongo_testlog.json diff --git a/data/mongo_testdevice.json b/data/mongo_testdevice.json new file mode 100644 index 0000000..6c2a598 --- /dev/null +++ b/data/mongo_testdevice.json @@ -0,0 +1,10 @@ +[ +{ + "_id": { + "$oid": "6580400c4898199d6e0173cd" + }, + "mac": "001122334455", + "name": "test device", + "level": 3 +} +] diff --git a/data/mongo_testlog.json b/data/mongo_testlog.json new file mode 100644 index 0000000..65760c1 --- /dev/null +++ b/data/mongo_testlog.json @@ -0,0 +1,14 @@ +[ +{ + "_id": { + "$oid": "658040214898199d6e0173d0" + }, + "log": "hello log", + "logMeta": { + "logMac": "001122334455", + "nestMore": { + "level": 3 + } + } +} +] diff --git a/deparse.c b/deparse.c index 0cc6115..1c1d307 100644 --- a/deparse.c +++ b/deparse.c @@ -567,7 +567,8 @@ mongo_append_column_name(Var *column, BSON *child_doc, pipeline_cxt *context) return; if (columnInfo->isOuter && context->isJoinClause) - field = psprintf("$$v_%s", columnInfo->colName); + field = psprintf("$$%s", + get_varname_for_outer_col(columnInfo->colName)); else field = psprintf("$%s", columnInfo->colName); @@ -599,7 +600,8 @@ mongo_add_null_check(Var *column, BSON *expr, pipeline_cxt *context) return; if (columnInfo->isOuter && context->isJoinClause) - field = psprintf("$$v_%s", columnInfo->colName); + field = psprintf("$$%s", + get_varname_for_outer_col(columnInfo->colName)); else field = psprintf("$%s", columnInfo->colName); diff --git a/expected/select.out b/expected/select.out index c6cd495..073a77b 100644 --- a/expected/select.out +++ b/expected/select.out @@ -48,6 +48,10 @@ 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 @@ -1381,6 +1385,16 @@ SELECT 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; @@ -1407,6 +1421,8 @@ 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; diff --git a/mongo_query.c b/mongo_query.c index 830cfec..1a7595a 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -314,13 +314,8 @@ mongo_query_document(ForeignScanState *scanStateNode) */ if (is_outer && strcmp(colname, "*") != 0) { - /* - * 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. - */ - char *varname = psprintf("v_%s", colname); + char *varname = psprintf("%s", + get_varname_for_outer_col(colname)); char *field = psprintf("$%s", colname); bsonAppendUTF8(&let_exprs, varname, field); @@ -1816,3 +1811,44 @@ mongo_append_unique_var(List *varlist, Var *var) 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[66]; + + /* + * 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. + */ + sprintf(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; +} diff --git a/mongo_query.h b/mongo_query.h index 8d6540a..0658f67 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -144,5 +144,7 @@ extern void mongo_append_expr(Expr *node, BSON *child_doc, 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 */ diff --git a/mongodb_init.sh b/mongodb_init.sh index 358d929..005707c 100755 --- a/mongodb_init.sh +++ b/mongodb_init.sh @@ -16,4 +16,6 @@ export MONGO_PWD="edb" 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 mongo --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --authenticationDatabase "mongo_fdw_regress" < data/mongo_test_data.js > /dev/null diff --git a/sql/select.sql b/sql/select.sql index 5e07dc6..13c2a7c 100644 --- a/sql/select.sql +++ b/sql/select.sql @@ -47,6 +47,10 @@ 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; @@ -356,6 +360,12 @@ 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; @@ -382,6 +392,8 @@ 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; From cc848d10eaca7c7a9bf4b8f0e0225fc447165c2d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 18 Mar 2025 08:47:58 +0530 Subject: [PATCH 228/239] Update EDB copyrights for 2025. Vaibhav Dalvi. --- Makefile | 2 +- README.md | 2 +- autogen.sh | 2 +- connection.c | 2 +- deparse.c | 2 +- mongo_fdw--1.0.sql | 2 +- mongo_fdw--1.1.sql | 2 +- mongo_fdw.c | 2 +- mongo_fdw.control | 2 +- mongo_fdw.h | 2 +- mongo_query.c | 2 +- mongo_query.h | 2 +- mongo_wrapper.c | 2 +- mongo_wrapper.h | 2 +- option.c | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index a232f26..ce92c21 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # mongo_fdw/Makefile # -# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # diff --git a/README.md b/README.md index 2f048cf..63d5e62 100644 --- a/README.md +++ b/README.md @@ -474,7 +474,7 @@ Reference FDW realization, `postgres_fdw` License ------- -Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +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 diff --git a/autogen.sh b/autogen.sh index 459ae8c..9a8a578 100755 --- a/autogen.sh +++ b/autogen.sh @@ -6,7 +6,7 @@ # Foreign-data wrapper for remote MongoDB servers # # Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group -# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. # # IDENTIFICATION # autogen.sh diff --git a/connection.c b/connection.c index b35bd35..8387124 100644 --- a/connection.c +++ b/connection.c @@ -4,7 +4,7 @@ * Connection management functions for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/deparse.c b/deparse.c index 1c1d307..36998b9 100644 --- a/deparse.c +++ b/deparse.c @@ -4,7 +4,7 @@ * Query deparser for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw--1.0.sql b/mongo_fdw--1.0.sql index 12f3753..bfdefe5 100644 --- a/mongo_fdw--1.0.sql +++ b/mongo_fdw--1.0.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.0.sql */ --- Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +-- 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 diff --git a/mongo_fdw--1.1.sql b/mongo_fdw--1.1.sql index 47ec3c2..a23e102 100644 --- a/mongo_fdw--1.1.sql +++ b/mongo_fdw--1.1.sql @@ -1,6 +1,6 @@ /* mongo_fdw/mongo_fdw--1.1.sql */ --- Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +-- 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 diff --git a/mongo_fdw.c b/mongo_fdw.c index 80f0ae0..2054422 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_fdw.control b/mongo_fdw.control index 311c587..d91e62e 100644 --- a/mongo_fdw.control +++ b/mongo_fdw.control @@ -1,6 +1,6 @@ # mongo_fdw extension # -# Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. +# Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. # Portions Copyright © 2012–2014 Citus Data, Inc. # comment = 'foreign data wrapper for MongoDB access' diff --git a/mongo_fdw.h b/mongo_fdw.h index 4f615bc..dd9adca 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -4,7 +4,7 @@ * Foreign-data wrapper for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.c b/mongo_query.c index 1a7595a..3d7ceec 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_query.h b/mongo_query.h index 0658f67..2314b39 100644 --- a/mongo_query.h +++ b/mongo_query.h @@ -4,7 +4,7 @@ * FDW query handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 69ad65e..b3a2441 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/mongo_wrapper.h b/mongo_wrapper.h index 30efe7f..326ed3c 100644 --- a/mongo_wrapper.h +++ b/mongo_wrapper.h @@ -4,7 +4,7 @@ * Wrapper functions for remote MongoDB servers * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION diff --git a/option.c b/option.c index 0dba5d4..18979d3 100644 --- a/option.c +++ b/option.c @@ -4,7 +4,7 @@ * FDW option handling for mongo_fdw * * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group - * Portions Copyright (c) 2004-2024, EnterpriseDB Corporation. + * Portions Copyright (c) 2004-2025, EnterpriseDB Corporation. * Portions Copyright (c) 2012–2014 Citus Data, Inc. * * IDENTIFICATION From acc9542b5e5d710893a1e957f43867944ddd7537 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 May 2025 12:08:32 +0530 Subject: [PATCH 229/239] Drop support for v12. Standard support has ended for EDB Postgres Advanced Server 12, Postgres Extended Server 12, and PostgreSQL 12. This commit removes compatibility with v12 by updating the Makefile to prevent building mongo_fdw against it. Corresponding updates are made to the README, and obsolete code paths related to v12 are cleaned up. FDW-716, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajaykumar Pal. --- Makefile | 4 +- README.md | 2 +- connection.c | 2 - deparse.c | 2 - expected/aggregate_pushdown_1.out | 1988 --------------------------- expected/join_pushdown.out | 1 - expected/join_pushdown_1.out | 2093 ----------------------------- expected/join_pushdown_2.out | 1 - expected/join_pushdown_3.out | 1 - mongo_fdw.c | 22 - mongo_fdw.h | 4 - mongo_query.c | 14 - sql/join_pushdown.sql | 1 - 13 files changed, 3 insertions(+), 4132 deletions(-) delete mode 100644 expected/aggregate_pushdown_1.out delete mode 100644 expected/join_pushdown_1.out diff --git a/Makefile b/Makefile index ce92c21..c8893df 100644 --- a/Makefile +++ b/Makefile @@ -43,8 +43,8 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 12 13 14 15 16 17)) - $(error PostgreSQL 12, 13, 14, 15, 16, or 17 is required to compile this extension) +ifeq (,$(findstring $(MAJORVERSION), 13 14 15 16 17)) + $(error PostgreSQL 13, 14, 15, 16, or 17 is required to compile this extension) endif else diff --git a/README.md b/README.md index 63d5e62..8e342d5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 12, 13, 14, 15, 16 and 17. +Postgres Advanced Server 13, 14, 15, 16 and 17. Contents -------- diff --git a/connection.c b/connection.c index 8387124..815a635 100644 --- a/connection.c +++ b/connection.c @@ -15,9 +15,7 @@ #include "postgres.h" #include "access/xact.h" -#if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" -#endif #include "mongo_wrapper.h" #include "utils/inval.h" #include "utils/syscache.h" diff --git a/deparse.c b/deparse.c index 36998b9..03b978b 100644 --- a/deparse.c +++ b/deparse.c @@ -20,9 +20,7 @@ #include "access/htup_details.h" #include "catalog/pg_operator.h" -#if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" -#endif #include "mongoc.h" #include "mongo_query.h" #include "optimizer/optimizer.h" diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out deleted file mode 100644 index a96eab8..0000000 --- a/expected/aggregate_pushdown_1.out +++ /dev/null @@ -1,1988 +0,0 @@ -\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: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) - Sort Key: (sum(ftprt1_p1.c1)) - -> Append - -> Foreign Scan - Output: ftprt1_p1.c1, (sum(ftprt1_p1.c1)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) - -> Foreign Scan - Output: ftprt1_p2.c1, (sum(ftprt1_p2.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: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) - Sort Key: (sum(ftprt1_p1.c2)) - -> Append - -> Foreign Scan - Output: ftprt1_p1.c1, (sum(ftprt1_p1.c2)), (min(ftprt1_p1.c2)), (count(*)) - Foreign Namespace: Aggregate on (mongo_fdw_regress.test1 fprt1) - -> Foreign Scan - Output: ftprt1_p2.c1, (sum(ftprt1_p2.c2)), (min(ftprt1_p2.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; diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 3fa1e39..fcebbb4 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -1649,7 +1649,6 @@ SELECT t1.c1, t1.c2 (4 rows) -- With PHVs, partitionwise join selected but no join pushdown --- Table alias in foreign scan is different for v12, v11 and v10. 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 diff --git a/expected/join_pushdown_1.out b/expected/join_pushdown_1.out deleted file mode 100644 index 5115d36..0000000 --- a/expected/join_pushdown_1.out +++ /dev/null @@ -1,2093 +0,0 @@ -\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'); -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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - 30 | SALES | 200 | EMP2 | 1600 | 30 - 30 | SALES | 300 | EMP3 | 1250 | 30 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 - 30 | SALES | 500 | EMP5 | 1250.23 | 30 - 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 - 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 - 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | -(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 -----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 - | | 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 | | -(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 | 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 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. -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) - --- Don't pushdown when full document retrieval is involved. -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) - --- 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 | 10 - 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 | 10 - 100 | 20 - 100 | 30 -(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.c1, t2.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Foreign Scan - Output: t1_1.c1, t2_1.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.c1, t2.c2, t3.c2 - Hash Cond: (t1.c1 = t3.c1) - -> Foreign Scan - Output: t1.c1, t2.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Hash - Output: t3.c2, t3.c1 - -> Foreign Scan on public.ftprt1_p1 t3 - Output: t3.c2, t3.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> 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.test2 t1) INNER JOIN (mongo_fdw_regress.test4 t2) - -> Hash - Output: t3_1.c2, t3_1.c1 - -> Foreign Scan on public.ftprt1_p2 t3_1 - Output: t3_1.c2, t3_1.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.c1, t1.c2 - Foreign Namespace: (mongo_fdw_regress.test1 t1) INNER JOIN (mongo_fdw_regress.test3 t2) - -> Foreign Scan - Output: t1_1.c1, t1_1.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 --- Table alias in foreign scan is different for v12, v11 and v10. -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 ------------------------------------------------------------------------------------- - Sort - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Sort Key: ftprt1_p1.c1, ftprt2_p1.c2 - -> Append - -> Hash Left Join - Output: ftprt1_p1.c1, 't1_phv'::text, ftprt2_p1.c2, ('t2_phv'::text) - Hash Cond: (ftprt1_p1.c1 = ftprt2_p1.c2) - -> Foreign Scan on public.ftprt1_p1 - Output: ftprt1_p1.c1 - Foreign Namespace: mongo_fdw_regress.test1 - -> Hash - Output: ftprt2_p1.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p1 - Output: ftprt2_p1.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test3 - -> Hash Left Join - Output: ftprt1_p2.c1, 't1_phv'::text, ftprt2_p2.c2, ('t2_phv'::text) - Hash Cond: (ftprt1_p2.c1 = ftprt2_p2.c2) - -> Foreign Scan on public.ftprt1_p2 - Output: ftprt1_p2.c1 - Foreign Namespace: mongo_fdw_regress.test2 - -> Hash - Output: ftprt2_p2.c2, ('t2_phv'::text) - -> Foreign Scan on public.ftprt2_p2 - Output: ftprt2_p2.c2, 't2_phv'::text - Foreign Namespace: mongo_fdw_regress.test4 -(26 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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 --------------------------------------------------------------------- - Sort - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Sort Key: d.c1, e.c1 - -> Hash Join - Output: d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 - Hash Cond: (d.c1 = e.c8) - -> Foreign Scan on public.f_test_tbl2 d - Output: d._id, d.c1, d.c2, d.c3 - Foreign Namespace: mongo_fdw_regress.test_tbl2 - -> Hash - Output: e.c1, e.c2, e.c6, 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 -(14 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) - -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; -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 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 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index c664f77..d560bf6 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -1649,7 +1649,6 @@ SELECT t1.c1, t1.c2 (4 rows) -- With PHVs, partitionwise join selected but no join pushdown --- Table alias in foreign scan is different for v12, v11 and v10. 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 diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 096ef91..d9c3677 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -1649,7 +1649,6 @@ SELECT t1.c1, t1.c2 (4 rows) -- With PHVs, partitionwise join selected but no join pushdown --- Table alias in foreign scan is different for v12, v11 and v10. 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 diff --git a/mongo_fdw.c b/mongo_fdw.c index 2054422..464643e 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -20,10 +20,8 @@ #include "catalog/heap.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" -#if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" #include "common/jsonapi.h" -#endif #include "miscadmin.h" #include "mongo_fdw.h" #include "mongo_query.h" @@ -41,11 +39,7 @@ #include "storage/ipc.h" #include "utils/guc.h" #include "utils/jsonb.h" -#if PG_VERSION_NUM < 130000 -#include "utils/jsonapi.h" -#else #include "utils/jsonfuncs.h" -#endif #include "utils/rel.h" #include "utils/selfuncs.h" #include "utils/syscache.h" @@ -250,19 +244,11 @@ static void mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *ordered_rel); /* The null action object used for pure validation */ -#if PG_VERSION_NUM < 130000 -static 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 @@ -1417,11 +1403,7 @@ mongoPlanForeignModify(PlannerInfo *root, * Core code already has some lock on each rel being planned, so we can * use NoLock here. */ -#if PG_VERSION_NUM < 130000 - rel = heap_open(rte->relid, NoLock); -#else rel = table_open(rte->relid, NoLock); -#endif if (operation == CMD_INSERT) { TupleDesc tupdesc = RelationGetDescr(rel); @@ -1487,11 +1469,7 @@ mongoPlanForeignModify(PlannerInfo *root, if (plan->returningLists) elog(ERROR, "RETURNING is not supported by this FDW"); -#if PG_VERSION_NUM < 130000 - heap_close(rel, NoLock); -#else table_close(rel, NoLock); -#endif return list_make1(targetAttrs); } diff --git a/mongo_fdw.h b/mongo_fdw.h index dd9adca..a957d99 100644 --- a/mongo_fdw.h +++ b/mongo_fdw.h @@ -125,11 +125,7 @@ #define POSTGRES_TO_UNIX_EPOCH_USECS (POSTGRES_TO_UNIX_EPOCH_DAYS * USECS_PER_DAY) /* Macro for list API backporting. */ -#if PG_VERSION_NUM < 130000 -#define mongo_list_concat(l1, l2) list_concat(l1, list_copy(l2)) -#else #define mongo_list_concat(l1, l2) list_concat((l1), (l2)) -#endif /* Macro for hard-coded aggregation result key */ #define AGG_RESULT_KEY "v_agg" diff --git a/mongo_query.c b/mongo_query.c index 3d7ceec..46d4a45 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -23,9 +23,7 @@ #include "catalog/heap.h" #include "catalog/pg_collation.h" #include "catalog/pg_operator.h" -#if PG_VERSION_NUM >= 130000 #include "common/hashfn.h" -#endif #include "mongoc.h" #include "mongo_query.h" #include "optimizer/optimizer.h" @@ -1542,11 +1540,7 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) Assert(OidIsValid(relid)); /* Planner must have taken a lock, so request no lock here */ -#if PG_VERSION_NUM < 130000 - relation = heap_open(relid, NoLock); -#else relation = table_open(relid, NoLock); -#endif tupdesc = RelationGetDescr(relation); @@ -1575,11 +1569,7 @@ prepare_var_list_for_baserel(Oid relid, Index varno, Bitmapset *attrs_used) } } -#if PG_VERSION_NUM < 130000 - heap_close(relation, NoLock); -#else table_close(relation, NoLock); -#endif return tlist; } @@ -1638,11 +1628,7 @@ column_info_hash(List *colname_list, List *colnum_list, List *rti_list, columnInfo->colName = columnName; columnInfo->isOuter = isOuter; -#if PG_VERSION_NUM >= 130000 l4 = lnext(isouter_list, l4); -#else - l4 = lnext(l4); -#endif } return columnInfoHash; diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 4cd4cdd..36bdea6 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -445,7 +445,6 @@ SELECT t1.c1, t1.c2 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 --- Table alias in foreign scan is different for v12, v11 and v10. 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 From abdfffe530aba372af79aeba9f0d64ac9868ab60 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 May 2025 14:57:33 +0530 Subject: [PATCH 230/239] Fix incorrect appending of WHERE clause in mongo_query_document Pushable WHERE clause expressions were incorrectly added to the MongoDB query pipeline using bsonAppend[Start/Finish]Array, leading to the warning: "bson_append_array(): invalid array detected". This commit corrects the placement of the filter clause in the pipeline to avoid the error and ensure proper query construction. Reported on GitHub through issue #188 by Chris Williams (C-Williams). DB-720, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajaykumar Pal. --- mongo_query.c | 52 +++++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/mongo_query.c b/mongo_query.c index 46d4a45..dab2eab 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -187,7 +187,6 @@ mongo_query_document(ForeignScanState *scanStateNode) { ForeignScan *fsplan = (ForeignScan *) scanStateNode->ss.ps.plan; BSON *queryDocument = bsonCreate(); - BSON *filter = bsonCreate(); List *PrivateList = fsplan->fdw_private; List *opExpressionList = list_nth(PrivateList, mongoFdwPrivateRemoteExprList); @@ -253,28 +252,6 @@ mongo_query_document(ForeignScanState *scanStateNode) /* Prepare array of stages */ bsonAppendStartArray(queryDocument, "pipeline", &root_pipeline); - /* - * 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; - - bsonAppendStartArray(filter, "pipeline", &match_stage); - - /* Form equivalent WHERE clauses in MongoDB */ - mongo_prepare_pipeline(opExpressionList, &match_stage, &context); - - bsonAppendFinishArray(filter, &match_stage); - } - if (fmstate->relType == JOIN_REL || fmstate->relType == UPPER_JOIN_REL) { BSON inner_pipeline; @@ -345,10 +322,6 @@ mongo_query_document(ForeignScanState *scanStateNode) bsonAppendFinishObject(&lookup_object, &lookup); bsonAppendFinishObject(&root_pipeline, &lookup_object); - /* $match stage. This is to add a filter */ - if (opExpressionList) - bsonAppendBson(&root_pipeline, "$match", &match_stage); - /* * $unwind stage. This deconstructs an array field from the input * documents to output a document for each element. @@ -366,8 +339,29 @@ mongo_query_document(ForeignScanState *scanStateNode) fmstate->outerRelName = outer_relname; } - else if (opExpressionList) - bsonAppendBson(&root_pipeline, "$match", &match_stage); + + /* + * 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) From 9cd07d8d01b8bd63b2d99dca93c1fa79c4f43431 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 May 2025 19:02:06 +0530 Subject: [PATCH 231/239] Fix ORDER BY pushdown on columns from inner relation in join In join queries, the result is correctly labeled as "Join_Result". However, while constructing the sort pipeline for the ORDER BY clause, the column reference mistakenly used "Join_result" (with incorrect casing). This mismatch prevented the expected row ordering. This commit corrects the name usage to ensure proper ORDER BY pushdown. DB-721, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Ajaykumar Pal. --- expected/join_pushdown.out | 71 ++++++++++++++++++++++++------------ expected/join_pushdown_2.out | 71 ++++++++++++++++++++++++------------ expected/join_pushdown_3.out | 71 ++++++++++++++++++++++++------------ mongo_query.c | 4 +- sql/join_pushdown.sql | 20 ++++++++++ 5 files changed, 166 insertions(+), 71 deletions(-) diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index fcebbb4..6c85a04 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -79,22 +79,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -242,22 +242,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -381,7 +381,6 @@ 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 ----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 | | 200 | EMP2 | 1600 | 30 | | 300 | EMP3 | 1250 | 30 | | 400 | EMP4 | 2975 | 20 @@ -397,6 +396,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 (16 rows) -- Push INNER JOIN. @@ -779,9 +779,9 @@ 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 | 700 | EMP7 | 2450.34 | 10 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 (3 rows) EXPLAIN (COSTS OFF) @@ -1364,7 +1364,7 @@ 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 | 10 + 100 | 20 100 | 30 (2 rows) @@ -1443,9 +1443,9 @@ 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 - 100 | 30 (3 rows) -- Limit with -ve value. Shouldn't pushdown. @@ -2098,10 +2098,33 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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; @@ -2113,6 +2136,8 @@ 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index d560bf6..a718b92 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -79,22 +79,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -242,22 +242,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -381,7 +381,6 @@ 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 ----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 | | 200 | EMP2 | 1600 | 30 | | 300 | EMP3 | 1250 | 30 | | 400 | EMP4 | 2975 | 20 @@ -397,6 +396,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 (16 rows) -- Push INNER JOIN. @@ -779,9 +779,9 @@ 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 | 700 | EMP7 | 2450.34 | 10 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 (3 rows) EXPLAIN (COSTS OFF) @@ -1364,7 +1364,7 @@ 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 | 10 + 100 | 20 100 | 30 (2 rows) @@ -1443,9 +1443,9 @@ 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 - 100 | 30 (3 rows) -- Limit with -ve value. Shouldn't pushdown. @@ -2102,10 +2102,33 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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; @@ -2117,6 +2140,8 @@ 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index d9c3677..311b6bb 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -79,22 +79,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -242,22 +242,22 @@ 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 - 20 | ADMINISTRATION | 400 | EMP4 | 2975 | 20 30 | SALES | 500 | EMP5 | 1250.23 | 30 30 | SALES | 600 | EMP6 | 2850 | 30 - 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 - 20 | ADMINISTRATION | 800 | EMP8 | 3000 | 20 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 30 | SALES | 1000 | EMP10 | 1500 | 30 - 20 | ADMINISTRATION | 1100 | EMP11 | 1100 | 20 30 | SALES | 1200 | EMP12 | 950 | 30 - 20 | ADMINISTRATION | 1300 | EMP13 | 3000 | 20 - 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 - | | 1500 | EMP15 | 950 | 60 - | | 1600 | EMP16 | | (16 rows) EXPLAIN (COSTS OFF) @@ -381,7 +381,6 @@ 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 ----+----------------+------+-------+---------+---- - 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 | | 200 | EMP2 | 1600 | 30 | | 300 | EMP3 | 1250 | 30 | | 400 | EMP4 | 2975 | 20 @@ -397,6 +396,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | 1400 | EMP14 | 1300 | 10 | | 1500 | EMP15 | 950 | 60 | | 1600 | EMP16 | | + 20 | ADMINISTRATION | 100 | EMP1 | 800.3 | 20 (16 rows) -- Push INNER JOIN. @@ -779,9 +779,9 @@ 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 | 700 | EMP7 | 2450.34 | 10 - 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 10 | DEVELOPMENT | 1400 | EMP14 | 1300 | 10 + 10 | DEVELOPMENT | 900 | EMP9 | 5000 | 10 + 10 | DEVELOPMENT | 700 | EMP7 | 2450.34 | 10 (3 rows) EXPLAIN (COSTS OFF) @@ -1364,7 +1364,7 @@ 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 | 10 + 100 | 20 100 | 30 (2 rows) @@ -1443,9 +1443,9 @@ 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 - 100 | 30 (3 rows) -- Limit with -ve value. Shouldn't pushdown. @@ -2102,10 +2102,33 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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; @@ -2117,6 +2140,8 @@ 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; diff --git a/mongo_query.c b/mongo_query.c index dab2eab..4da12e9 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -552,7 +552,7 @@ mongo_query_document(ForeignScanState *scanStateNode) /* * 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 + * 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. @@ -564,7 +564,7 @@ mongo_query_document(ForeignScanState *scanStateNode) is_asc_sort); else if (!columnInfo->isOuter && fmstate->relType != BASE_REL) bsonAppendInt32(&sort, - psprintf("Join_result.%s", + psprintf("Join_Result.%s", columnInfo->colName), is_asc_sort); else diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index 36bdea6..e31a87b 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -572,10 +572,28 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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; @@ -587,6 +605,8 @@ 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; From 23b87dd6d0c50855959b31cda94625f869bc200d Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 12 May 2025 19:16:10 +0530 Subject: [PATCH 232/239] Fix SonarQube issue related to sprintf() usage Ensure the buffer size is sufficient to safely hold a string representation of an integer, including its sign. Replace sprintf() with snprintf() to avoid potential buffer overflows and improve safety. DB-722, Vaibhav Dalvi, reviewed by Suraj Kharage. --- mongo_wrapper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mongo_wrapper.c b/mongo_wrapper.c index b3a2441..7637760 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -607,13 +607,13 @@ jsonToBsonAppendElement(BSON *bb, const char *k, struct json_object *v) case json_type_array: { int i; - char buf[10]; + char buf[12]; BSON t; bsonAppendStartArray(bb, k, &t); for (i = 0; i < json_object_array_length(v); i++) { - sprintf(buf, "%d", i); + snprintf(buf, sizeof(buf), "%d", i); jsonToBsonAppendElement(&t, buf, json_object_array_get_idx(v, i)); } bsonAppendFinishObject(bb, &t); From 4129b2a8f0f6e7e32d48dec81e2239f6792faf67 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 4 Jul 2025 10:53:03 +0530 Subject: [PATCH 233/239] Update mongoc and json-c libraries to support MongoDB 8. Upgrade the libmongoc driver to version 1.30.2 and the json-c library to version 0.18-20240915. Deprecated APIs have been replaced with their modern equivalents as part of this update. The previous versions had limitations that prevented reliable connections to newer MongoDB servers, particularly version 8. With this change, MongoDB 8 is now supported. Compatibility with MongoDB versions 5 and earlier may be affected, but those versions are already end-of-life (EOL). FDW-713, Vaibhav Dalvi, reviewed by Suraj Kharage, tested by Kashif Zeeshan. --- INSTALL.md | 24 ++++++++++++------------ autogen.sh | 6 +++--- data/mongo_test_data.js | 2 +- expected/dml.out | 36 ------------------------------------ mongo_wrapper.c | 34 ++++++++++++++-------------------- mongodb_init.sh | 2 +- sql/dml.sql | 16 ---------------- 7 files changed, 31 insertions(+), 89 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index ffa0a34..4a99911 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -7,7 +7,7 @@ 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.17.3 of MongoDB. +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 @@ -30,13 +30,13 @@ driver accordingly. ## Steps for manual installation ### mongo-c -1. Download and extract source code of mongoc driver for version `1.17.3` +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.17.3/mongo-c-driver-1.17.3.tar.gz - tar xzf mongo-c-driver-1.17.3.tar.gz + 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.17.3 mongo-c-driver + mv mongo-c-driver-1.30.2 mongo-c-driver cd mongo-c-driver ``` @@ -66,10 +66,10 @@ For more details on installation of mongo-c driver, you can refer [here][3]. 1. Download and extract source code ```sh - wget https://github.com/json-c/json-c/archive/json-c-0.15-20200726.tar.gz - tar -xzf json-c-0.15-20200726.tar.gz - rm -rf json-c - mv json-c-json-c-0.15-20200726/ json-c + 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 ``` @@ -98,7 +98,7 @@ 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 +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 @@ -151,5 +151,5 @@ 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]: http://mongoc.org/libmongoc/1.17.3/installing.html#configuring-the-build -[4]: https://github.com/json-c/json-c/tree/json-c-0.15-20200726#build-instructions-- +[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-- diff --git a/autogen.sh b/autogen.sh index 9a8a578..cf6d4af 100755 --- a/autogen.sh +++ b/autogen.sh @@ -14,8 +14,8 @@ #------------------------------------------------------------------------- -MONGOC_VERSION=1.17.3 -JSONC_VERSION=0.15-20200726 +MONGOC_VERSION=1.30.2 +JSONC_VERSION=0.18-20240915 MONGOC_INSTALL="${MONGOC_INSTALL_DIR}" JSONC_INSTALL="${JSONC_INSTALL_DIR}" @@ -63,7 +63,7 @@ function checkout_json_lib function install_json_lib { cd json-c && - $CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$JSONC_INSTALL $JSONC_CFLAGS . && + $CMAKE_COMMAND -DCMAKE_INSTALL_PREFIX=$JSONC_INSTALL $JSONC_CFLAGS -DDISABLE_EXTRA_LIBS=ON . && make install && cd .. } diff --git a/data/mongo_test_data.js b/data/mongo_test_data.js index a09926b..ee05aa6 100644 --- a/data/mongo_test_data.js +++ b/data/mongo_test_data.js @@ -97,7 +97,7 @@ db.test_tbl5.insertMany([ {a: true} ]); db.test_tbl7.insertMany([ - {_id: null, a: NumberInt(10), b: "ROW1"}, + {a: NumberInt(10), b: "ROW1"}, {a: NumberInt(20), b: "ROW2"} ]); db.test_tbl8.insertMany([ diff --git a/expected/dml.out b/expected/dml.out index cff659a..51a11f5 100644 --- a/expected/dml.out +++ b/expected/dml.out @@ -261,7 +261,6 @@ SELECT a, b FROM f_mongo_test6 ORDER BY a; ---+--- (0 rows) ---FDW-481: UPDATE/DELETE shouldn't lead to crash when _id is NULL. -- 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'); @@ -291,41 +290,6 @@ 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" -DROP FOREIGN TABLE f_mongo_test7; --- UPDATE/DELETE when _id is NULL. Shouldn't crash. -CREATE FOREIGN TABLE f_mongo_test7 (_id 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) - -SELECT * FROM f_mongo_test7 WHERE a = 10 ORDER BY 1; - _id | a | b ------+----+------ - | 10 | ROW1 -(1 row) - -UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE _id IS NULL; -SELECT a, b FROM f_mongo_test7 ORDER BY 1; - a | b -----+--------- - 10 | UPDATED - 20 | ROW2 -(2 rows) - -DELETE FROM f_mongo_test7 WHERE a = 20; -SELECT a, b FROM f_mongo_test7 ORDER BY 1; - a | b -----+--------- - 10 | UPDATED -(1 row) - --- Retain original data of test_tbl7 -UPDATE f_mongo_test7 SET b = 'ROW1' WHERE a = 10; -INSERT INTO f_mongo_test7 VALUES(0, 20, 'ROW2'); -- 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'); diff --git a/mongo_wrapper.c b/mongo_wrapper.c index 7637760..bbeea63 100644 --- a/mongo_wrapper.c +++ b/mongo_wrapper.c @@ -646,7 +646,9 @@ mongoAggregateCount(MONGO_CONN *conn, const char *database, BSON *command; BSON *reply; double count = 0; - mongoc_cursor_t *cursor; + bson_error_t error; + bool retval; + bson_iter_t it; command = bsonCreate(); reply = bsonCreate(); @@ -654,24 +656,16 @@ mongoAggregateCount(MONGO_CONN *conn, const char *database, if (b) /* Not empty */ bsonAppendBson(command, "query", (BSON *) b); - cursor = mongoc_client_command(conn, database, MONGOC_QUERY_SLAVE_OK, 0, 1, - 0, command, NULL, NULL); - if (cursor) - { - BSON *doc; - bool ret; + 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))); - ret = mongoc_cursor_next(cursor, (const BSON **) &doc); - if (ret) - { - bson_iter_t it; + if (bson_iter_init_find(&it, reply, "n")) + count = bsonIterDouble(&it); - bson_copy_to(doc, reply); - if (bson_iter_init_find(&it, reply, "n")) - count = bsonIterDouble(&it); - } - mongoc_cursor_destroy(cursor); - } bsonDestroy(reply); bsonDestroy(command); @@ -731,7 +725,7 @@ dumpJsonObject(StringInfo output, BSON_ITERATOR *iter) bson_iter_document(iter, &len, &data); if (bson_init_static(&bson, data, len)) { - char *json = bson_as_json(&bson, NULL); + char *json = bsonAsJson(&bson); if (json != NULL) { @@ -753,7 +747,7 @@ dumpJsonArray(StringInfo output, BSON_ITERATOR *iter) { char *json; - if ((json = bson_array_as_json(&bson, NULL))) + if ((json = bson_array_as_legacy_extended_json(&bson, NULL))) { appendStringInfoString(output, json); bson_free(json); @@ -764,5 +758,5 @@ dumpJsonArray(StringInfo output, BSON_ITERATOR *iter) char * bsonAsJson(const BSON *bsonDocument) { - return bson_as_json(bsonDocument, NULL); + return bson_as_legacy_extended_json(bsonDocument, NULL); } diff --git a/mongodb_init.sh b/mongodb_init.sh index 005707c..a4b7484 100755 --- a/mongodb_init.sh +++ b/mongodb_init.sh @@ -18,4 +18,4 @@ mongoimport --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_ 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 -mongo --host=$MONGO_HOST --port=$MONGO_PORT -u $MONGO_USER_NAME -p $MONGO_PWD --authenticationDatabase "mongo_fdw_regress" < data/mongo_test_data.js > /dev/null +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 diff --git a/sql/dml.sql b/sql/dml.sql index aa61bff..5df57d9 100644 --- a/sql/dml.sql +++ b/sql/dml.sql @@ -165,7 +165,6 @@ 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; ---FDW-481: UPDATE/DELETE shouldn't lead to crash when _id is NULL. -- 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'); @@ -180,21 +179,6 @@ CREATE FOREIGN TABLE f_mongo_test7 (id1 NAME, a int, b text) SERVER mongo_server 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; - --- UPDATE/DELETE when _id is NULL. Shouldn't crash. -CREATE FOREIGN TABLE f_mongo_test7 (_id 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; -SELECT * FROM f_mongo_test7 WHERE a = 10 ORDER BY 1; -UPDATE f_mongo_test7 SET b = 'UPDATED' WHERE _id IS NULL; -SELECT a, b FROM f_mongo_test7 ORDER BY 1; -DELETE FROM f_mongo_test7 WHERE a = 20; -SELECT a, b FROM f_mongo_test7 ORDER BY 1; - --- Retain original data of test_tbl7 -UPDATE f_mongo_test7 SET b = 'ROW1' WHERE a = 10; -INSERT INTO f_mongo_test7 VALUES(0, 20, 'ROW2'); -- 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 From 333307940fcf197fcde1505d92d1787f9fbb387a Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 4 Jul 2025 15:31:57 +0530 Subject: [PATCH 234/239] Add support for PostgreSQL 18, EDB Postgres Advanced Server 18, and EDB Postgres Extended Server 18. Code changes include updating calls to certain server-side functions to match their modified signatures, along with adjustments to related constants. FDW-725, Vaibhav Dalvi, reviewed by Sravan Velagandula, tested by Kashif Zeeshan. --- Makefile | 4 +- README.md | 2 +- mongo_fdw.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 151 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index c8893df..62644fd 100644 --- a/Makefile +++ b/Makefile @@ -43,8 +43,8 @@ ifndef MAJORVERSION MAJORVERSION := $(basename $(VERSION)) endif -ifeq (,$(findstring $(MAJORVERSION), 13 14 15 16 17)) - $(error PostgreSQL 13, 14, 15, 16, or 17 is required to compile this extension) +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 diff --git a/README.md b/README.md index 8e342d5..06e7346 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ 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 and 17. +Postgres Advanced Server 13, 14, 15, 16, 17, and 18. Contents -------- diff --git a/mongo_fdw.c b/mongo_fdw.c index 464643e..24bff3d 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -20,6 +20,10 @@ #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" @@ -59,9 +63,15 @@ PG_MODULE_MAGIC; * 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 @@ -244,11 +254,19 @@ static void mongo_add_foreign_ordered_paths(PlannerInfo *root, 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 @@ -559,7 +577,19 @@ mongoGetForeignPaths(PlannerInfo *root, } /* Create a foreign path node */ -#if PG_VERSION_NUM >= 170000 +#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, @@ -988,7 +1018,11 @@ mongoGetForeignPlan(PlannerInfo *root, 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); @@ -2757,7 +2791,11 @@ mongo_acquire_sample_rows(Relation relation, 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)); @@ -2977,7 +3015,21 @@ mongoGetForeignJoinPaths(PlannerInfo *root, RelOptInfo *joinrel, * Create a new join path and add it to the joinrel which represents a * join between foreign tables. */ -#if PG_VERSION_NUM >= 170000 +#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, @@ -3606,7 +3658,20 @@ mongo_add_foreign_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, #endif /* Create and add foreign path to the grouping relation. */ -#if PG_VERSION_NUM >= 170000 +#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, @@ -3963,7 +4028,20 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, -1.0); if (IS_SIMPLE_REL(rel)) -#if PG_VERSION_NUM >= 170000 +#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, @@ -3988,7 +4066,20 @@ mongo_add_paths_with_pathkeys(PlannerInfo *root, RelOptInfo *rel, NIL)); /* no fdw_private list */ #endif else -#if PG_VERSION_NUM >= 170000 +#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, @@ -4178,7 +4269,19 @@ mongo_add_foreign_ordered_paths(PlannerInfo *root, RelOptInfo *input_rel, fdw_private = list_make2(makeInteger(true), makeInteger(false)); /* Create foreign ordering path */ -#if PG_VERSION_NUM >= 170000 +#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], @@ -4294,7 +4397,20 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, * no-longer-needed outer plan (if any), which makes the * EXPLAIN output look cleaner */ -#if PG_VERSION_NUM >= 170000 +#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, @@ -4430,7 +4546,19 @@ mongo_add_foreign_final_paths(PlannerInfo *root, RelOptInfo *input_rel, * 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 >= 170000 +#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], @@ -4544,13 +4672,25 @@ mongo_is_default_sort_operator(EquivalenceMember *em, PathKey *pathkey) 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. */ From 198645c70d071b465831ae78f719de8a3f51e372 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 4 Jul 2025 15:49:19 +0530 Subject: [PATCH 235/239] Add log_remote_query GUC to control logging of remote MongoDB queries. Introduce a new GUC log_remote_query to enable logging of the remote query that would be executed on the MongoDB server. This is intended for debugging purposes only. When enabled, the remote query is printed to the PostgreSQL server logs. The default value is false. FDW-729, Vaibhav Dalvi, reviewed and some adjustments by Jeevan Chalke. --- README.md | 3 +++ expected/pushdown.out | 14 ++++++++++++++ mongo_fdw.c | 44 +++++++++++++++++++++++++++++++++++++++++++ sql/pushdown.sql | 10 ++++++++++ 4 files changed, 71 insertions(+) diff --git a/README.md b/README.md index 06e7346..5645be0 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,9 @@ network traffic between local PostgreSQL and remote MongoDB servers. * `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 ------------------- diff --git a/expected/pushdown.out b/expected/pushdown.out index 3abbd3c..ab49c4f 100644 --- a/expected/pushdown.out +++ b/expected/pushdown.out @@ -877,6 +877,20 @@ SELECT name, pass FROM f_test_tbl3 WHERE pass = true ORDER BY name; 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 24bff3d..e4db2b3 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -87,6 +87,7 @@ PG_MODULE_MAGIC; 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. @@ -313,6 +314,21 @@ _PG_init(void) 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(); @@ -1343,6 +1359,34 @@ mongoIterateForeignScan(ForeignScanState *node) 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; + uint32 len; + + /* Get readable query pipeline in the extended json format. */ + ext_json = bson_as_relaxed_extended_json(queryDocument, NULL); + + /* + * 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. + */ + len = strlen(ext_json); + ext_json[len - 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, diff --git a/sql/pushdown.sql b/sql/pushdown.sql index 6bf665b..375bb4c 100644 --- a/sql/pushdown.sql +++ b/sql/pushdown.sql @@ -364,6 +364,16 @@ 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; From 98c2292682c88be48b6573ae8d5b001f486c1807 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 7 Jul 2025 09:29:58 +0530 Subject: [PATCH 236/239] Add expected/aggregate_pushdown_1.out file I failed to add in 333307940fcf197fcde1505d92d1787f9fbb387a. Reported by Vaibhav Dalvi. --- expected/aggregate_pushdown_1.out | 2000 +++++++++++++++++++++++++++++ 1 file changed, 2000 insertions(+) create mode 100644 expected/aggregate_pushdown_1.out diff --git a/expected/aggregate_pushdown_1.out b/expected/aggregate_pushdown_1.out new file mode 100644 index 0000000..ab3d911 --- /dev/null +++ b/expected/aggregate_pushdown_1.out @@ -0,0 +1,2000 @@ +\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; From e89c6a368adb35043bb3284992a42abc8815862c Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 8 Jul 2025 13:01:20 +0530 Subject: [PATCH 237/239] Fix SonarQube warnings for unsafe sprintf() and strlen() usage Replace sprintf() with snprintf() to prevent potential buffer overflows and enhance code safety. Also, remove the strlen() call by using the length returned by bson_as_relaxed_extended_json(). Vaibhav Dalvi, reviewed by Jeevan Chalke. --- mongo_fdw.c | 33 ++++++++++++++++++--------------- mongo_query.c | 4 ++-- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/mongo_fdw.c b/mongo_fdw.c index e4db2b3..0848ea2 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -1366,25 +1366,28 @@ mongoIterateForeignScan(ForeignScanState *node) */ if (log_remote_query) { - char *ext_json; - uint32 len; + 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, NULL); + ext_json = bson_as_relaxed_extended_json(queryDocument, &length); - /* - * 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. - */ - len = strlen(ext_json); - ext_json[len - 1] = '\0'; /* Remove last '}' */ - ereport(LOG, - errmsg("remote query: db.%s.aggregate(%s)", collectionName, - ext_json + 14)); /* 14 = length of '{ "pipeline" :' */ + 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); + bson_free(ext_json); + } } mongoCursor = mongoCursorCreate(fmstate->mongoConnection, diff --git a/mongo_query.c b/mongo_query.c index 4da12e9..35af683 100644 --- a/mongo_query.c +++ b/mongo_query.c @@ -1799,14 +1799,14 @@ mongo_append_unique_var(List *varlist, Var *var) char * get_varname_for_outer_col(const char *str) { - static char result[66]; + 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. */ - sprintf(result, "v_%s", str); + snprintf(result, sizeof(result), "v_%s", str); /* * Also, replace occurences of dot (".") in the variable name with From 4a15e810bc56f5797448b81441e8f4e55921cb92 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Mon, 28 Jul 2025 17:15:33 +0530 Subject: [PATCH 238/239] Don't push down joins with full document or whole row references in clauses. Previously, join pushdown was restricted when full document retrieval was involved in the target list, due to the lack of infrastructure for correctly retrieving and processing full documents locally from join results. However, this restriction was not consistently applied to restrict clauses (WHERE and JOIN clauses). This oversight could lead to incorrect results when such conditions were evaluated locally. This commit extends the existing restriction to prevent join pushdown when full document retrieval is involved in either the target list or any restrict clause. For similar reasons, this commit also restricts join pushdown when whole row references are present in restrict clauses. These changes ensure correctness by preventing scenarios where local evaluation cannot properly handle complex data types or complete row structures derived from pushed-down joins. Reported on GitHub through issue #191 by ephemeralctl. FDW-733, Vaibhav Dalvi and Jeevan Chalke. --- expected/join_pushdown.out | 98 +++++++++++++++++++++++++++++++++++- expected/join_pushdown_2.out | 98 +++++++++++++++++++++++++++++++++++- expected/join_pushdown_3.out | 98 +++++++++++++++++++++++++++++++++++- mongo_fdw.c | 28 +++++++++++ sql/join_pushdown.sql | 22 +++++++- 5 files changed, 336 insertions(+), 8 deletions(-) diff --git a/expected/join_pushdown.out b/expected/join_pushdown.out index 6c85a04..27886f3 100644 --- a/expected/join_pushdown.out +++ b/expected/join_pushdown.out @@ -26,6 +26,8 @@ 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'); @@ -1145,7 +1147,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (17 rows) --- Don't pushdown when whole row reference is involved. +-- 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; @@ -1169,7 +1171,25 @@ SELECT d, e Foreign Namespace: mongo_fdw_regress.test_tbl1 (16 rows) --- Don't pushdown when full document retrieval is involved. +-- 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"; @@ -1206,6 +1226,79 @@ SELECT json_data.key AS key1, json_data.value AS value1 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 @@ -2129,6 +2222,7 @@ 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; diff --git a/expected/join_pushdown_2.out b/expected/join_pushdown_2.out index a718b92..3983664 100644 --- a/expected/join_pushdown_2.out +++ b/expected/join_pushdown_2.out @@ -26,6 +26,8 @@ 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'); @@ -1145,7 +1147,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (17 rows) --- Don't pushdown when whole row reference is involved. +-- 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; @@ -1169,7 +1171,25 @@ SELECT d, e Foreign Namespace: mongo_fdw_regress.test_tbl1 (16 rows) --- Don't pushdown when full document retrieval is involved. +-- 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"; @@ -1206,6 +1226,79 @@ SELECT json_data.key AS key1, json_data.value AS value1 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 @@ -2133,6 +2226,7 @@ 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; diff --git a/expected/join_pushdown_3.out b/expected/join_pushdown_3.out index 311b6bb..63fefef 100644 --- a/expected/join_pushdown_3.out +++ b/expected/join_pushdown_3.out @@ -26,6 +26,8 @@ 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'); @@ -1145,7 +1147,7 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 | | | | | (17 rows) --- Don't pushdown when whole row reference is involved. +-- 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; @@ -1169,7 +1171,25 @@ SELECT d, e Foreign Namespace: mongo_fdw_regress.test_tbl1 (16 rows) --- Don't pushdown when full document retrieval is involved. +-- 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"; @@ -1206,6 +1226,79 @@ SELECT json_data.key AS key1, json_data.value AS value1 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 @@ -2133,6 +2226,7 @@ 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; diff --git a/mongo_fdw.c b/mongo_fdw.c index 0848ea2..c121f9b 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -3236,7 +3236,35 @@ mongo_foreign_join_ok(PlannerInfo *root, RelOptInfo *joinrel, 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); + } } } diff --git a/sql/join_pushdown.sql b/sql/join_pushdown.sql index e31a87b..4f8003a 100644 --- a/sql/join_pushdown.sql +++ b/sql/join_pushdown.sql @@ -30,6 +30,8 @@ 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); @@ -295,17 +297,32 @@ SELECT d.c1, d.c2, e.c1, e.c2, e.c6, e.c8 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. +-- 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. +-- 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) @@ -598,6 +615,7 @@ 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; From 27aa7b04af197129bf10069c4b512a326c94f3f8 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Tue, 30 Sep 2025 10:58:33 +0530 Subject: [PATCH 239/239] Stamp 5.5.3. --- expected/select.out | 2 +- mongo_fdw.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expected/select.out b/expected/select.out index 073a77b..0843cf4 100644 --- a/expected/select.out +++ b/expected/select.out @@ -14,7 +14,7 @@ CREATE USER MAPPING FOR public SERVER mongo_server; SELECT mongo_fdw_version(); mongo_fdw_version ------------------- - 50502 + 50503 (1 row) -- Create foreign tables diff --git a/mongo_fdw.c b/mongo_fdw.c index c121f9b..7bf7ef0 100644 --- a/mongo_fdw.c +++ b/mongo_fdw.c @@ -54,9 +54,9 @@ PG_MODULE_MAGIC; /* * In PG 9.5.1 the number will be 90501, - * our version is 5.5.2 so number will be 50502 + * our version is 5.5.3 so number will be 50503 */ -#define CODE_VERSION 50502 +#define CODE_VERSION 50503 /* * Macro to check unsupported sorting methods. Currently, ASC NULLS FIRST and